我的習慣是(雖然不知道好不好,因爲之前的塔防就是...),會先將遊戲的雛形做在本地端,可以玩後再開始擴增或實作我要的最終版本,所以我們就先來簡單做一個五子棋的遊戲吧
老話,先初始化我們的遊戲,並載入圖片
var config = {
type: Phaser.AUTO,
width: 600,
height: 600,
backgroundColor: 0x705045,
scene: {
preload: preload,
create: create,
}
}
var game = new Phaser.Game(config)
function preload() {
this.load.image('black', 'assets/black.png')
this.load.image('white', 'assets/white.png')
}
function create(){
}
對我來說五子棋有幾個步驟:
畫出棋盤分成兩個部分,看得到的與看不到的,我們都知道五子棋是要下在線的交叉點,但是對程式來說他下的那個是佔一格位置不是四個各佔1/4,所以這邊就分成兩個部分,我們用一個二維陣列裝程式判斷的位置,再來將線畫在格子的中間,來畫出我們的棋盤
var checkerboard = createCheckerboard()
function createCheckerboard() {
let checkerboard = []
for (let i = 0; i < 20; i++) {
checkerboard[i] = new Array()
for (let j = 0; j < 20; j++) {
checkerboard[i][j] = 0
}
}
return checkerboard
}
function drawCheckerboard(graphics) {
graphics.lineStyle(1, 0xffffff, 1)
for (let i = 0; i < 20; i++) {
graphics.moveTo(0, 14.5 + i * 30);
graphics.lineTo(600, 14.5 + i * 30)
}
for (let j = 0; j < 20; j++) {
graphics.moveTo(14.5 + j * 30, 0)
graphics.lineTo(14.5 + j * 30, 600)
}
graphics.strokePath()
}
function create(){
let path = this.add.path(0, 15)
let graphics = this.add.graphics()
drawCheckerboard(graphics)
}
再來判斷下的位置,這邊要提醒,這個看似一個第一象限的 x y 座標圖,但他本質是一個二維陣列,所以在計算格子與之後的計算勝利時,要記得,往上下是修改 第幾列 的值(往上是減往下是加),往左右是修改 第幾行 的值(往左是減往右是加),那我們就來下棋了
這邊下棋的方式與塔防的很類似,先判斷點擊的位置是在二維陣列的那一塊,然後判斷是否有東西,沒有就可以下,並且加在二維陣列裡面與渲染到畫面
var self
function create(){
self = this
this.input.on('pointerdown',putChess)
}
function putChess(pointer){
let i = Math.floor(pointer.y / 30)
let j = Math.floor(pointer.x / 30)
if(isEnpty(i,j)){
checkerboard[i][j] = 'o'
self.add.image(15 + j * 30, 15 + i * 30, 'black')
}
}
function isEnpty(i,j){
return checkerboard[i][j] === 0
}
到這邊就可以在棋盤上一直加黑棋,並且對應的二維陣列會有一個 o 代表黑棋
再來當然是可以白棋黑棋互相下,所以對下棋的地方做一點修改,並有一個狀態表示目前是誰的可以下,並在每次下完後可以換顏色
var status = 'o'
function putChess(pointer){
if(isEnpty(i,j)){
if(status === 'o'){
checkerboard[i][j] = 'o'
self.add.image(15 + j * 30, 15 + i * 30, 'black')
}else{
checkerboard[i][j] = 'x'
self.add.image(15 + j * 30, 15 + i * 30, 'white')
}
status = status === 'o' ? 'x' : 'o'
}
}
最後就是最麻煩也是最關鍵的地方,如何判斷勝利,大家可以去 google 看看相對於五子棋的其他棋類的勝利判斷方式難易度(以四子棋是最容易的,依序是五子棋、暗棋、跳棋、西洋棋與軍棋差不多、最後是圍棋),這個不止在於規則的定製,這個除了規則之外最重要的還有 AI 製作的難易度,因爲電腦要贏,就是要知道誰什麼時候會贏、要怎麼下防止贏、怎樣下才會贏,而以五子棋爲例,有兩種判斷方式:
而我是選擇第二種方式,雖然不優雅但用土法鍊鋼的方式也可以判斷勝利,這邊就以一個方向爲舉例:
以左右爲例,當我下了一個棋子,我會以自己下的那個點爲主,因爲是左右,所以是固定列,變動行的加減,而判斷方式爲跑一個迴圈,如果往左(j - x)一個跟自己的顏色一樣,就先加 1 並且繼續往左(j - x),直到遇到不同顏色或是爲空,反之往右則(j + x)一樣,而最後會回傳是否有超過 5 ,因爲五子棋是 5 個或以上就贏了,所以會回傳左右找有沒有找到大於 5 的 true 或 false
//左右
function horizontalWin(i, j) {
let count = 1
for (let x = 1; x < 5; x++) {
if (checkerboard[i][j] === checkerboard[i][j - x]) {
count += 1
} else {
break
}
}
for (let x = 1; x < 5; x++) {
if (checkerboard[i][j] === checkerboard[i][j + x]) {
count += 1
} else {
break
}
}
return count >= 5
}
當然其他方向的就都一樣的意思啦,但是要注意的地方有一個,小心邊界的大小,這邊遇到的問題會在之後講過程遇到的問題一起討論與解決,但還是跟大家說要注意不要讓第一個數字(行)超出了範圍,所以要先做一個判斷:
for (let y = 1; y < 5; y++) {
if (i - y < 0) {
break
}
//...
}
//在有可能超過的地方先判斷行是否會超出範圍,不會再做接下來的判斷,會就直接跳出迴圈
如果沒問題就可以自己跟自己下棋啦嗚嗚邊緣人
我的 github 有完整的勝利判斷,但是是在 server 的檔案裡面,原因是什麼明天會跟大家講的