前言:
要使得一个角色动起来,CocosCreator提供了动作系统,里面有很多的API函数,可以通过调用不同的API函数来控制角色的运动,动作系统可以在一定时间内对节点完成位移,缩放,旋转等各种动作。需要注意的是,动作系统并不能取代 动画系统,动作系统提供的是面向程序员的 API 接口,而动画系统则是提供在编辑器中来设计的。
一、动作系统应用
1. 动作系统API
这里提醒一下:这些动作系统的API都是相对节点而言的,是用来操作节点的,千万别给组件加动作.
一定用在节点上
// 创建一个动作
var action = cc.moveTo(1, 100, 100);// 参数:维持时间、X坐标、y坐标
// 节点执action行动作
node.runAction(action);
// 停止一个动作
node.stopAction(action);
// 停止所有动作
node.stopAllActions();
开发者还可以给动作设置 tag,并通过 tag 来控制动作。
这就相当于给一个动作贴上一个标签,编一个号之类的。
// 给 action 设置 tag
var action_tag = 1;
action.setTag(action_tag);
// 通过 tag 获取 action
node.getActionByTag(action_tag);
// 通过 tag 停止一个动作
node.stopActionByTag(action_tag);
2. 动作类型
由于CocosCreator里面的动作有很多,这里介绍一些很常用的。
基础动作
基础动作就是实现各种形变,位移动画的动作,比如cc.moveTo
用来移动节点到某个位置;cc.rotateBy
用来旋转节点一定的角度;cc.scaleTo
用来缩放节点。
基础动作中分为时间间隔动作和即时动作:
时间间隔动作是在一定时间间隔内完成的渐变动作,前面提到的都是时间间隔动作,它们全部继承自 cc.ActionInterval
。
即时动作则是立即发生的,比如用来调用回调函数的cc.callFunc
;用来隐藏节点的cc.hide
,它们全部继承自cc.ActionInstant
。
容器动作
容器动作就是对已有的一些动作惊进行管理和修饰,使其具有一些特定的效果。
1. 顺序动作cc.sequence
:
顺序动作可以让一系列子动作按顺序一个个执行。
// 让节点在两个点之间来回移动
var seq = cc.sequence(cc.moveBy(1, 200, 0), cc.moveBy(1, -200, 0));
node.runAction(seq);
2. 同步动作cc.spawn
:
同步动作可以同步执行对一系列子动作,子动作的执行结果会叠加起来
修改节点的属性。
// 让节点在向上移动的同时缩放
var spawn = cc.spawn(cc.moveBy(1, 0, 50), cc.scaleTo(0.5, 0.8, 1.4));
node.runAction(spawn);
3. 重复动作 cc.repeat
:
重复动作用来多次重复一个动作。
// 让节点左右来回移动,并重复5次
var seq = cc.repeat(
cc.sequence(
cc.moveBy(2, 200, 0),
cc.moveBy(2, -200, 0)
), 5);
node.runAction(seq);
4. 永远重复动作 cc.repeatForever
:
顾名思义,这个动作容器可以让目标动作一直重复,直到手动停止。
// 让节点左右来回移动并一直重复
var seq = cc.repeatForever(
cc.sequence(
cc.moveBy(2, 200, 0),
cc.moveBy(2, -200, 0)
));
5. 速度动作cc.speed
:
速度动作可以改变目标动作的执行速率,让动作更快或者更慢完成。
// 让目标动作速度加快一倍,相当于原本2秒的动作在1秒内完成
var action = cc.speed(
cc.spawn(
cc.moveBy(2, 0, 50),
cc.scaleTo(2, 0.8, 1.4)
), 2);
node.runAction(action);
缓动动作
缓动动作不可以单独存在,它永远是为了修饰基础动作而存在的,它可以用来修改基础动作的时间曲线,让动作有快入、缓入、快出或其它更复杂的特效
。需要注意的是,只有时间间隔动作才支持缓动:
var action = cc.scaleTo(0.5, 2, 2);
action.easing(cc.easeIn(3.0));
基础的缓动动作类是 cc.ActionEase
。
回调动作
第一个参数是一个执行时函数,第二个参数指定了处理回调方法的 context(也就是绑定 this),第三个参数是向处理回调方法的传参。
// 动作回调函数的声明:两种
var finished = cc.callFunc(this.myMethod, this, opt);// 方法一
var finished = cc.callFunc(function(target, score) {
// 方法二
this.score += score;
}, this, 100);//动作完成后会给玩家加100分
// 在声明了回调动作 finished 后,您可以配合 cc.sequence 来执行一整串动作并触发回调:
var myAction = cc.sequence(cc.moveBy(1, cc.v2(0, 100)), cc.fadeOut(1), finished);
// 在同一个 sequence 里也可以多次插入回调:
var myAction = cc.sequence(cc.moveTo(1, cc.v2(0, 0)), finished1, cc.fadeOut(1), finished2);
// 注意:finished1, finished2 都是使用 cc.callFunc 定义的回调动作
注意: 在 cc.callFunc 中不应该停止自身动作,由于动作是不能被立即删除,
如果在动作回调中暂停自身动作会引发一系列遍历问题,导致更严重的 bug。
二、动作汇总
容器动作
动作名称
描述
动作名称
描述
cc.sequence
顺序执行动作
cc.spawn
同步执行动作
cc.repeat
重复执行动作
cc.repeatForever
永远重复动作
cc.speed
修改动作速率 API 描述
即时动作
动作名称
描述
动作名称
描述
cc.show
立即显示
cc.hide
立即隐藏
cc.toggleVisibility
显隐状态切换
cc.removeSelf
从父节点移除自身
cc.flipX
X轴翻转
cc.flipY
Y轴翻转
cc.place
放置在目标位置
cc.callFunc
执行回调函数
cc.targetedAction
用已有动作和一个新的目标节点创建动作
时间间隔动作
动作名称
描述
动作名称
描述
cc.moveTo
移动到目标位置
cc.moveBy
移动指定的距离
cc.rotateTo
旋转到目标角度
cc.rotateBy
旋转指定的角度
cc.scaleTo
将节点大小缩放到指定的倍数
cc.scaleBy
按指定的倍数缩放节点大小
cc.skewTo
偏斜到目标角度
cc.skewBy
偏斜指定的角度
cc.jumpBy
用跳跃的方式移动指定的距离
cc.jumpTo
用跳跃的方式移动到目标位置
cc.follow
追踪目标节点的位置
cc.bezierTo
按贝赛尔曲线轨迹移动到目标位置
cc.bezierBy
按贝赛尔曲线轨迹移动指定的距离
cc.blink
闪烁(基于透明度)
cc.fadeTo
修改透明度到指定值
cc.cardinalSplineTo
按基数样条曲线轨迹移动到目标位置
cc.fadeOut
渐隐
cc.catmullRomTo
按 Catmull Rom 样条曲线轨迹移动到目标位置
cc.tintBy
按照指定的增量修改颜色
cc.delayTime
延迟指定的时间量
cc.reverseTime
反转目标动作的时间轴
cc.fadeIn
渐显
cc.cardinalSplineBy
按基数样条曲线轨迹移动指定的距离
cc.tintTo
修改颜色到指定值
cc.catmullRomBy
按 Catmull Rom 样条曲线轨迹移动指定的距离
缓动动作
cc.easeIn
cc.easeOut
cc.easeInOut
cc.easeExponentialIn
cc.easeExponentialOut
cc.easeExponentialInOut
cc.easeSineIn
cc.easeSineOut
cc.easeSineInOut
cc.easeElasticIn
cc.easeElasticOut
cc.easeElasticInOut
cc.easeBounceIn
cc.easeBounceOut
cc.easeBounceInOut
cc.easeBackIn
cc.easeBackOut
cc.easeBackInOut
cc.easeBezierAction
cc.easeQuadraticActionIn
cc.easeQuadraticActionOut
cc.easeQuadraticActionInOut
cc.easeQuarticActionIn
cc.easeQuarticActionOut
三、缓动系统(cc.tween)
cc.tween
会比 cc.Action
更加简洁易用,因为 cc.tween 提供了链式创建的方法,可以对任何对象进行操作,并且可以对对象的任意属性进行缓动。
动作系统只支持在节点属性上使用,并且如果要支持新的属性就需要再添加一个新的动作。
为了提供更好的 API, cc.tween 在 动作系统 的基础上做了一层 API 封装
。
下面是 cc.Action 与 cc.tween 在使用上的对比:
// 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()
1. 链式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
2. 设置缓动属性
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()
3. 支持缓动任意对象的任意属性
let obj = {
a: 0 }
cc.tween(obj)
.to(1, {
a: 100 })
.start()
4. 同时执行多个属性
cc.tween(this.node)
// 同时对 scale, position, rotation 三个属性缓动
.to(1, {
scale: 2, position: cc.v2(100, 100), rotation: 90 })
.start()
5. easing
你可以使用 easing 来使缓动更生动, cc.tween 针对不同的情况提供了多种使用方式。
// 传入 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' } })
6. 自定义 progress
相对于 easing,自定义 progress 函数可以更自由的控制缓动的过程。
// 对所有属性自定义 progress
cc.tween().to(1, {
scale: 2, rotation: 90 }, {
progress: (start, end, current, ratio) => {
return start + (end - start) * ratio;
}
})
// 对单个属性自定义 progress
cc.tween().to(1, {
scale: 2,
position: {
value: cc.v3(),
progress: (start, end, current, t) => {
// 注意,传入的属性为 cc.Vec3,所以需要使用 Vec3.lerp 进行插值计算
return start.lerp(end, t, current);
}
}
})
7. 复制缓动
clone 函数会克隆一个当前的缓动,并接受一个 target 作为参数。
// 先创建一个缓动作为模板
let tween = cc.tween().to(4, {
scale: 2 })
// 复制 tween,并使用节点 Canvas/cocos 作为 target
tween.clone(cc.find('Canvas/cocos')).start()
// 复制 tween,并使用节点 Canvas/cocos2 作为 target
tween.clone(cc.find('Canvas/cocos2')).start()
8. 插入其他的缓动到队列中
你可以事先创建一些固定的缓动,然后通过组合这些缓动形成新的缓动来减少代码的编写。
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)
// 先缩放再移动
cc.tween(this.node).then(scale).then(move)
9. 并行执行缓动
cc.tween 在链式执行时是按照 sequence 的方式来执行的,但是在编写复杂缓动的时候可能会需要同时并行执行多个队列, cc.tween 提供了 parallel 接口来满足这个需求。
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()
10. 回调
cc.tween(this.node)
.to(2, {
rotation: 90})
.to(1, {
scale: 2})
// 当前面的动作都执行完毕后才会调用这个回调函数
.call(() => {
cc.log('This is a callback') })
.start()
11. 重复执行
repeat/repeatForever 函数会将前一个 action 作为作用对象。但是如果有参数提供了其他的 action 或者 tween,则 repeat/repeatForever 函数会将传入的 action 或者 tween 作为作用对象。
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()
12. 延迟执行
cc.tween(this.node)
// 延迟 1s
.delay(1)
.to(1, {
scale: 2 })
// 再延迟 1s
.delay(1)
.to(1, {
scale: 3 })
.start()
这篇动作篇有点长,不过这些都挺重要的,还是要记住,这样在游戏开发时能够能更快,代码也可以更精炼,效果更好!
推荐阅读:
一个小时完成CocosCreator射击小游戏 (适合初学者)
本文同步分享在 博客“战 胜”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。