学习自定义Action的最好方法是去查看Cocos Creator中常用动作的写法。比如cc.MoveTo
继承了cc.MoveBy
,而cc.MoveBy
则进一步继承了cc.ActionInterval
。
可以从cocos-creator的引擎源码找到cc.MoveBy的相关定义:
cc.MoveBy = cc.Class({
name: 'cc.MoveBy',
extends: cc.ActionInterval,
ctor:function (duration, deltaPos, deltaY) {
this._positionDelta = cc.v2(0, 0);
this._startPosition = cc.v2(0, 0);
this._previousPosition = cc.v2(0, 0);
deltaPos !== undefined && cc.MoveBy.prototype.initWithDuration.call(this, duration, deltaPos, deltaY);
}
...
})
如果需要自定义Action只需要按照自己的需求仿照cc.MoveBy的定义写一个就行了。如实现cc.ActionInterval
类的initWithDuration
、startWithTarget
、update
方法。「sprite 圆形运动,圆周运动」给出了自定义一个圆周运动的三种方案,其中第一种方案就是集成cc.ActionInterval,自定义Action的方法。这里不再赘述。
因为自己做cocos creator时用的ts语言,在写法上与之前提到的略有不同。目的是实现精灵绕某一点旋转一定的角度。
这里给出实现的源码,并做相关解释。
const {ccclass, property} = cc._decorator;
export default class ArcMove extends cc.ActionInterval {
name: string = "CircleMove";
_positionDelta: cc.Vec2;
_previousPosition: cc.Vec2;
/**圆心 */
_centre:cc.Vec2;
/**半径 */
_R: number = 0;
/**设置节点旋转度数 */
_angle;
/**节点 */
_target: cc.Node;
/**节点在圆上的初始位置(角度)*/
_initAngle:number = 0;
/**圆周运动方向 counter-clockwise(逆时针方向)默认false*/
_ccw: boolean = false;
/**节点初始自身rotation */
_rotation: number = 0;
/**
*
* @param duration
* @param R 半径
* @param angle 绕圆心旋转角度(360度为完整圆)
*/
constructor(duration, R, angle=360){
super();
this._positionDelta = cc.v2(0,0);
this._centre = cc.v2(0,0);
this._previousPosition = cc.v2(0,0);
this._R = R;
this.initWithDuration(duration, R, angle);
}
initWithDuration(duration, R, angle):boolean {
if(super["initWithDuration"](duration, this)){
this._R = R;
this._angle = angle;
return true
}
return false;
}
startWithTarget(target:cc.Node) {
super["startWithTarget"](this, target);
this._target = target;
// 根据节点scaleX判断圆周运动方向(顺/逆时针)
if(target.scaleX == 1){
this._ccw = false;
}else{
this._ccw = true;
}
// 根据节点自身倾角和半径计算圆心
let angle = -(target.rotation + 90);
let v1:cc.Vec2 = new cc.Vec2(Math.cos(angle/180*Math.PI), Math.sin(angle/180*Math.PI));
let center: cc.Vec2 = v1.mul(this._R).add(target.position);
this._centre .x = center.x;
this._centre .y = center.y;
// 计算节点在圆上初始位置
this._initAngle = 90 - target.rotation;
this._rotation = target.rotation;
cc.log("在圆上角度:" + this._initAngle);
}
update (dt) {
if (this._target) {
var locStartPosition = this._centre ;
if (cc.macro.ENABLE_STACKABLE_ACTIONS) {
let dir: number = -1;
if(this._ccw){
dir = 1;
}
var R = this._R;
var rat = this._initAngle * Math.PI / 180 + dir * dt * Math.PI * 2 * (this._angle / 360);
var x = locStartPosition.x + R * Math.cos(rat);
var y = locStartPosition.y + R * Math.sin(rat);
this._target.setPosition(x, y);
this._target.rotation = this._rotation + dt * this._angle;
let a = 1;
} else {
this._target.setPosition(locStartPosition.x + x, locStartPosition.y + y);
}
}
}
}
export var arcMove = function(duration, R, angle){
return new ArcMove(duration, R, angle);
}
这里的_positionDelta和_previousPosition暂时没有用到。由于需要根据精灵的自身旋转角度和给定的半径得出圆心。随后从当前位置开始圆周运动并调整精灵的自身旋转角度。_angle指定精灵的圆周运动度数,一圈是360,具体运动度数可以任意设置。_target则是精灵节点,在initWithTarget
中完成初始化。_initAngle记录的是初始时,从水平方向到精灵的旋转角度,即记录精灵在圆上的初始位置。_rotation记录精灵初始的自身旋转角度。
js中重写initWithDuration
和initWithTarget
方法是通过cc.ActionInterval.prototype.initWithXXX.call()
的方式进行的。但ts似乎没有把接口暴露出来,因此这里采用了super["initWithXXX"]()
的形式。
余下内容计算出圆心、初始圆上位置等等。在update函数中,需要注意dt,比如给定精灵的运动时间duration为3秒,dt的变化还是从0到1.
在使用的时候,import 该类。
let action = arMove(3, 100, 180);
随后节点runAction即可。