Cocos Creator實現的《點我+1》

1、前言

在學習Cocos中,須要一些東西來練手,因而前段時間就開發仿照一款公司以前的產品《點我+1》來作,仿照過程當中,全部的算法邏輯都是本身研究的,並無參考公司代碼,也沒有使用公司的美術資源,因此也就不存在公司機密的內容啦,徹底只是學習練習而已。
這是一款消除類遊戲,規則和大多數三消類遊戲差很少,在一個5x5的格子中,有25個方塊,每一個方塊有一個數字,用戶的操做就是點擊方塊,使方塊的數字+1,當至少每3個相同數字的時候,這些數字相同的方塊合成爲一個,而且數字+1,在玩家的表現就是:除了點擊的方塊以外,其餘的相同數字的方塊所有消失,而後點擊的方塊再+1。這款遊戲看似很簡單,但實際也是蘊含不少細節以及算法,我也是用了一週時間斷斷續續才作出來。
而且在此次練習中,我也是第一次嘗試使用Cocos Creator來作,使用中也是遇到了很多問題,它與Cocos2d-JS的原生API仍是有必定的差異的,Cocos Creator在我看來就是Cocos在向Unity靠近的一個產物,想要像Unity那樣實現一整套的工做流,將遊戲中的全部元素都組件化,遊戲場景能夠經過拖動就能搭建,遊戲邏輯的實現只須要對組件添加相應的腳本便可,讓遊戲開發更加的方便,更加直觀可視化。javascript

2、Cocos Creator

Cocos Creator是Cocos家族又一個劃時代意義的產物,前面已經提到,我我的認爲這是Cocos向Unity靠近的產物,或許也能夠說,遊戲開發的腳本化、組件化、以及高效工做流、高度可擴展,已經成爲任何一款遊戲引擎的發展方向,這樣的高效快速的開發的工具,纔是開發者的首選工具。瞭解Cocos家族歷史的人應該都知道,Cocos從各類語言版本,到2dx跨平臺版本,到Cocos Stuio、Cocos IDE、Cocos Builder,再到Cocos Creator,這一切都是在使開發者愈來愈方便,使得遊戲開發的門檻愈來愈低,任何人,只要你有心,有遊戲夢,你均可以經過自身的努力來造夢,而Cocos Creator,就是咱們造夢的工具。
不過說實話,Cocos Creator目前是1.1版本,在使用仍然還有許多bug,好比我打包Web版本以後,直接在本地沒法運行,查看控制檯輸出應該是跨域的問題,而發佈到服務器就能正常運行,再好比遊戲的開發者,開發者仍是須要考慮不少各類型號手機屏幕兼容的問題,但願Cocos Creator還能有一套成熟的兼容適配的方案,還有一些我我的的問題或許只是我還不熟悉Cocos的組件化開發而已,須要等到我真正熟悉這款工具,才能用好它,所謂工欲善其事必先利其器,工具用好了,你的造夢之路纔會更加順暢!
Cocos Creator的用戶手冊:http://www.cocos.com/docs/creator/index.html
Cocos Creator的API:http://www.cocos.com/docs/creator/api/index.htmlhtml

3、遊戲分析

這款遊戲是比較常見的三消類遊戲,不過它也有着本身的特點,將數字類遊戲玩法與消除類遊戲玩法相結合,遊戲過程也比較具備挑戰性,讓人產生挑戰的慾望,遊戲的玩法在上文已有描述,沒有玩過這款消除類遊戲的朋友能夠經過如下地址進行下載:
安卓版地址:http://app.mi.com/detail/231892?ref=search
iOS版地址:https://itunes.apple.com/cn/app/dian-wo+1/id1012314214?mt=8
沒玩過的朋友能夠經過以上地址下載下來玩一下,體驗下游戲玩法,而後仔細分析其中的遊戲邏輯運算過程。
經過遊戲玩法的分析咱們至少能夠分析出如下幾點:
1).點擊遊戲方塊,方塊數字+1
2).在至少3個相同數字的方塊彼此相鄰時,能夠合成一個+1的數
3).生成5x5的地圖時,最多隻能有兩個相同數字方塊相鄰
4).每完成一次方塊合成以後,全部方塊向下填補空缺的位置
5).在填補完空缺位置以後,再生成新的方塊
在Cocos Creator中,咱們要熟悉組件化開發,所以咱們須要開始場景、遊戲場景、結束場景、以及方塊預製資源、能量條預製資源,以及各類腳本和音效等。分別用scene、script和voice三個文件夾來存放場景組件,腳本組件和音效組件,另外將預製資源放在根目錄下,以下圖資源目錄:
資源結構圖java

