【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
新做的遊戲可以在公眾號裡線上玩,部落格更新也會在裡面提醒