麒麟子Cocos Creator实用技巧八:回合战棋类RPG战斗效果

HELLO,大家好,我是麒麟子。作为Cocos社区高产用户,今天又给大家带来了一个看起来很酷,但实际上大多数人用不到的DEMO。

不知道大家是否记得梦幻西游、问道、英雄无敌、仙剑奇侠传、神仙道、神曲OL。

不知道大家是否最近在玩自走棋(哎哟,不错哟,最近特别火)

然而,他们从技术上讲,没有本质的区别,他们的战斗都是回合制。 

回合制的游戏就像棋牌一样,每一个回合,同一时间,只有一个人能操作。 等这个人表演完。再交给下一个。


DEMO内容:

这个DEMO展示了一个战斗场景,玩家可以控制一个英雄去攻击目标。 英雄有6个技能,当玩家点击某个技能的时候,英雄会冲到目标跟前,翻云覆云一番,再退回自己的位置。

有在线演示可以看哦:https://qilinzi.ukylin.net/?lesson=08

当然,还有,源码:https://gitee.com/qilinzi/qlz_ccc_tips


一、动作与特效

扫描二维码关注公众号,回复: 8543605 查看本文章

在这个例子中,英雄的动作和特效是在同一张图上的。显然,这样的方式不适合像传奇这样的MMORPG。 但是对于不换装的游戏,是完全没有问题的。还能省下不少DrawCall。

值得注意的是,我们在这个例子中,并没有使用Cocos Creator中的Animation来编辑英雄动画。 因为一个游戏有上百上千种动画,如果一个个手工编辑的话,是要死人的。 SO。。。 我们自己手写了一个。 请看大屏幕。

//动画信息配置
var AnimConfig = { }
AnimConfig['0001'] = {
    'attack':{frames:8,fps:8},
    'attacked':{frames:1,fps:8},
    'combat_idle':{frames:4,fps:8},
    'idle':{frames:4,fps:8},
    'ride_idle':{frames:4,fps:8},
    'ride_run':{frames:8,fps:8},
    'run':{frames:8,fps:8},
    'rush':{frames:1,fps:8},
    'spell1':{frames:8,fps:8},
    'spell2':{frames:8,fps:8},
    'spell3':{frames:14,fps:8},
    'spell4':{frames:8,fps:8},
    'spell5':{frames:4,fps:8},
    'spell6':{frames:10,fps:8},
}

AnimConfig['0003'] = {
    'attack':{frames:8,fps:8},
    'attacked':{frames:1,fps:8},
    'combat_idle':{frames:4,fps:8},
    'idle':{frames:4,fps:8},
    'ride_idle':{frames:4,fps:8},
    'ride_run':{frames:8,fps:8},
    'run':{frames:8,fps:8},
    'rush':{frames:1,fps:8},
    'spell1':{frames:8,fps:8},
    'spell2':{frames:8,fps:8},
    'spell3':{frames:14,fps:8},
    'spell4':{frames:8,fps:8},
    'spell5':{frames:4,fps:8},
    'spell6':{frames:10,fps:8},
}

export default class NewClass {
    public static getRoleInfo(roleId){
        return AnimConfig[roleId];
    }

}

上面的类用于配置我们对应角色的动画,每一个动画,有动画名,帧数,帧率 三个属性。 每一个角色都有一个编码,如0001,0003。

import AnimConfig from './08_anim_config';

const {ccclass, property} = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {

    @property
    roleId:string = '0001';

    @property
    defaultAnim:string = 'idle';
    // LIFE-CYCLE CALLBACKS:

    private _spriteFrames:Array<cc.SpriteFrame> = [];
    private _lastStartTime = 0;

    private getAnimInfo(animName:string):any{
        var info = AnimConfig.getRoleInfo(this.roleId);
        return info[animName];
    }

    onLoad () {
    }

    playAnim(animName:string){
        var aniInfo = this.getAnimInfo(animName);
        if(!aniInfo){
            return null;
        }
        this.defaultAnim = animName;
        this._lastStartTime = Date.now();

        var folder =  'roles/' + this.roleId + '/';
        var arr = [];
        for(var i = 0; i < aniInfo.frames; ++i){
            var url = folder + animName + '/frame' + i;
            arr.push(url);
        }
        cc.loader.loadResArray(arr,cc.SpriteFrame,function(err,arr){
            this._spriteFrames = arr;
        }.bind(this));
    }

    start () {
        this.playAnim(this.defaultAnim);
    }



    update (dt) {
        if(!this._lastStartTime || !this._spriteFrames.length){
            return;
        }

        var fps = this.getAnimInfo(this.defaultAnim).fps;

        var index = Math.floor((Date.now() - this._lastStartTime) / 1000 * fps);
        if(index > this._spriteFrames.length){
            this.playAnim('combat_idle');
            return;
        }
        index %= this._spriteFrames.length;
        this.node.getComponent(cc.Sprite).spriteFrame = this._spriteFrames[index];
    }
}