4、遊戲實現

如以前一篇文章對2048遊戲的分析同樣,咱們首先須要一個數組來存放全部的方塊,並將方塊單獨提出來爲一個對象,在這裏,咱們將方塊作成預製資源,在須要的時候直接建立方塊預製資源,一樣,能量條也能夠作成預製資源。另外咱們須要開始場景、遊戲場景和結束場景三個場景,並對這三個場景組件編寫腳本。
在開發寫代碼以前,咱們通過如上的分析,知道了咱們大體要實現的幾個核心算法:
1).掃描某一個位置的上下左右四個方向全部相鄰的相同數字的點,並獲得這些點的個數
2).全部方塊向下移動,填補全部的空缺
核心的就這兩個算法,其餘的都是一些簡單的邏輯,下面先對這兩個算法進行分析node

1.掃描算法

掃描算法實質上就是對指定的點得四周進行掃描,掃描是否有數字相同的點,若是有數字相同的點,那麼還須要進一步對這個數字相同的點再進行掃描,直到掃描的點四周都沒有數字相同的點,才能確認最終相鄰點的個數,實際上就是對指定的點向四周擴散掃描。
個人實現方式是這樣的,寫一個函數,傳入兩個數組,一個用來記錄掃描到的數字相同的點,一個用來記錄掃描過得點,以便於下次掃描再也不掃描已經掃描過的點,除此以外,還須要傳入行,列,上次掃描的行,上次掃描的列(在下次掃描時,不用再對上次的那個方向進行掃描),以及掃描要比對的數字。經過指定的點,我向上、下、左、右四個方向進行掃描,若是數字相同,我就將這個點加入到記錄數字相同的點得數組中,並對這個點再遞歸調用這個函數,在四個方向全都遞歸掃描完畢後,我獲得的這個掃描過的點的數組,就是全部與指定點的數字相同且彼此相鄰的點的集合。可能我描述不是太清楚,我直接將代碼貼出來,個人算法基礎不是太好,可能這不是最優的算法,但願有朋友能爲我提供更好更快的算法。git

/*
 * 核心掃描邏輯
 * @param row 指定行
 * @param col 指定列
 * @param lastRow 上次掃描的行
 * @param lastCol 上次掃描的列
 * @param num 掃描要比對的數字
 * @param arr 記錄數字相同且彼此相鄰的數組
 * @param scanArr 記錄掃描過的點的數組
 */
scanAround:function(row,col,lastRow,lastCol,num,arr,scanArr){
    // cc.log("row:",row,",col:",col,",lastRow:",lastRow,",lastCol:",lastCol,",num:",num,",arr:",arr,",scanArr:",scanArr);
    if(this.tiles[row][col]==null){
        return;
    }
    var isClear = false;
    if(scanArr==undefined){
        scanArr = new Array();
    }
    // 掃描過的節點再也不掃描
    if(scanArr.indexOf(row+"#"+col)==-1){
        scanArr.push(row+"#"+col);
    }else{
        return;
    }
    // 掃描上
    if(row<4&&(lastRow!=(row+1)||lastCol!=col)&&this.tiles[row+1][col]!=null){
        var nextNum = parseInt(this.tiles[row+1][col].getComponent("Tile").numLabel.string);
        if(nextNum==num){
            if(arr.indexOf(row+"#"+col)==-1){
                arr.push(row+"#"+col);
            }
            this.scanAround(row+1,col,row,col,num,arr,scanArr);
            isClear = true;
        }
    }
    // 掃描下
    if(row>0&&(lastRow!=(row-1)||lastCol!=col)&&this.tiles[row-1][col]!=null){
        var nextNum = parseInt(this.tiles[row-1][col].getComponent("Tile").numLabel.string);
        if(nextNum==num){
            if(arr.indexOf(row+"#"+col)==-1){
                arr.push(row+"#"+col);
            }
            this.scanAround(row-1,col,row,col,num,arr,scanArr);
            isClear = true;
        }
    }
    // 掃描左
    if(col>0&&(lastRow!=row||lastCol!=(col-1))&&this.tiles[row][col-1]!=null){
        var nextNum = parseInt(this.tiles[row][col-1].getComponent("Tile").numLabel.string);
        if(nextNum==num){
            if(arr.indexOf(row+"#"+col)==-1){
                arr.push(row+"#"+col);
            }
            this.scanAround(row,col-1,row,col,num,arr,scanArr);
            isClear = true;
        }
    }
    // 掃描右
    if(col<4&&(lastRow!=row||lastCol!=(col+1))&&this.tiles[row][col+1]!=null){
        var nextNum = parseInt(this.tiles[row][col+1].getComponent("Tile").numLabel.string);
        if(nextNum==num){
            if(arr.indexOf(row+"#"+col)==-1){
                arr.push(row+"#"+col);
            }
            this.scanAround(row,col+1,row,col,num,arr,scanArr);
            isClear = true;
        }
    }
    // 四周都不通,但不是出發遍歷點,而且數字相同,也加入到數組
    if(!isClear&&(lastRow!=-1&&lastCol!=-1)){
        var curNum = parseInt(this.tiles[row][col].getComponent("Tile").numLabel.string)
        if(curNum==num){
            if(arr.indexOf(row+"#"+col)==-1){
                arr.push(row+"#"+col);
            }
        }
    }
}

