背景:一个人物模型Actor与一张地图map,在地图上人物随意移动,比如能从A点到B点,或A点到B点过程中用户点击C点,那么改变移动方向到C点,在移动结束后执行其它人物动作Action
路径移动
首先我们能联想到的是cc.moveBy或cc.moveTo实现一格的移动,通过给出的路径path[ point1 , point2, ..., B]到达B点。
1. 生成路径path: generatePath(A, B, map?)(函数实现略)
2. 添加路径并移动: Actor.move(path)
move(path){
if(path == null || path == undefined || path.length == 0 )
return;
else{
this.path = path;
this._move(path);
}
}
3. 一格移动: Actor._move(point)
_move(path){
// path = [ point1 , point2, ..., B]
if(this.moveActions!=null && !this.moveActions.isDone()) {
// 上个动作没有完成,记录下path等之后完成
this.path = path;
return;
}else{
this.moveActions = new cc.ActionInterval();
let nextSite = this.path.splice(0, 1)[0];
if(nextSite){
// 换算成网格移动所需要的x, y
let x = (nextSite.x - this.actor.x) * this.node.width,
y = - (nextSite.y - this.actor.y) * this.node.height;
// cc.moveBy移动一格,通过cc.sequence与cc.callFunc组合,实现移动一格结束后的回调
this.moveActions = cc.sequence(cc.moveBy(MOVE_SPEED, x, y), this.moveActions);
this.moveActions = cc.sequence(this.moveActions, cc.callFunc(this._moveFinished, this, nextSite));
this.node.runAction(this.moveActions);
}
}
}
_moveFinished(targetNode, nextSite){
//移动一格,更新自己的位置
this.actor.x = nextSite.x;
this.actor.y = nextSite.y;
//目标路径没有移动完,继续移动
if(this.path && this.path.length){
this._move(this.path);
}else{
cc.log('Promise: move is done');
}
}
路径1:用户点击B点,生成path(A->B),调用Actor.move(path),_move移动到B点结束
路径2:用户点击B点,生成path(A->B),调用Actor.move(path(A->B)),_move向着B点移动;中途用户再次点击C点,生成当前位置到C点的路径path(_point->C),调用Actor.move(path(_point->C)),_move改变方向向着C点移动。
添加Promise
关于在人物移动结束后还要执行的Action动作,则需要为整个移动添加一个promise了:
move(path){
let promise = new Promise((resolve, reject)=>{
if(path == null || path == undefined || path.length == 0 )
reject('path is null');
else{
this.path = path;
this._move(path, resolve);
}
});
return promise;
}
_move(path, resolve){
if(this.moveActions!=null && !this.moveActions.isDone()) {
// 上个动作没有完成,记录下path等之后完成
this.path = path;
return;
}else{
this.moveActions = new cc.ActionInterval();
let nextSite = this.path.splice(0, 1)[0];
if(nextSite){
let x = (nextSite.x - this.actor.x) * this.node.width,
y = - (nextSite.y - this.actor.y) * this.node.height;
// cc.moveBy移动一格,通过cc.sequence与cc.callFunc组合,实现移动一格结束后的回调
this.moveActions = cc.sequence(cc.moveBy(MOVE_SPEED, x, y), this.moveActions);
// cc.callFunc回调里加入resolve作为参数
this.moveActions = cc.sequence(this.moveActions, cc.callFunc(this._moveFinished, this, {nextSite, resolve}));
this.node.runAction(this.moveActions);
}
}
}
_moveFinished(targetNode, {nextSite, resolve}){
this.actor.x = nextSite.x;
this.actor.y = nextSite.y;
if(this.path && this.path.length){
this._move(this.path, resolve);
}else{
cc.log('Promise: move is done');
resolve('');
}
}
以上加入了promise的丑陋的代码看似完成了完成动作后的回调,你只需要
Actor.move(path).then(()=>{
//这里写下一个动作
})
就能在move结束后执行其它相关操作了。
但但但但但但 是:
对于路径二,在用户第二次点击改变path的同时,_move方法会执行第一个if判断:
_move(path, resolve){
if(this.moveActions!=null && !this.moveActions.isDone()) {
// 上个动作没有完成,记录下path等之后完成
this.path = path;
return;
}
也就是说,路径二的promise几乎永远不会被resolve,而
cc.callFunc(this._moveFinished, this, {nextSite, resolve})
中的resolve还是从A->B时的promise的resolve,因为它被闭包了。
调整代码,加入this.movePromise记录当前promise,使resolve从闭包中脱离:
move(path){
let promise = new Promise((resolve, reject)=>{
if(this.movePromise){
// 上个未完成动作会被直接reject
this.movePromise.reject('');
cc.log('Move Promise reject');
}
this.movePromise = {resolve, reject};
if(path == null || path == undefined || path.length == 0 )
reject('path is null');
else{
this.path = path;
this._move(path);
}
});
return promise;
}
_move(path){
if(this.moveActions!=null && !this.moveActions.isDone()) {
this.path = path;
return;
}else{
this.moveActions = new cc.ActionInterval();
let nextSite = this.path.splice(0, 1)[0];
if(nextSite){
let x = (nextSite.x - this.actor.x) * this.node.width,
y = - (nextSite.y - this.actor.y) * this.node.height;
this.moveActions = cc.sequence(cc.moveBy(MOVE_SPEED, x, y), this.moveActions);
this.moveActions = cc.sequence(this.moveActions, cc.callFunc(this._moveFinished, this, {nextSite}));
this.node.runAction(this.moveActions);
}
}
}
_moveFinished(targetNode, {nextSite}){
this.actor.x = nextSite.x;
this.actor.y = nextSite.y;
if(this.path && this.path.length){
this._move(this.path);
}else{
cc.log('Promise: move is done');
this.movePromise.resolve('');
this.movePromise = null;
}
}
这下就能开开心心的使用move了
Actor.move(path).then(()=>{
//这里写下一个动作
})