在上面的代码中,playAnim被调用的时候,我们首先获取到动画的信息,然后使用cc.loader.loadResArray来加载动画所需要使用到的图片。待加载完毕后,放入对象变量中缓存。

update里,我们根据时间计算出当前帧。 

通过这样的方式,我们就可以基于配置和文件命名规则来实现大量的角色动画和NPC动画。 减少动画编辑的工作量。

二、攻击过程

回合制游戏,最大的特点就是玩家不需要控制角色位置。 所以,近身攻击是需要系统主动移动位置到目标跟前的。 攻击完毕后,又要移回来。 我们在这里,使用了cc.Action组合来做。先欣赏一下代码。

    onCastSpell(event){
        var animName = event.target.__meta;
        if(this._isSpelling){
            return;
        }
        this._isSpelling = true;
        var arr = [];
        //切换成冲刺动画,并移动到目标跟前  
 arr.push(cc.spawn(cc.moveTo(0.3,this.target.node.position.sub(cc.v2(120,0))),cc.callFunc(function(){
            this.hero.playAnim('rush');
        },this)) );
        //播放攻击动画
        arr.push(cc.callFunc(function(){
            this.hero.playAnim(animName);
        },this));
        var animInfo = AnimConfig.getRoleInfo(this.hero.roleId)[animName];
        var playTime = animInfo.frames / animInfo.fps;
        //等待攻击完成
        arr.push(cc.delayTime(0.5 + playTime));
        //移回原来位置
        arr.push(cc.moveTo(0.1,this.hero.node.position));
        arr.push(cc.callFunc(function(){
            this._isSpelling = false;
        },this));

        var act = cc.sequence(arr);
        this.hero.node.runAction(act);
    }

1、冲刺到目标面前

为了实现冲刺到目标面前,我们需要在切换动画的同时,移动角色位置。 Cocos Creator提供了cc.spawn,构建出能够同时执行多个Action的组合Action。cc.callFunc我们理解为自定义Action,你可以在回调函数里编写你期望的逻辑。

2、攻击

我们只需要简单的使用cc.callFunc写一个自定义Action,进行攻击动作的播放即可。

3、等待播放完成

由于我们自己写的动画播放类,还未处理播放完成事件,所以在这里,我们使用了一个cc.delayTime来做等待,等待的时间,是根据动画帧率*帧数 + 一个固定值

4、回到原来位置

这个用cc.moveTo即可实现

5、把它们串起来

我们把所有的Action放入一个数组中,再使用cc.sequence,即可以构建出一个按顺序执行的cc.Action。 并把这个Action交给目标节点执行即可

6、其它

大家会看到一个this._isSpelling的变量,和这个相关的代码,均是为了防止用户在攻击过程中,多次点击出现BUG。

三、技能图标与池

技能图标的实现相当简单,就是把他们放到了一个Layout里面,只不过,我们是动态添加的节点。 在这里,麒麟子使用了一套常用的辅助函数

    public static removeItemToPool(listRoot){
        for(var i = 0; i < listRoot.childrenCount; ++i){
            listRoot.children[i].active = false;
        }
    };
    
    public static addItemFromPool(listRoot){
        for(var i = 0; i < listRoot.childrenCount; ++i){
            var child = listRoot.children[i];
            if(child.active == false){
                child.active = true;
                return child;
            }
        }
    
        var newChild = cc.instantiate(listRoot.children[0]);
        listRoot.addChild(newChild);
        return newChild;
    };

removeItemToPool 会把所有的layout子节点标记为active = false; 

addItemFromPool 会从layout子节点中选择一个active为false的节点,若没有可用的,则复制0号节点来用。 

上面这个套路,麒麟子用了很久了,既简单明了,又能够控制对象池。 大家在做背包,或者其它大量子元素界面的时候可以试试。可以很任性的随意刷新,完全不用担心效率和内存问题。


四、结束语

有在线演示:https://qilinzi.ukylin.net/?lesson=08

源码:https://gitee.com/qilinzi/qlz_ccc_tips

大家朋什么不明白的,或者想了解但麒麟子没有写的。在本博客中留言即可,也可以直接在交流群里@麒麟子,或者私聊麒麟子。 谢谢大家的支持!

发布了230 篇原创文章 · 获赞 542 · 访问量 118万+

猜你喜欢

转载自blog.csdn.net/qq_36720848/article/details/89714656
今日推荐