2.移動算法

移動算法相對就比較簡單了,我只須要對每一列從下往上遍歷,當遍歷中發現方塊不爲null時(即這個點有方塊),咱們就以這個方塊爲起點,對這一列往下遍歷,在子循環中,若是下方爲null時(即下方沒有方塊),這個方塊就向下移動一個單位,一直循環到下方見底,或者下方遇到方塊,纔會中止。這個算法相對簡單,代碼以下:github

for (var col = 0; col < 5; col++) {
    for (var row = 0; row < 5; row++) {
        if (this.tiles[row][col] != null) {// 有方塊
            for (var row1 = row; row1 > 0; row1--) {
                if (this.tiles[row1 - 1][col] == null){
                    //若是沒有向下移動
                    this.tiles[row1 - 1][col] = this.tiles[row1][col];
                    this.tiles[row1][col] = null;
                    this.tiles[row1 - 1][col].getComponent("Tile").moveTo(row1 - 1, col);
                }
            }
        }
    }
}

3.方塊類

方塊類咱們經過Creator的預製資源來製做,以下圖所示:
tile圖
在Tile預製資源中,咱們只須要一個背景層,和一個顯示數字的Label便可,其背景層的顏色和數字顯示都經過腳原本控制,方塊須要包含如下幾個方法:
1).產生新方塊的初始化及特效
2).移動到指定點的邏輯及特效
3).方塊銷燬的邏輯及特效
4).設置方塊數字以及動態改變其背景色
5).在方塊的初始化onLoad函數中,須要對方塊添加TOUCH_START事件,事件中綁定上面提到的4方法,方塊類的具體實現代碼以下:
1).方塊初始化函數
添加觸摸點擊事件,綁定設置數字函數,清空combo次數(combo次數記錄放在全局組件Global中)算法

onLoad: function () {
    var self = this;
    this.node.on(cc.Node.EventType.TOUCH_START,function(event){
        if(!self.game.isCal){
            cc.audioEngine.playEffect(self.clickEffect);
            self.game.isCal = true;
            // 連擊次數歸零
            Global.combo = 0;
            cc.audioEngine.playEffect(this.addCoin);
            self.setNum(parseInt(self.numLabel.string)+1,true,false);
        }
    }, this.node);
}

2).產生新方塊
執行從小變大的動畫,設置數組中的行列到屬性中chrome

newTile:function(row,col){
    this.node.setPosition(5+(5+this.node.width)*col+this.node.width/2,5+(5+this.node.height)*row+this.node.height/2);
    this.node.setScale(0);
    this.node.runAction(cc.scaleTo(0.1,1));
    this.setArrPosition(row,col);
}

3).移動到指定點
執行移動動畫,設置數組中行列到屬性中api

