1. 程式人生 > >遊戲開發入門之五子棋

遊戲開發入門之五子棋

有了棋盤了,下面就是要進行遊戲設計了.在設計之前我們先把棋子相關的配置資訊寫出來。

棋子配置資訊

var chessConfig = {
	cls : 'chess',//棋子元素預設樣式
	blackChess : {
		name : '黑棋',
		cls : 'black',
		lastCls : 'black-last',
		flag : 1
	},
	whiteChess : {
		name : '白棋',
		cls : 'white',
		lastCls : 'white-last',
		flag : 2
	},
	defFlag : 0,
	direction : {//棋子排列方向
		heng : [0, 1],//橫向
		shu : [1, 0],//豎向
		pie : [-1, 1],//撇
		na : [1, 1]//捺
	}
};

棋盤使用示例

為了提高興趣與驗證棋盤的可用性,我決定先測試下落子功能。落子功能很簡單,僅需繫結棋盤點選事件,然後在事件中對應的DOM元素內插入個棋子就可以。具體程式碼如下:

var board = new ChessBoard(15, 15, $('#board'));
board.bindBoardClick(function(event, row, col, $dom, ev){
	var $chess = $('<div/>').addClass(chessConfig.cls).addClass(chessConfig.blackChess.cls);
	$dom.empty().append($chess);
});
通過上面5行程式碼就可將棋子新增到棋盤中顯示了.

五子棋遊戲設計

分析遊戲規則

五子棋遊戲是在row*col大小的棋盤上使用黑子、白子來展開的對戰遊戲。其中每個落子點有3個狀態(空、黑子、白子),每次落子後都要檢查落子後選手是否贏得遊戲,遊戲的勝利規則為落子後該子所在點的橫豎撇捺四個方向上相同的棋子數量》=5即可贏得遊戲。

首先,根據棋盤大小我們建立一個row*col的二維陣列,用於儲存整個棋盤的狀態資訊,預設棋盤狀態為0(chessConfig.defFlag)。棋子資訊為chessConfig.xxxChess.flag,目前我們只有blackChess(黑棋)和whiteChess(白棋)兩個型別的棋子。

其次,每次落子後需要進行連子計算,分別計算4個方向上的連子數量。已落子點為原點建立直角座標系不難發現,4個方向上棋子的座標與原點座標都是遞增(從左向右看)或遞減(從右向左看)變化。根據這個規則我們分別定義了4個方向上的單步遞增量chessConfig.direction,這樣我們在計算某個方向上的連子數量時僅傳入落子點的座標row,col,棋子的標記,棋盤狀態資訊(二維陣列),方向遞增量即可計算出該方向上的連子數量(注意除了正向數量還要加上反向數量,反向遞增量僅需把遞增量個座標*-1即可)。

最終,每次落子後計算完是否勝利,如果勝利則提示遊戲結束XXX勝利,否則需要切換下棋方,比如之前是黑棋落子,經判斷沒有勝利則下次落子則應為白棋落子。

五子棋遊戲實現程式碼

