Cocos Creator 2.4x 笔记 - 从Unity到Cocos [2]

cc.Tween 缓动系统

cc.tween 能够对对象的任意属性进行缓动,功能类似于 cc.Action(动作系统)。但是 cc.tween 会比 cc.Action 更加简洁易用,因为 cc.tween 提供了链式创建的方法,可以对任何对象进行操作,并且可以对对象的任意属性进行缓动。

动作系统 是从 Cocos2d-x 迁移到 Cocos Creator 的,提供的 API 比较繁琐,只支持在节点属性上使用,并且如果要支持新的属性就需要再添加一个新的动作。为了提供更好的 API,cc.tween动作系统 的基础上做了一层 API 封装。

动作系统目前已不推荐使用,未来将逐步移除,建议使用 缓动系统 做为替代。

使用对比

cc.Action:

this.node.runAction(
    cc.sequence(
        cc.spawn(
            cc.moveTo(1, 100, 100),
            cc.rotateTo(1, 360),
        ),
        cc.scale(1, 2)
    )
)

cc.tween:

cc.tween(this.node)
    .to(1, { position: cc.v2(100, 100), rotation: 360 })
    .to(1, { scale: 2 })
    .start()

链式 API
cc.tween 的每一个 API 都会在内部生成一个 action,并将这个 action 添加到内部队列中,在 API 调用完后会再返回自身实例,这样就可以通过链式调用的方式来组织代码。
cc.tween 在调用 start 时会将之前生成的 action 队列重新组合生成一个 cc.sequence 队列,所以 cc.tween 的链式结构是依次执行每一个 API 的,也就是会执行完一个 API 再执行下一个 API。

cc.tween(this.node)
    // 0s 时,node 的 scale 还是 1
    .to(1, { scale: 2 })
    // 1s 时,执行完第一个 action,scale 为 2
    .to(1, { scale: 3 })
    // 2s 时,执行完第二个 action,scale 为 3
    .start()
    // 调用 start 开始执行 cc.tween

cc.tween 提供了两个设置属性的 API:
to:对属性进行绝对值计算,最终的运行结果即是设置的属性值,即改变到某个值。
by:对属性进行相对值计算,最终的运行结果是设置的属性值加上开始运行时节点的属性值,即变化值。

cc.tween(node)
  .to(1, {scale: 2})      // node.scale === 2
  .by(1, {scale: 2})      // node.scale === 4 (2 + 2)
  .by(1, {scale: 1})      // node.scale === 5
  .to(1, {scale: 2})      // node.scale === 2
  .start()

tween 可以支持任意对象的任意属性

let obj = { a: 0 }
cc.tween(obj).to(1, { a: 100 }).start()

修改缓动函数 Ease EaseType(动画曲线)
查看cocos的Easing类型

ease type.png

// 传入 easing 名字,直接使用内置 easing 函数
cc.tween().to(1, { scale: 2 }, { easing: 'sineOutIn'})

// 使用自定义 easing 函数
cc.tween().to(1, { scale: 2 }, { easing: t => t*t; })

// 只对单个属性使用 easing 函数
// value 必须与 easing 或者 progress 配合使用
cc.tween().to(1, { scale: 2, position: { value: cc.v3(100, 100, 100), easing: 'sineOutIn' } })

可以用自定义 progress 函数来控制缓动
可以复制当前缓动并且接受一个targettween.clone(cc.find('Canvas/cocos')).start()
可以不用.start(),而是创建一些固定缓动然后再组合

let scale = cc.tween().to(1, { scale: 2 })
let rotate = cc.tween().to(1, { rotation: 90})
let move = cc.tween().to(1, { position: cc.v3(100, 100, 100)})

// 先缩放再旋转
cc.tween(this.node).then(scale).then(rotate)

缓动可以借助 parallel实现并行,而不是按照原本的sequence的方式

let t = cc.tween;
t(this.node)
    // 同时执行两个 cc.tween
    .parallel(
        t().to(1, { scale: 2 }),
        t().to(2, { position: cc.v2(100, 100) })
    )
    .call(() => {
        console.log('All tweens finished.')
    })
    .start()

可以在动作执行中进行函数回调

cc.tween(this.node)
    .to(2, { rotation: 90})
    .to(1, { scale: 2})
    // 当前面的动作都执行完毕后才会调用这个回调函数
    .call(() => { cc.log('This is a callback') })
    .start()

重复执行 repeat repeatForever

cc.tween(this.node)
    .by(1, { scale: 1 })
    // 对前一个 by 重复执行 10次
    .repeat(10)
    // 最后 node.scale === 11
    .start()

// 也可以这样用
cc.tween(this.node)
    .repeat(10,
        cc.tween().by(1, { scale: 1 })
    )
    .start()

// 一直重复执行下去
cc.tween(this.node)
    .by(1, { scale: 1 })
    .repeatForever()
    .start()

延迟执行

cc.tween(this.node)
    // 延迟 1s
    .delay(1)
    .to(1, { scale: 2 })
    // 再延迟 1s
    .delay(1)
    .to(1, { scale: 3 })
    .start()

计时器

在 Cocos Creator 中,我们为组件提供了方便的计时器,这个计时器源自于 Cocos2d-x 中的 cc.Scheduler,我们将它保留在了 Cocos Creator 中并适配了基于组件的使用方式。

也许有人会认为 setTimeoutsetInterval 就足够了,开发者当然可以使用这两个函数,不过我们更推荐使用计时器,因为它更加强大灵活,和组件也结合得更好!

下面这个计时器将每隔 5s 执行一次。

 component.schedule(function() {
     this.doSomething();
 }, 5);