moveTo:function(row,col){
    this.row = row;
    this.col = col;
    this.node.stopActionByTag(1);
    var action = cc.moveTo(0.2,cc.p(5+(5+this.node.width)*col+this.node.width/2,5+(5+this.node.height)*row+this.node.height/2));
    this.node.runAction(action);
    action.setTag(1);
}

4).方塊銷燬
執行從大變小的動畫,執行完動畫以後調用destory方法銷燬方塊跨域

destoryTile:function(){
    var action = cc.sequence(cc.scaleTo(0.1,0),cc.callFunc(function(node){
        node.destroy();
    },this.node,this.node));
    this.node.runAction(action);
}

5).設置方塊數字
設置方塊的顯示數字,並動態改變相應數字對應的顏色,顏色存在全局組件變量Colors中,經過參數exeLogic判斷是否須要執行遊戲的消除邏輯,經過playEffect判斷是否須要播放音效

setNum:function(num,exeLogic,playEffect){
    this.game.maxNum = num>this.game.maxNum?num:this.game.maxNum;
    this.numLabel.string = num;
    switch(num){
        case 1:
            this.node.color = Colors.num1;
            break;
        case 2:
            this.node.color = Colors.num2;
            break;
        case 3:
            this.node.color = Colors.num3;
            break;  
        case 4:
            this.node.color = Colors.num4;
            break;
        case 5:
            this.node.color = Colors.num5;
            break;
        case 6:
            this.node.color = Colors.num6;
            break;  
        case 7:
            this.node.color = Colors.num7;
            break;
        case 8:
            this.node.color = Colors.num8;
            break;
        case 9:
            this.node.color = Colors.num9;
            break;  
        case 10:
            this.node.color = Colors.num10;
            break;
        case 11:
            this.node.color = Colors.num11;
            break;
        case 12:
            this.node.color = Colors.num12;
            break;  
        case 13:
            this.node.color = Colors.num13;
            break;
        case 14:
            this.node.color = Colors.num14;
            break;
        case 15:
            this.node.color = Colors.num15;
            break;
        case 16:
            this.node.color = Colors.num16;
            break;
        case 17:
            this.node.color = Colors.num17;
            break;
        case 18:
            this.node.color = Colors.num18;
            break;
        case 19:
            this.node.color = Colors.num19;
            break;
        case 20:
            this.node.color = Colors.num20;
            break;
        default:
            this.node.color = Colors.nums;
            break;
    }
    // 播放特效
    if(playEffect){
        this.node.runAction(cc.sequence(cc.scaleTo(0.15,1.5),cc.scaleTo(0.15,1)));
    }
    // 消除邏輯
    if(exeLogic){
        // 執行邏輯
        var isMove = this.game.operateLogic(this.row,this.col,parseInt(this.numLabel.string),true);
        var powers = this.game.powers;
        // 能量條-1
        if(!isMove){
            for (var i = powers.length - 1; i >= 0; i--) {
                if(powers[i]!=null){
                    var costBarAction = cc.sequence(cc.scaleTo(0.1,0),cc.callFunc(function(power){
                        power.destroy();
                    },null,powers[i]));
                    powers[i].runAction(costBarAction);
                    powers[i] = null;
                    break;
                }
            };
            // 遊戲結束邏輯判斷:能量條爲空
            if(powers[0]==null){
                Global.score = this.game.scoreNum.string;
                // Game Over
                cc.director.loadScene("overScene");
            }
        }
    }
}

4.能量類

同方塊類,咱們將遊戲場景中的能量條也作成預製資源,其中只須要添加一個背景層就能夠,背景層也是經過代碼動態控制顏色,能量條不須要任何腳本文件。

5.開始場景

作好了預製資源以後,咱們就能夠先作開始場景了,效果圖以下:
startScene圖
開始場景中有三個元素,遊戲文字,開始按鈕,和背景層,其中游戲文字呈現一個變大變小循環播放的動畫,點擊開始遊戲按鈕便可轉入遊戲場景。具體代碼以下:
1).onLoad加載
動態設置元素的位置及寬高,以適配各類型號手機屏幕

