1. 程式人生 > >【Cocos Creator實戰教程(6)】——get47(數字消除)

【Cocos Creator實戰教程(6)】——get47(數字消除)

先來看一下游戲效果

遊戲玩法:
這裡寫圖片描述

  • 遊戲操作仿的是天天愛消除,點選一個方塊向相鄰的方塊滑動就會交換兩個方塊
  • 當沒有可移動的方塊時,可以點選下面的update按鈕
  • 橫向相連的方塊數字之和會增加分數,縱向相連的方塊數字之和會減少分數
  • 最終目的就是get47

為什麼是47呢?

因為47是我們的女主角嘛

這是我們的遊戲場景
這裡寫圖片描述

Tile就代表方塊,TileLayout就是用來裝Tile的,當然我們還是會在腳本里動態新增tile,整體結構很簡單,根據工程檔案就可以看的很清楚,這裡就不講了

下面來重點講一下演算法

先來看一下Tile的指令碼
Tile.js

cc.Class({
    extends
: cc.Component, properties: { pics:{ type:cc.SpriteFrame, default:[], }, _type:1, posIndex:cc.Vec, type:{ set:function(value){ this._type = value; this.node.getComponent(cc.Sprite).spriteFrame = this
.pics[value-1]; }, get:function(){ return this._type; } }, isAlive:true }, onLoad: function () { this.initType(); }, initType:function(){ this.type = Math.floor(Math.random()*this.pics.length) + 1
; }, });

這裡有三個重要的屬性,type,isAlive,posIndex,先記住它們

再來看一下TileController的指令碼的主要方法

這裡寫圖片描述

這幾個方法裡最核心的就是中間的三個:deleteTiles,fallTiles,addTiles
顧名思義,
deleTiles:刪除相連線的方塊
fallTiles:deleTiles後會有一些空白,這時就需要把上面的方塊落下來
addTiles:fallTiles後上面就有一些空白,所以就要在上面填上新的方塊

他們的倫理關係如下(如果你們覺得我的字好看,請誇我一下,如果覺得不好看,請保持沉默。。。)
這裡寫圖片描述
由於大小限制,圖片有點渣。。。

Tile裡有一個posIndex,這個屬性是用來標記tile的位置資訊的,之所以要用一個屬性來標記,而不是直接移動位置,是為了實現tile的動畫效果,因為在fallTiles時,會有很多的tile同時落下,我們的做法是先把所有要下落的tile找出來,然後一起讓他們執行動作:position=posIndex

主體思路如上,具體的細節請看程式碼
TileController.js