下面的计时器将在 10 秒后开始计时,每 5 秒执行一次回调,执行 3 + 1 次。

// 以秒为单位的时间间隔
 var interval = 5;
 // 重复次数
 var repeat = 3;
 // 开始延时
 var delay = 10;
 component.schedule(function() {
     // 这里的 this 指向 component
     this.doSomething();
 }, interval, repeat, delay);

只执行一次的计时器

 component.scheduleOnce(function() {
     this.doSomething();
 }, 2);

用回调函数取消计时器

 this.count = 0;
 this.callback = function () {
     if (this.count === 5) {
         // 在第六次执行回调时取消这个计时器
         this.unschedule(this.callback);
     }
     this.doSomething();
     this.count++;
 }
 component.schedule(this.callback, 1);

schedule:开始一个计时器
scheduleOnce:开始一个只执行一次的计时器
unschedule:取消一个计时器
unscheduleAllCallbacks:取消这个组件的所有计时器

控制脚本的执行顺序

使用统一的控制脚本来初始化其他脚本
其中在 Player.js、Enemy.js 和 Menu.js 中需要实现 init 方法,并将初始化逻辑放进去。这样我们就可以保证 Player、Enemy 和 Menu 的初始化顺序。

// Game.js

const Player = require('Player');
const Enemy = require('Enemy');
const Menu = require('Menu');

cc.Class({
    extends: cc.Component,
    properties: {
        player: Player,
        enemy: Enemy,
        menu: Menu
    },

    onLoad: function () {
        this.player.init();
        this.enemy.init();
        this.menu.init();
    }
});

在 Update 中用自定义方法控制更新顺序
同理如果要保证以上三个脚本的每帧更新顺序,我们也可以将分散在每个脚本里的 update 替换成自己定义的方法:updatePlayer: function (dt) { // do player update} ,然后在Game.js中脚本的update里调用这些

// Game.js
    update: function (dt) {
        this.player.updatePlayer(dt);
        this.enemy.updateEnemy(dt);
        this.menu.updateMenu(dt);
    }

控制同一个节点上的组件执行顺序
在同一个节点上的组件脚本执行顺序:

  1. 可以通过组件在 属性检查器 里的排列顺序来控制。排列在上的组件会先于排列在下的组件执行。我们可以通过组件右上角的齿轮按钮里的 Move Up 和 Move Down 菜单来调整组件的排列顺序和执行顺序。
  2. 可以设置组件执行优先级 ,设置组件的 executionOrderexecutionOrder 会影响组件的生命周期回调的执行优先级。 executionOrder 越小,相对于其他组件就会越优先执行。
cc.Class({
    extends: cc.Component,
    editor: {
        executionOrder: -1
    },

    onLoad: function () {
        cc.log('Player onLoad!');
    }
});

标准网络接口

在 Cocos Creator 中,我们支持 Web 平台上最广泛使用的标准网络接口:
XMLHttpRequest:用于短连接
WebSocket:用于长连接

这部分暂时不了解 ,参考文档 标准网络接口

对象池

cocos自带有cc.NodePool的对象池,使用可参考文档 使用对象池

模块化脚本

Cocos Creator 允许你将代码拆分成多个脚本文件,并且让它们相互调用。要实现这点,你需要了解如何在 Cocos Creator 中定义和使用模块,这个步骤简称为 模块化。感觉这个东西类似于引用变量,然后提供了一个public的引用进行代码之间的耦合。

如果你还不确定模块化究竟能做什么,模块化相当于:

  • Java 和 Python 中的 import
  • C# 中的using
  • C/C++ 中的 include
  • HTML 中的 <link>

模块化使你可以在 Cocos Creator 中引用其它脚本文件:

  • 访问其它文件导出的参数
  • 调用其它文件导出的方法
  • 使用其它文件导出的类型
  • 使用或继承其它 Component

Cocos Creator 中的 JavaScript 使用和 Node.js 几乎相同的 CommonJS 标准来实现模块化,简单来说:

  • 每一个单独的脚本文件就构成一个模块
  • 每个模块都是一个单独的作用域
  • 以 同步 的 require 方法来引用其它模块
  • 设置 module.exports 为导出的变量

在本文中,“模块”和“脚本”这两个术语是等价的。所有“备注”都属于进阶内容,一开始不需要了解。
不论模块如何定义,所有用户代码最终会由 Creator 编译为原生的 JavaScript,可直接在浏览器中运行。

引用模块

var Rotate = require("Rotate"); require 返回的就是被模块导出的对象,通常我们都会将结果立即存到一个变量(var Rotate)。传入 require 的字符串就是模块的文件名,这个名字不包含路径也不包含后缀,而且大小写敏感。

派生范例

var Rotate = require("Rotate");

var SinRotate = cc.Class({
    extends: Rotate,
    update: function (dt) {
        this.rotation += this.speed * Math.sin(dt);
    }
});

定义模块

如果你的模块是extends:cc.Component 的话,在脚本中声明了一个组件,Creator 会默认把它导出,其它脚本直接 require 这个模块就能使用这个组件。
但是模块导出的也可以是任意的JavaScript对象,只要通过 module.exports = xxx; 就可以导出
module.exports 默认是一个空对象({}),可以直接往里面增加新的字段。

  module.exports = {
      FOO: function () {
          this.type = "foo";
      },
      bar: "bar"
  };

在脚本内定义的变量可以通过封装来进行访问:

// foobar.js:
var dirty = false;
module.exports = {
    setDirty: function () {
        dirty = true;
    },
    isDirty: function () {
        return dirty;
    },
};

脚本进阶参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容