onLoad: function () {
    // 背景鋪平
    this.bg.width = cc.winSize.width;
    this.bg.height = cc.winSize.height;
    this.bg.setPosition(this.bg.width/2,this.bg.height/2);
    this.bg.color = Colors.startBg;
    // 設置文字
    var action = cc.repeatForever(cc.sequence(cc.scaleTo(1, 1.5),cc.scaleTo(1,1)));
    this.gameName.runAction(action);
    this.gameName.setPosition(cc.winSize.width/2,cc.winSize.height/2);
    // 設置按鈕
    this.startBtn.setPosition(this.gameName.getPositionX(),this.gameName.getPositionY()-210);
}

2).開始遊戲按鈕回調
點擊進入遊戲場景

startGame:function(){
    cc.audioEngine.playEffect(this.btnEffect);
    cc.director.loadScene("gameScene");
}

6.結束場景

結束場景與開始場景差很少,相比之下,結束場景主要是提供遊戲分數特效呈現的功能,效果圖以下:
overScene圖
結束場景也是幾個Label,一個背景層和一個Button按鈕組成,其中最重要的是分數特效,經過從0一直往上加,加到實際分數的特效,來使玩家得到成就感,具體代碼以下:
1).加載函數
動態設置場景中元素的寬高及位置,播放分數計算特效,綁定TOUCH_START事件,使特效當即播放完成

onLoad: function () {
    // 背景層
    this.bg.width = cc.winSize.width;
    this.bg.height = cc.winSize.height;
    this.bg.setPosition(this.bg.width/2,this.bg.height/2);
    this.bg.color = Colors.overBg;
    // 文字層
    this.gameText.setPosition(cc.winSize.width/2,cc.winSize.height/2);
    var action = cc.repeatForever(cc.sequence(cc.scaleTo(1, 1.5),cc.scaleTo(1,1)));
    this.gameText.runAction(action);
    // 播放結束音效
    cc.audioEngine.playEffect(this.overEffect);
    // 分數
    this.scoreText.setPosition(this.gameText.getPositionX(),this.gameText.getPositionY()+200);
    this.score = Global.score;
    this.schedule(this.updateScore,0.1,cc.REPEAT_FOREVER,2);
    // 點擊分數當即加到最高分數
    var self = this;
    this.bg.on(cc.Node.EventType.TOUCH_START,function(event){
        cc.log("score text touch");
        cc.audioEngine.playEffect(self.addCoin);
        self.changeScore = self.score;
        self.scoreLabel.string = "最終分數:"+self.changeScore;
    }, this.bg);
    // 返回按鈕
    this.backBtn.setPosition(this.gameText.getPositionX(),this.gameText.getPositionY()-200);
}

2).更新分數回調
每一次更新分數回調,將特效分數增長20,直到加滿到遊戲得分,並將這一過程顯示在場景的Label中

updateScore(){
    if(this.score<=this.changeScore){
        this.unschedule(this.updateScore);
    }
    this.changeScore += 20;
    this.changeScore = this.changeScore>this.score?this.score:this.changeScore;
    // 添加音效
    cc.audioEngine.playEffect(this.addCoin);
    this.scoreLabel.string = "最終分數:"+this.changeScore;
}

3).返回按鈕回調
遊戲分數的全局變量歸零,切換加載開始遊戲的場景

back:function(){
    Global.score = 0;
    cc.audioEngine.playEffect(this.btnEffect);
    cc.director.loadScene("startScene");
}

7.遊戲場景

最主要的仍是遊戲場景,在遊戲場景中,咱們進行全部的遊戲邏輯的運算,包括前面提到的兩個核心算法,遊戲場景的效果圖以下:
gameScene圖
在遊戲主場景中,咱們主要須要在onLoad加載時,初始化25個方塊,並放在地圖中,使他們之間相鄰個數小於3個,而後咱們須要提供一個主要邏輯判斷函數,這個函數中判斷遊戲中方塊的消除邏輯,再前面的Tile的setNum函數中進行調用,代碼以下:
1).場景加載
在場景加載中,咱們須要初始化25個互相相鄰小於3個的方塊,與5個能量條,而且動態設置全部元素的寬高與位置。

