棋類遊戲中的棋子擺放邏輯
其實要點就一句話:我們看到的不應是棋盤,而是座標。
現在通過下面的五子棋(或圍棋)實例來看下如何理解這句話。
運行效果如下:
Cocos Creator版本:2.2.2
後臺回覆"棋",獲取該項目完整文件。
創建節點
1. bg爲Sprite類型節點,作爲場景背景。
2. board bg也是Sprite類型節點,作爲棋盤背景。
3. board雖然爲空節點,但是非常關鍵,因爲棋子都是添加到這個節點上的。它的大小應等於棋盤上可落子區域大小:
爲方便開發,我們可以將該節點的錨點設置爲(0, 0),那麼對棋子來說左下角就是座標原點了。
4. dot預製也會添加到board節點上。棋盤上有多少個點,就會有多少個預製。每個預製都會進行觸摸監聽,玩家點擊到哪個dot,那麼我們就在相應的dot位置上放一個棋子。
5. piece預製用來當做棋子。
代碼編寫
首先我們新建一個Board.js腳本,添加屬性並編寫onLoad方法內容:
// Board.js
properties: {
piecePrefab: cc.Prefab, // 棋子預製
touchDotPrefab: cc.Prefab // 觸摸點預製
},
// LIFE-CYCLE CALLBACKS:
onLoad () {
cc.debug.setDisplayStats(false);
const hLines = 15; // 水平線數量
const vLines = 15; // 垂直線數量
this.blockWidth = (this.node.width / (vLines-1)).toFixed(2); // 每塊區域的寬度
this.blockHeight = (this.node.height / (hLines-1)).toFixed(2); // 每塊區域的高度
this.boardPosArray = []; // 棋盤上所有點的座標
this.getBoardPos(hLines, vLines);
this.currentPlayer = 'BLACK'; // 當前着步方
this.initTouchDots(); // 添加所有觸摸點
},
1. hLines和vLines常量用來保存棋盤上的水平線和垂直線數量,五子棋(圍棋)的棋盤是一個正方形,所以橫豎線數量相等,都爲15。
2. 既然已經知道了可落子區域的總大小和橫豎線數量,那麼我們就可以求出每塊小區域的大小了。
3. boardPosArray數組用來存儲棋盤上各個點的座標。而獲取各個點的座標就在getBoardPos方法中:
// Board.js
getBoardPos(hLines, vLines) {
for (let i=0; i<hLines; i++) {
for (let j=0; j<vLines; j++) {
let x = j*this.blockWidth;
let y = i*this.blockHeight;
this.boardPosArray.push([x, y, '0']);
}
}
},
boardPosArray數組的每個元素包含一個x座標,一個y座標以及用來判斷當前位置是否存在棋子的標識,初始化時都是'0'。
4. currentPlayer變量用來存儲當前的着步方。
5. initTouches用來添加觸摸點,方法編寫如下:
// Board.js
initTouchDots() {
for (let i=0; i<this.boardPosArray.length; i++) {
let touchDot = cc.instantiate(this.touchDotPrefab);
touchDot.opacity = 0;
this.node.addChild(touchDot);
touchDot.getComponent('TouchDot').initBoard(this);
touchDot.setPosition(this.boardPosArray[i][0], this.boardPosArray[i][1])
}
},
觸摸點是不能被看到的,所以透明度需要設爲0。筆者這裏給大家看下沒有設置爲透明時的樣子:
該預製上掛着的TouchDot.js腳本編寫如下:
// TouchDot.js
cc.Class({
extends: cc.Component,
properties: {
},
// LIFE-CYCLE CALLBACKS:
onLoad () {
this.node.on('touchstart', this.onTouchStart, this);
},
initBoard(board) {
this.board = board;
},
onTouchStart() {
this.board.putPiece(this.node.x, this.node.y);
}
});
initBoard方法用於獲取Board.js腳本實例。在onTouchStart方法中我們調用Board.js中的putPiece方法直接將棋子放置到該觸摸點的位置。
putPiece方法編寫如下:
// Board.js
putPiece(x, y) {
// 放置棋子,並更新着步方xx
let is_ok = this.check(x.toFixed(2), y.toFixed(2));
if (!is_ok)
return;
let piece = cc.instantiate(this.piecePrefab);
this.node.addChild(piece);
piece.getComponent('Piece').setPic(this.currentPlayer);
piece.width = this.blockWidth*0.9;
piece.height = this.blockHeight*0.9;
piece.setPosition(x, y)
if (this.currentPlayer == 'BLACK') {
this.currentPlayer = 'WHITE';
}
else {
this.currentPlayer = 'BLACK';
}
},
在該方法中,我們首先要調用check方法判斷當前位置是否已經有棋子了,如果沒有那麼就在該位置上放一個棋子,放置完畢則更改着步方。
check方法編寫如下:
// Board.js
check(x, y) {
// 查看當前位置是否可以着步
for (let i=0; i<this.boardPosArray.length; i++) {
if (x==this.boardPosArray[i][0] && y==this.boardPosArray[i][1]){
if (this.boardPosArray[i][2]=='0') {
// 可以放置,並更新數組
this.boardPosArray[i][2] = '1';
return true;
}
else {
// 已有棋子,不可放置
return false;
}
}
}
}
其實就是循環boardPosArray數組,判斷該位置上是'0'還是'1',如果是前者,那麼表明可以放置。
注:筆者這裏的判斷只是給大家進行簡單進行演示,並不一定符合下棋規則。比如圍棋中的某個空位落子後會直接沒"氣"(也就是說該空位已經被圍死了),那麼也不能在這個空位上落子對吧。
棋子預製上掛着的Piece.js腳本編寫如下:
// Piece.js
cc.Class({
extends: cc.Component,
properties: {
blackPic: cc.SpriteFrame,
whitePic: cc.SpriteFrame
},
// LIFE-CYCLE CALLBACKS:
onLoad () {},
setPic(currentPlayer) {
if (currentPlayer=='BLACK') {
this.node.getComponent(cc.Sprite).spriteFrame = this.blackPic;
}
else if (currentPlayer=='WHITE') {
this.node.getComponent(cc.Sprite).spriteFrame = this.whitePic;
}
}
});
blackPic和whitePic就是黑白棋子圖片,而setPic方法會根據當前的着步方來設置相應的棋子圖片。
好,那本節教程就到這,希望大家有所收穫~
P.S. 雖然筆者這裏用的是五子棋(或圍棋)這個例子,但是開發邏輯同樣適用於其他棋類,比如象棋。