var FiveChess = function(conf){
    this.conf = conf;
    var row = this.row = conf.row || 15;
    var col = this.col = conf.col || 15;
    var $board = this.$board = $(conf.board);
    this.chessDatas = new Array(row);
    this.chessBoard = new ChessBoard(row, col, $board);
    this.initFiveChess();
};
FiveChess.prototype = {
    userFirst : true,//是否使用者先手
    useBlack : true,//使用者是否使用黑色棋子
    userChess : chessConfig.blackChess,//使用者棋子資訊
    rivalChess : chessConfig.whiteChess,//對手棋子資訊
    userTurn : true,//是否輪到使用者下棋
    chessBoard : null,//棋盤物件
    lastChesses : {},//最後下棋資訊
    errors : {
        putChess : []
    },
    initFiveChess : function(){
        var me = this, row = me.row, col = me.col, $board = me.$board;
        me.userFirst = true;//黑棋先下
        me.useBlack = true;//當前使用者使用黑棋
    },
    initGame : function(){
        var me = this;
        me.chessBoard.bindBoardClick(me.getBoardClickListener());
        me.userChess = me.useBlack ? chessConfig.blackChess : chessConfig.whiteChess;
        me.rivalChess = me.useBlack ? chessConfig.whiteChess : chessConfig.blackChess;
        me.userTurn = me.userFirst;
        me.chessBoard.initBoard();
        for(var i = 0; i < me.row; i++){
            me.chessDatas[i] = new Array(me.col);
            for(var j = 0; j < me.col; j++){
                me.chessDatas[i][j] = chessConfig.defFlag;
            }
        }
    },
    gameStart : function(){
        var me = this;
        me.gameOver();
        me.initGame();
    },
    gameOver : function(){
        var me = this;
        me.chessBoard.unbindBoardClick(me.getBoardClickListener());
    },
    serviceNext : function(){
        var me = this;
        me.userTurn = !me.userTurn;
    },
    checkWin : function(row, col, chess, chessDatas){
        var me = this, chessDatas = me.chessDatas;
        var fx1 = me.chessLineNums(row, col, chess.flag, chessDatas, chessConfig.direction.heng, true),
        fx2 = me.chessLineNums(row, col, chess.flag, chessDatas, chessConfig.direction.shu, true),
        fx3 = me.chessLineNums(row, col, chess.flag, chessDatas, chessConfig.direction.pie, true),
        fx4 = me.chessLineNums(row, col, chess.flag, chessDatas, chessConfig.direction.na, true),
        maxLineNum = Math.max(fx1, fx2, fx3, fx4);//獲取四個方向上最大連子數量
        if(maxLineNum >= 5){
            alert(chess.name + '贏了!');
            me.gameOver();
            return true;
        }
        return false;
    },
    getBoardClickListener : function(){//繫結事件處理函式的作用域(對遊戲自身的引用).
        var me = this;
        if(!me.onBoardClick){
            me.onBoardClick = function(event, row, col, $dom, ev){
                var chessDatas = me.chessDatas,
                    chess = me.userTurn ? me.userChess : me.rivalChess;
                var putResult, isWin;
                putResult = me.putChess(row, col, chess);
                if(putResult){
                    isWin = me.checkWin(row, col, chess, chessDatas);
                    if(!isWin){
                        me.serviceNext();
                    }
                }
            };
        }
        return me.onBoardClick;
    },
    onBoardClick : undefined,
    putChess : function(row, col, chess){
        var me = this, chessDatas = me.chessDatas;
        var selector = 'div.{0}[row="{1}"][col="{2}"]'.format([boardConfig.cls, row, col]);
        $dom = me.chessBoard.ele.find(selector);
        if(chessDatas[row][col] == chessConfig.defFlag){
            var $chess = $('<div/>').addClass(chessConfig.cls).addClass(chess.lastCls);//新增棋子並標記最後落子樣式
            $dom.append($chess);
            chessDatas[row][col] = chess.flag;
            var $prevLastChess = me.lastChesses[chess.flag];
            if($prevLastChess && $prevLastChess.removeClass){//移除上一次標記的最後落子樣式
                $prevLastChess.removeClass(chess.lastCls).addClass(chess.cls);
            }
            me.lastChesses[chess.flag] = $chess;
            return 1;
        }else{
            return 0;
        }
    },
    chessLineNums : function(row, col, chessType, arr, direction, checkReverse){//下子座標row, 下子座標Y, 下子型別, 棋子陣列, 方向[行,列], 是否檢查反向
        var w = arr[0].length, h = arr.length;
        var dRow = direction[0], dCol = direction[1];
        var nums = 1;
        var srow = row, scol = col, multiple = 1;
        while(true){
            srow += dRow;//下一方向X座標
            scol += dCol;//下一方向Y座標
            if(srow < w && scol < h && srow >= 0 && scol >= 0){//座標是否在棋盤內
                var type = arr[srow][scol];
                if(type == chessType){//下一方向棋子型別相同
                    nums++;
                }else{//下一方向棋子型別不同,退出
                    break;
                }
            }else{
                break;
            }
            multiple++;
        }
        if(checkReverse){
            nums = nums + this.chessLineNums(row, col, chessType, arr, [-dRow, -dCol], false) - 1;//加上反向的資料
        }
        return nums;
    }
};

程式原始碼下載