onLoad: function () {
    // 播放背景音樂
    cc.audioEngine.playMusic(this.bgMusic,true);
    // 初始化方塊數組
    this.tiles = [
        [null,null,null,null,null],
        [null,null,null,null,null],
        [null,null,null,null,null],
        [null,null,null,null,null],
        [null,null,null,null,null]
    ];
    this.powers = [null,null,null,null,null];
    // 背景層
    this.bg.width = cc.winSize.width;
    this.bg.height = cc.winSize.height;
    this.bg.setPosition(-cc.winSize.width/2,-cc.winSize.height/2);
    this.bg.color = Colors.gameBg;
    // 頂部背景層
    this.topBg.width = cc.winSize.width-30;
    this.topBg.height = 100;
    this.topBg.setPosition(-cc.winSize.width/2+15,(cc.winSize.width-30)/2);
    // 能量條背景層
    this.powerBarBg.width = cc.winSize.width-30;
    this.powerBarBg.height = this.powerBarBg.width/5/2;
    this.powerBarBg.setPosition(15-cc.winSize.width/2,this.topBg.getPositionY()-200);
    this.powerBarBg.color = Colors.powerBarBg;
    // 方塊背景層
    this.tileBg.width = cc.winSize.width-30;
    this.tileBg.height = this.tileBg.width;
    this.tileBg.setPosition(15-cc.winSize.width/2,this.powerBarBg.getPositionY()-10-this.tileBg.height);
    this.tileBg.color = Colors.tileBg;
    // 生成能量條
    for(var i=0;i<5;i++){
        var power = cc.instantiate(this.powerPre);
        power.width = (this.powerBarBg.width-30)/5;
        power.height = this.powerBarBg.height-10;
        this.powerBarBg.addChild(power);
        power.setPosition(5+(5+power.width)*i+power.width/2,5+power.height/2);
        power.color = Colors.power;
        this.powers[i] = power;
    };
    // 生成初始方塊
    for(var row=0;row<5;row++){
        for(var col = 0;col<5;col++){
            var tile = cc.instantiate(this.tilePre);
            tile.getComponent("Tile").game = this;
            tile.width = (this.tileBg.width-30)/5;
            tile.height = (this.tileBg.height-30)/5;
            var count = 0;
            var maxRandom = 5;
            var randomNum = 0;
            while(true){
                count++;
                var arr = new Array();
                var scanArr = new Array();
                if(count>10){
                    maxRandom++;
                }
                randomNum = Math.ceil(Math.random()*maxRandom);
                tile.getComponent("Tile").setNum(randomNum,false,false);
                tile.setPosition(5+(5+tile.width)*col+tile.width/2,5+(5+tile.height)*row+tile.height/2);
                this.tiles[row][col] = tile;
                this.scanAround(row,col,-1,-1,randomNum,arr,scanArr);
                if(arr.length<3){
                    break;
                }
            }
            tile.getComponent("Tile").setArrPosition(row,col);
            this.tileBg.addChild(tile);
        }
    }
}

2).掃描邏輯
前面已經講過
3).主要操做邏輯
在這個函數中,咱們須要對指定的點進行掃描,若是相鄰數超過三個,則進行合成動做,而後更新分數,將全部的方塊向下移動填補,並補充一條能量條,增長連擊次數

operateLogic:function(touchRow,touchCol,curNum,isFirstCall){
    var arr = new Array();
    var scanArr = new Array();
    this.scanAround(touchRow,touchCol,-1,-1,curNum,arr,scanArr);
    if(arr.length>=3){
        var addScore = 0;
        for(var index in arr){
            var row = arr[index].split("#")[0];
            var col = arr[index].split("#")[1];
            addScore += parseInt(this.tiles[row][col].getComponent("Tile").numLabel.string*10);
            if(row!=touchRow||col!=touchCol){
                // 執行銷燬動做                    
                this.tiles[row][col].getComponent("Tile").destoryTile();
                this.tiles[row][col] = null;
            }else{
                this.tiles[row][col].getComponent("Tile").setNum(curNum+1,false,true);
                this.maxNum = curNum+1>this.maxNum?curNum+1:this.maxNum;
            }
        }
        // 更新分數
        this.scoreNum.string = parseInt(this.scoreNum.string)+addScore;
        this.scheduleOnce(function() {
            // 0.1s後全部方塊向下移動
            this.moveAllTileDown();
        },0.1);
        if(!isFirstCall){
            // 能量條補充一格
            for(var i=0;i<5;i++){
                if(this.powers[i]==null){
                    var power = cc.instantiate(this.powerPre);
                    power.width = (this.powerBarBg.width-30)/5;
                    power.height = this.powerBarBg.height-10;
                    this.powerBarBg.addChild(power);
                    power.setPosition(5+(5+power.width)*i+power.width/2,5+power.height/2);
                    power.color = Colors.power;
                    power.setScale(0);
                    power.runAction(cc.scaleTo(0.1,1));
                    this.powers[i] = power;
                    break;
                }
            };
        }
        // 連擊次數+1
        Global.combo++;
        // cc.log("連擊次數:"+Global.combo);
        // 播放音效
        switch(Global.combo){
            case 1:
                cc.audioEngine.playEffect(this.star1);
            break;
            case 2:
                cc.audioEngine.playEffect(this.star2);
            break;
            case 3:
                cc.audioEngine.playEffect(this.star3);
            break;
            case 4:
                cc.audioEngine.playEffect(this.star4);
            break;
            case 5:
                cc.audioEngine.playEffect(this.star5);
            break;
            case 6:
                cc.audioEngine.playEffect(this.star6);
            break;
            case 7:
                cc.audioEngine.playEffect(this.star7);
            break;
            default:
                cc.audioEngine.playEffect(this.star7);
            break;
        }
        return true;
    }else{
        this.isCal = false;
    }
    return false;
}

