美文网首页
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