美文网首页
Cocos Creator开发游戏消灭星星——星星消除

Cocos Creator开发游戏消灭星星——星星消除

作者: 吃菜小怪兽 | 来源:发表于2018-11-30 11:25 被阅读0次

    上一篇文章写了星星生成的逻辑,详情请看Cocos Creator开发游戏消灭星星——星星生成

    写在消除星星之前

    星星消除是发生在用户点击之后,所以需要处理用户触摸操作。在上一篇制作星星预制时有提及,在脚本组件starCtr.js的start函数里监听触摸。

    start () {
        this.node.on(cc.Node.EventType.TOUCH_START, function (event) {
            //TODO:触摸处理
        }, this);
    },
    

    消除星星是消除上下左右相连的星星,所以需要根据用户点击的星星找到其他相连的星星。在Utils中增加方法needRemoveList:

    //Utils.js
    //检测数组array中是否坐标p
    function indexOfV2 (array, p) {
        return array.some(function (elem, index, arr) {
            return elem.x == p.x && elem.y == p.y
        });
    };
    
    //根据矩阵数据查找消除的星星
    function needRemoveList (data, p) {
        var list = [];
        var travelList = [];
        travelList.push(p);
    
        var tag = data[p.x][p.y];
        do {
            var any = travelList.pop();
            //左
            if (any.y - 1 >= 0 && tag == data[any.x][any.y-1]) {
                var tp = cc.v2(any.x, any.y-1);
                if (!this.indexOfV2(list, tp) && !this.indexOfV2(travelList, tp)) {
                    travelList.push(tp);
                }
            }
            //右
            if (any.y + 1 < Config.matrixCol && tag == data[any.x][any.y+1]) {
                var tp = cc.v2(any.x, any.y+1);
                if (!this.indexOfV2(list, tp) && !this.indexOfV2(travelList, tp)) {
                    travelList.push(tp);
                }
            }
            //下
            if (any.x - 1 >= 0 && tag == data[any.x-1][any.y]) {
                var tp = cc.v2(any.x-1, any.y);
                if (!this.indexOfV2(list, tp) && !this.indexOfV2(travelList, tp)) {
                    travelList.push(tp);
                }
            } 
            //上
            if (any.x + 1 < Config.matrixRow && tag == data[any.x+1][any.y]) {
                var tp = cc.v2(any.x+1, any.y);
                if (!this.indexOfV2(list, tp) && !this.indexOfV2(travelList, tp)) {
                    travelList.push(tp);
                }
            }
            list.push(any);
        } while (travelList.length > 0);
        return list;
    };
    

    消除星星

    现在来完成触摸处理逻辑:

    //starCtr.js
    //触摸处理
    var list = Utils.needRemoveList(GameData.starMatrix, cc.v2(this._gx, this._gy));
    if (list.length >= 2) {
        var event = new cc.Event.EventCustom("delete_stars", true);
        event.detail = list;
        this.node.dispatchEvent(event);
    }
    

    通过用户点击的星星坐标找到与其相连的星星们,然后发射delete_stars事件,通知地图消除星星。关于监听和发射时间参考官方文档监听和发射事件

    在matrixCtr.js的onLoad方法中添加事件监听

    //matrixCtr.js
    onLoad () {
        this.node.on("delete_stars", this.deleteSprites, this);
        //其他无关代码这里省略
    },
    onDestroy () {
        this._starPool.clear();
        this.node.off("delete_stars", this.deleteSprites, this); //移除监听
    },
    

    先添加几个属性来记录消除数据

    //matrixCtr.js
    properties: {
        //其他无关属性这里省略
        _totalCounts: 0, //总的消除星星个数
        _currCount: 0, //已经消除的星星个数
        _bombList: [], //存储待消除的星星的坐标
        _tamping: false, //夯实数组的标记,就是消除星星后的地图处理(星星下落动画、整列星星左移动画等)
    },
    

    在回调函数中处理消除逻辑

    //matrixCtr.js
    // 监听回调函数
    deleteSprites (event) {
        if (this._tamping) {
            return;
        }
        var bombList = event.detail;
        //防止重复消除
        if (Utils.indexOfV2(this._bombList, bombList[0])) {
            return;
        }
        this._totalCounts += bombList.length;
        this._bombList = this._bombList.concat(bombList);
        this.showComboEffect(bombList.length);
        GameData.cleanStarData(bombList);
        this.bomb(bombList, bombList.length);
    }
    

    播放combo特效

    上一篇说过,动画和特效主要放在节点ActionRoot中处理。如图,combo特效就在combNode节点中播放。

    ActionRoot
    特效是用骨骼动画制作的,所以在combNode上添加dragonBones的渲染组件,同时,再添加脚本组件dragonBonesCtr来控制逻辑。

    combNode节点需要播放不同的动画,所以组件中没有指定资源,这个在脚本中控制。看一下dragonBonesCtr.js的属性:
    //dragonBonesCtr.js
    properties: {
        asset: {
            default: [],
            type: dragonBones.DragonBonesAsset,
        },
        atlasAsset: {
            default: [],
            type: dragonBones.DragonBonesAtlasAsset,
        },
        combName: {
            default: [],
            type: cc.String,
        },
        _anim: dragonBones.ArmatureDisplay,
    },
    

    asset、atlasAsset分别存储骨骼动画资源,combName中存储骨骼动画的名字,和资源数组一一对应,_anim是dragonBones组件。

    //dragonBonesCtr.js
    onLoad () {
        this._anim = this.node.getComponent(dragonBones.ArmatureDisplay);
    },
    playComb (type) {
        var i = this.combName.indexOf(type);
        if (i >= 0) {
            this._anim.dragonAsset = this.asset[I];
            this._anim.dragonAtlasAsset = this.atlasAsset[I];
            this._anim.armatureName = "armatureName";
            this._anim.playAnimation("Animation1");
        }
    },
    

    playComb即是播放特效的方法。

    //matrixCtr.js
    showComboEffect (count) {
        if (count == 5) {
            this.combCtr.playComb("GOOD");
        }
        else if (count >= 6 && count <= 7) {
            this.combCtr.playComb("NICE");
        }
        else if (count >= 8 && count <= 9) {
            this.combCtr.playComb("EXCELLENT");
        }
        else if (count >= 10) {
            this.combCtr.playComb("UNBELIEVABLE");
        }
    },
    

    combCtr是脚本组件matrixCtr中的属性,即是场景中ActionRoot节点的脚本组件。

    //matrixCtr.js
    properties: {
        //省略其他属性
        combCtr: cc.Node,
    },
    onLoad () {
        //其他无关代码这里省略
        this.combCtr = this.combCtr.getComponent("dragonBonesCtr");
    },
    

    数据处理

    将需要消除的星星对应的坐标清空(赋值-1)

    //gamedata.js
    //清除星星
    function cleanStarData (list) {
        list.forEach(function (elem, index, arr) {
            this.starMatrix[elem.x][elem.y] = -1;
        }, this);
    };
    

    消除星星

    按规则星星是一个一个消除的,所以bomb会递归调用,直到所有星星都消除。在消除星星的同时,有分数计算和动画逻辑。

    //matrixCtr.js
    bomb (list, count) {
        if (list.length > 0) {
            var gridPos = list.shift();
            var index = Utils.indexValue(gridPos.x, gridPos.y);
            this.bombStar(GameData.starSprite[index]);
            GameData.starSprite[index] = null;
            ++this._currCount;
            //单个方块的分数动画
            var wp = this.convertGridPositionToWorldSpaceAR(gridPos);
            var starScore = Utils.getOneScore(count-list.length);
            this.actCtr.playSingleScoreAction(starScore, wp);
            
            this.scheduleOnce(function () {
                this.bomb(list, count);
            }, 0.1);
            
            if (list.length == 0) {
                //消除总得分动画
                var wp = this.convertGridPositionToWorldSpaceAR(gridPos);
                this.actCtr.showScoreAction(Utils.getScore(count), wp);
                this.uiCtr.updateScoreSchedule(starScore); //当前总分滚动累计效果
            }
            else {
                this.uiCtr.updateScore(starScore);
            }
            this.checkIsSuccessed(); //检测是否达到目标分
        }
        else {
            //TODO: 星星消除完的逻辑处理
        }
    },
    

    星星的移除是在方法bombStar中处理的,在创建星星的时候使用了对象池,所以移除时把它重新放入对象池。

    //matrixCtr.js
    bombStar (node) {
        if (node) {
            var p = node.getPosition();
            var type = node.getComponent("starCtr")._starType;
            this._starPool.put(node); //移除星星,把它放入对象池中
            // 星星爆炸动画
            var particle = cc.instantiate(this.starParticle);
            particle.setPosition(p);
            this.node.addChild(particle);
            particle.getComponent("particleCtr").init(type);
        }
    },
    

    在移除星星的同时,伴随有星星爆炸的特效。starParticle是一个预制,层级很简单,在一个空节点中,添加Particle System组件和脚本组件particleCtr。

    particleCtr
    starSpriteFrames中存储了粒子系统使用的纹理资源,对应每一种星星。
    //particleCtr.js
    properties: {
        particle: cc.ParticleSystem,
        starSpriteFrames: {
            default: [],
            type: cc.SpriteFrame,
        },
    },
    init (type) {
        this.particle.spriteFrame = this.starSpriteFrames[type];
        this.particle.resetSystem();
    },
    

    Particle System组件设置自动移除,在属性检查器中勾选Auto Remove On Finish选项。

    分数计算

    我们知道一次消除星星方块越多,得分越高。

    //Utils.js
    //消除得分计算
    function getScore (count) {
        var score = 0;
        for(var i = 0; i < count; i++) {
            score += this.getOneScore(i);
        }
        return score;
    };
    //消除第i个方块的分数
    function getOneScore (i) {
        return 10 + (i-1) * 5;
    };
    

    分数动画有几种:

    • 单个方块的分数动画
    • 消除总得分动画
    • 当前总分有滚动累计的效果
    //matrixCtr.js
    //格子的世界坐标
    convertGridPositionToWorldSpaceAR (gp) {
        var p = Utils.grid2Pos(gp.x, gp.y);
        var wp = this.node.convertToWorldSpaceAR(p);
        return wp;
    },
    

    动画在actionCtr.js中处理:

    //actionCtr.js
    //单个方块的分数动画
    playSingleScoreAction (score, wp) {
        var label = null;
        if (this._pScorePool.size() > 0) {
            label = this._pScorePool.get();
        }
        else {
            label = cc.instantiate(this.partScore);
        }
        label.getComponent("partScore").setScore(score);
        this.node.addChild(label);
        label.setPosition(this.convertPosition(wp));
    
        var action = cc.spawn(cc.scaleTo(0.5, 0.4), cc.moveTo(0.5, this.refrencePoint.getPosition()));
        label.runAction(cc.sequence(action, cc.delayTime(0.2), cc.callFunc(function(){
            this._pScorePool.put(label);
        }, this)));
    },
    // 世界坐标转成当前节点中的坐标
    convertPosition (wp) {
        return this.node.convertToNodeSpaceAR(wp);
    },
    

    因为分数也会被频繁的创建和移除,所以也使用了对象池,分数的预制制作后面介绍。

    //actionCtr.js
    properties: {
        // 省略其他无关属性
        totalScore: cc.Prefab,
        _tScorePool: null,
        
        partScore: cc.Prefab,
        _pScorePool: null,
        poolCapacity: 30,
    },
    onLoad () {
        this._pScorePool = new cc.NodePool();
        for (var i = 0; i < this.poolCapacity; ++i) {
            var partscore = cc.instantiate(this.partScore);
            this._pScorePool.put(partscore);
        }
        this._tScorePool = new cc.NodePool();
        for (var i = 0; i < 3; ++i) {
            var totalscore = cc.instantiate(this.totalScore);
            this._tScorePool.put(totalscore);
        }
    },
    

    与单个方块的分数动画一样,消除总得分动画:

    //actionCtr.js
    //消除总得分动画
    showScoreAction (score, wp) {
        var node = null;
        if (this._tScorePool.size() > 0) {
            node = this._tScorePool.get();
        }
        else {
            node = cc.instantiate(this.totalScore);
        }
        this.node.addChild(node);
    
        node.getComponent("totalScore").setScore(score);
        var lw = node.getComponent("totalScore").scoreLabel.node.width;
        var ddd = lw * 0.5 * 1.2;
        var eddd = ddd - cc.winSize.width*0.5;
        var p = this.convertPosition(wp);
        if (p.x < eddd) {
            p.x = eddd;
        }
        if (p.x > cc.winSize.width*0.5 - ddd) {
            p.x = cc.winSize.width*0.5 - ddd;
        }
        node.setPosition(p);
    },
    

    分数预制

    分数预制

    层级结构很简单,都是空节点下加一个Label节点。父节点上都有一个脚本组件partScore、totalScore。

    //partScore.js
    properties: {
        scoreLabel: cc.Label,
    },
    setScore (score) {
        this.scoreLabel.string = score;
    },
    

    脚本也很简单,setScore方法给Label赋值。

    //totalScore.js
    properties: {
        scoreLabel: cc.Label,
    },
    setScore (score) {
        this.scoreLabel.string = "+"+score;
        this.playAnim();
    },
    playAnim () {
        var anim = this.scoreLabel.getComponent(cc.Animation);
        anim.play();
    },
    

    与单个分数不同的,总得分的Label动画使用Creator的Animation编辑器制作。所以,预制中需要在节点label中添加Animation组件,在这里我们在添加一个脚本组件totalScoreLabel,这个脚本主要处理Animation动画的事件回调方法。


    相关文章

      网友评论

          本文标题:Cocos Creator开发游戏消灭星星——星星消除

          本文链接:https://www.haomeiwen.com/subject/qbgccqtx.html