4).全部方塊向下移動
除了上文提到全部方塊的移動算法以外,在移動完成以後,還須要進行新產生一批方塊,並再次判斷消除邏輯的操做

moveAllTileDown:function(){
    for (var col = 0; col < 5; col++) {
        for (var row = 0; row < 5; row++) {
            if (this.tiles[row][col] != null) {// 有方塊
                for (var row1 = row; row1 > 0; row1--) {
                    if (this.tiles[row1 - 1][col] == null){
                        //若是沒有向下移動
                        this.tiles[row1 - 1][col] = this.tiles[row1][col];
                        this.tiles[row1][col] = null;
                        this.tiles[row1 - 1][col].getComponent("Tile").moveTo(row1 - 1, col);
                    }
                }
            }
        }
    }
    this.scheduleOnce(function() {
        // 0.3s後生成新方塊
        for (var col = 0; col < 5; col++) {
            for (var row = 0; row < 5; row++) {
                if(this.tiles[row][col]==null){
                    var tile = cc.instantiate(this.tilePre);
                    tile.getComponent("Tile").game = this;
                    tile.width = (this.tileBg.width-30)/5;
                    tile.height = (this.tileBg.height-30)/5;
                    var maxRandom = this.maxNum;
                    var randomNum = Math.ceil(Math.random()*maxRandom);
                    tile.getComponent("Tile").setNum(randomNum,false,false);
                    tile.getComponent("Tile").newTile(row,col);
                    this.tiles[row][col] = tile;
                    this.tileBg.addChild(tile);
                }
            }
        }
        // 0.5s後遍歷執行邏輯
        this.scheduleOnce(function() {
            var isSearch = false;
            for (var col = 0; col < 5; col++) {
                for (var row = 0; row < 5; row++) {
                    if(!isSearch){
                        isSearch = this.tiles[row][col]!=null&&this.operateLogic(row,col,parseInt(this.tiles[row][col].getComponent("Tile").numLabel.string),false);
                    }
                }
            }
        }, 0.5);        
     }, 0.3);
}

5、運行效果

最後的運行效果以下
點我+1效果圖
經過CVP平臺的項目託管可看到實際運行效果,地址以下:

  • 因爲我的對Cocos Creator的適配還不大瞭解,在pc上運行時,須要使用chrome的手機調試預覽(或者將chrome窗口調至手機屏幕大小),可是若是直接用手機打開,loadScene彷佛又有不兼容的問題,至今沒法理解,還請大神指點,而我不管是開發時的預覽,仍是經過xcode打包到個人iPhone6s Plus手機運行,都是徹底沒有問題。不知道是否是Creator的h5兼容尚未作好,又或者我哪裏的技術沒有掌握全面。
    http://www.cocoscvp.com/usercode/946fc5fd5d2c77331cc344ea1e4bfdd04630cf85/

6、源代碼

全部源代碼均上傳到github,歡迎交流學習,地址:
https://github.com/hjcenry/addone

  • 原文博客:http://hjcenry.github.io
相關文章
相關標籤/搜索