[Cocos Creator] 踩坑日记(三)-路径移动问题及为其添加Promise

背景:一个人物模型Actor与一张地图map,在地图上人物随意移动,比如能从A点到B点,或A点到B点过程中用户点击C点,那么改变移动方向到C点,在移动结束后执行其它人物动作Action

[Cocos Creator] 踩坑日记(三)-路径移动问题及为其添加Promise_第1张图片

路径移动

首先我们能联想到的是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(()=>{

//这里写下一个动作

})



你可能感兴趣的:([Cocos Creator] 踩坑日记(三)-路径移动问题及为其添加Promise)