var DIR = cc.Enum({
    UP:-1,
    DOWN:-1,
    LEFT:-1,
    RIGHT:-1
});
var GAME_STATE = cc.Enum({
    PLAYING:-1,
    WIN:-1
});
cc.Class({
    extends: cc.Component,

    properties: {
        gameState:{
            type:GAME_STATE,
            default:GAME_STATE.PLAYING
        },
        score:0,
        scoreLabel:cc.Label,
        tiles:[],
        tilesLayout:cc.Node,
        tilePrefab:cc.Prefab,
        rowNum:0,
        colNum:0,
        padding:0,
        spacing:0,
    },

    init: function (game) {
        this.game = game;
        this.scoreLabel.string = this.score+"";
        this.gameState = GAME_STATE.PLAYING;
        this.tileWidth = (this.tilesLayout.width - this.padding*2 - this.spacing*(this.colNum-1))/this.colNum;
        for(let y=0;y<this.rowNum;y++){
            for(let x=0;x<this.colNum;x++){
                let tile = cc.instantiate(this.tilePrefab);
                this.tilesLayout.addChild(tile);                
                tile.width = this.tileWidth;
                tile.height = this.tileWidth;
                tile.position = cc.p(this.padding + x*(this.tileWidth+this.spacing),
                    this.padding + y*(this.tileWidth+this.spacing));
                tile.getComponent("Tile").posIndex = tile.position;
                tile.tag = y*this.colNum + x;
                this.addTouchEvent(tile);
                this.tiles.push(tile);
            }
        }
        this.touchAble = false;
        this.deleteTiles();
    },

    addTouchEvent(tile){
        var p1,p2,dir;
        let self = this;
        var getDir = function(){
            if(Math.abs(p2.x-p1.x) > Math.abs(p2.y-p1.y)){
                if(p2.x>p1.x){
                    dir = DIR.RIGHT;
                }else{
                    dir = DIR.LEFT;
                }
            }else{
                if(p2.y>p1.y){
                    dir = DIR.UP;
                }else{
                    dir = DIR.DOWN;
                }
            }
            if(self.gameState === GAME_STATE.PLAYING && self.touchAble){
                self.touchAble = false;
                self.tryExchange(tile, dir);
            }
        }
        tile.on('touchstart',function(e){
            p1 = e.getLocation();
        },this);
        tile.on('touchmove',function(e){

        },this);
        tile.on('touchend',function(e){
            p2 = e.getLocation();
            getDir();
        },this);
        tile.on('touchcancel',function(e){
            p2 = e.getLocation();
            getDir();
        },this);

    },

    tryExchange(tile,dir){
        var neighborTile = this.getNeighborTile(tile,dir);
        if(neighborTile === null){
            return;
        }

        this.exchangeTwoTilesState(tile, neighborTile);
        var hLines = this.getHorizontalLines();
        var vLines = this.getVerticalLines();
        if(hLines.length+vLines.length > 0){
            this.exchangeTwoTilesPosIndex(tile, neighborTile);
            let finished = 0;
            let total = 2;
            let self = this;
            let action1 = cc.sequence(cc.moveTo(0.15,tile.getComponent("Tile").posIndex),
                cc.callFunc(function(){
                    finished++;
                    if(finished == total){
                        self.deleteTiles();
                    }
                })
            );
            let action2 = cc.sequence(cc.moveTo(0.15,neighborTile.getComponent("Tile").posIndex),
                cc.callFunc(function(){
                    finished++;
                    if(finished == total){
                        self.deleteTiles();
                    }
                })
            );
            tile.runAction(action1);
            neighborTile.runAction(action2);
        }else{
            this.exchangeTwoTilesState(tile, neighborTile);
            var finished = 0;
            var total = 2;
            var self = this;
            var tilePos = tile.position;
            var neighborTilePos = neighborTile.position;
            var action1 = cc.sequence(cc.moveTo(0.1,neighborTilePos),cc.moveTo(0.1,tilePos),
                cc.callFunc(function(){
                    finished++;
                    if(finished == total){
                        self.touchAble = true;
                    }
                })
            );
            var action2 = cc.sequence(cc.moveTo(0.1,tilePos),cc.moveTo(0.1,neighborTilePos),
                cc.callFunc(function(){
                    finished++;
                    if(finished == total){
                        self.touchAble = true;
                    }
                })
            );
            tile.runAction(action1);
            neighborTile.runAction(action2);
        }


    },

    exchangeTwoTilesState(tile1,tile2){
        this.tiles[tile1.tag] = tile2;
        this.tiles[tile2.tag] = tile1;

        var tile1Tag = tile1.tag;
        tile1.tag = tile2.tag;
        tile2.tag = tile1Tag;
    },

    exchangeTwoTilesPosIndex(tile1,tile2){//交換位置資訊,實際位置沒有改變
        var tile1Pos = tile1.getComponent("Tile").posIndex;
        var tile2Pos = tile2.getComponent("Tile").posIndex;

        tile1.getComponent("Tile").posIndex = tile2Pos;
        tile2.getComponent("Tile").posIndex = tile1Pos;
    },

    deleteTiles(){
        let self = this;
        var hLines = this.getHorizontalLines();
        var vLines = this.getVerticalLines();
        if(hLines.length + vLines.length===0){
            this.touchAble = true;
            return;
        }
        var addNumber = 0;//橫加豎減
        var minusNumber = 0;
        var lines = [];
        for(let i in hLines){
            addNumber += hLines[i].getComponent("Tile").type;
            lines.push(hLines[i]);
        }
        for(let i in vLines){
            minusNumber += vLines[i].getComponent("Tile").type;
            let isExist = false;
            for(let j in hLines){
                if(hLines[j] === vLines[i]){
                    isExist = true;
                }
            }
            if(!isExist){
                lines.push(vLines[i]);
            }
        }

        this.score += (addNumber - minusNumber);
        if(this.score === 47){
            this.gameState = GAME_STATE.WIN;
            this.scoreLabel.string = "YOU GET 47!";
        }else{
            this.scoreLabel.string = this.score+"";
        }

        var finished = 0;
        var total = lines.length;
        for(let i=0;i<total;i++){
            let action = cc.sequence(cc.scaleTo(0.15, 0, 0),
                cc.callFunc(function(){
                    finished++;
                    if(finished == total){
                        self.fallTiles();
                    }
                })
            );
            lines[i].getComponent("Tile").isAlive = false;
            lines[i].runAction(action);
        }
    },

    fallTiles(){
        let self = this;
        //下落
        var isAllFall = false;
        while(!isAllFall){
            isAllFall = true;
            for(let y=1;y<this.rowNum;y++){
                for(let x=0;x<this.rowNum;x++){
                    if(this.tiles[y*this.colNum+x].getComponent("Tile").isAlive && !this.tiles[(y-1)*this.colNum+x].getComponent("Tile").isAlive){
                        this.exchangeTwoTilesState(this.tiles[y*this.colNum+x], this.tiles[(y-1)*this.colNum+x]);
                        this.exchangeTwoTilesPosIndex(this.tiles[y*this.colNum+x], this.tiles[(y-1)*this.colNum+x]);
                        isAllFall = false;
                    }
                }
            }
        }
        var fallingTiles = [];
        for(let i in this.tiles){
            if(this.tiles[i].getComponent("Tile").posIndex !== this.tiles[i].position){
                fallingTiles.push(this.tiles[i]);
            }
        }

        var finished = 0;
        var total = fallingTiles.length;
        for(let i=0;i<total;i++){
            let action = cc.sequence(cc.moveTo(0.3, fallingTiles[i].getComponent("Tile").posIndex),
                cc.callFunc(function(){
                    finished++;
                    if(finished == total){
                        self.addTiles();
                    }
                })
            );
            fallingTiles[i].runAction(action);
        }

    },

    addTiles(){
        let self = this;
        //填補空白
        var addingTiles = [];
        for(let y=0;y<this.rowNum;y++){
            for(let x=0;x<this.rowNum;x++){
                if(!this.tiles[y*this.colNum+x].getComponent("Tile").isAlive){
                    addingTiles.push(this.tiles[y*this.colNum+x]);
                }
            }
        }

        var finished = 0;
        var total = addingTiles.length;
        for(let i=0;i<total;i++){
            let action = cc.sequence(cc.scaleTo(0.15, 1,1),
                cc.callFunc(function(){
                    finished++;
                    if(finished == total){
                        self.deleteTiles();
                    }
                })
            );
            addingTiles[i].getComponent("Tile").initType();
            addingTiles[i].getComponent("Tile").isAlive = true;
            addingTiles[i].runAction(action);
        }
    },

    getVerticalLines(){
        var lineTiles = [];
        var count = 1;
        for(let x=0;x<this.colNum;x++){
            for(let y=0;y<this.rowNum-2;y=y+count){
                let tile = this.tiles[y*this.colNum+x];
                let tileType = tile.getComponent("Tile").type;
                count = 1;
                for(let n=y+1;n<this.rowNum;n++){
                    if(this.tiles[n*this.colNum+x].getComponent("Tile").type === tileType){
                        count++;
                    }else{
                        break;
                    }
                }
                if(count>=3){
                    for(let i=0;i<count;i++){
                        lineTiles.push(this.tiles[(y+i)*this.colNum+x]);
                    }
                }
            }
        }
        return lineTiles;
    },

    getHorizontalLines(){
        var lineTiles = [];
        var count = 1;
        for(let y=0;y<this.rowNum;y++){
            for(let x=0;x<this.colNum-2;x=x+count){
                let tile = this.tiles[y*this.colNum+x];
                let tileType = tile.getComponent("Tile").type;
                count = 1;
                for(let n=x+1;n<this.colNum;n++){
                    if(this.tiles[y*this.colNum+n].getComponent("Tile").type === tileType){
                        count++;
                    }else{
                        break;
                    }
                }
                if(count>=3){
                    for(let i=0;i<count;i++){
                        lineTiles.push(this.tiles[y*this.colNum+x+i]);
                    }
                }
            }
        }
        return lineTiles;
    },

    getNeighborTile(tile,dir){
        var x = tile.tag % this.colNum;
        var y = (tile.tag-x) / this.rowNum;
        switch(dir){
            case DIR.LEFT:
                if(x===0){
                    return null
                }else{
                    return this.tiles[y*this.colNum+(x-1)];
                }
            case DIR.RIGHT:
                if(x===this.colNum-1){
                    return null
                }else{
                    return this.tiles[y*this.colNum+(x+1)];
                }
            case DIR.UP:
                if(y===this.rowNum-1){
                    return null
                }else{
                    return this.tiles[(y+1)*this.colNum+x];
                }
            case DIR.DOWN:
                if(y===0){
                    return null
                }else{
                    return this.tiles[(y-1)*this.colNum+x];
                }
        }
    },

    newView(){
        if(this.gameState == GAME_STATE.PLAYING && this.touchAble == true){
            var self = this;
            this.touchAble = false;
            var finished = 0;
            var total = this.tiles.length;
            for(let i=0;i<total;i++){
                let action = cc.sequence(cc.scaleTo(0.3, 0, 0),
                    cc.callFunc(function(){
                        finished++;
                        if(finished == total){
                            self.addTiles();
                        }
                    })
                );
                this.tiles[i].getComponent("Tile").isAlive = false;
                this.tiles[i].runAction(action);
            }
        }
    }
});

工程檔案:

後話:

成功得到分數47,會顯示”You get 47!”,但是我的直覺告訴我,好像時態不對,原諒很渣的英語水平。。。

這裡寫圖片描述

所以,就有了這個遊戲

所以,想看什麼方面的教程可以再部落格下面或者微信公眾號裡留言

所以,你還不關注一下公眾號?

新手程式設計師:xinshouit
這裡寫圖片描述

新做的遊戲可以在公眾號裡線上玩,部落格更新也會在裡面提醒