游戏开发入门之五子棋

游戏分析

棋盘

五子棋的棋盘与黑白棋、围棋等具有非常大的相似度,所以为了以后开发黑白棋、围棋等游戏对于五子棋的棋盘需要可以单独使用。基于这个要求我们需要把棋盘作为单独的对象提取出来,并可以应用与任何棋子游戏中。

棋盘应具有以下几个功能:

  1. 可以定义横向和纵向的落子点数量。
  2. 可以感知到鼠标即将点击的位置。
  3. 可以传递出棋盘哪里被点击了的事件。

棋盘绘制分析:

棋盘可以分别绘制左上角、上部、右上角、左部、中间(十字)、右部、左下角、下部、右下角。在显示上可以使用背景图片的方式处理,这样行数为row,列数为col的棋盘可以使用row*col个背景图片来组成,而其中不同的背景图片仅有上面分析的9种。

根据要求对棋盘设计出如下结构:

棋盘的具体实现代码如下:

ChessBoard(row, col, element)={

initBoard,//初始化棋盘,绘制棋盘等操作

hideHover,//隐藏鼠标覆盖提示

getHoverPosition,//获取鼠标覆盖的位置

onBoardClick,//触发棋盘点击事件(棋盘自己调用该方法)

bindBoardClick,//绑定棋盘点击事件

unbindBoardClick//取消绑定棋盘点击事件

}


棋盘配置信息:

var boardConfig = {
	cls : 'board',
	bgLeftTopCls : 'board-lt',
	bgTopCls : 'board-t',
	bgRightTopCls : 'board-rt',
	bgLeftCls : 'board-l',
	bgMiddleCls : 'board-m',
	bgRightCls : 'board-r',
	bgLeftDownCls : 'board-ld',
	bgDownCls : 'board-d',
	bgRightDownCls : 'board-rd',
	hoverCls : 'board-hover'
};

棋盘实现代码:

var ChessBoard = function(rows, cols, ele){
	this.rows = rows;
	this.cols = cols;
	this.ele = $(ele);
	this.initBoard();
};
ChessBoard.prototype = {
	initBoard : function(){//初始化棋盘
		var me = this, rows = me.rows, cols = me.cols, $ele = me.ele, bc = boardConfig;
		$ele.empty();
		$ele.addClass('chessboard');
		var $hover = $('<div/>').addClass(bc.hoverCls);
		me.eleHover = $hover;
		$hover.hide().appendTo($ele);
		var row = 0, col = 0, maxRow = rows - 1, maxCol = cols - 1;
		var cls = bc.bgMiddleCls, $dom;
		for(row = 0; row < rows; row++){
			for(col = 0; col < cols; col++){
				$dom = $('<div/>').addClass(bc.cls);
				if(row == 0){
					switch(col){
					case 0:
						cls = bc.bgLeftTopCls;
						break;
					case maxCol:
						cls = bc.bgRightTopCls;
						break;
					default:
						cls = bc.bgTopCls;
					}
				}else if(row == maxRow){
					switch(col){
					case 0:
						cls = bc.bgLeftDownCls;
						break;
					case maxCol:
						cls = bc.bgRightDownCls;
						break;
					default:
						cls = bc.bgDownCls;
					}
				}else{
					switch(col){
					case 0:
						cls = bc.bgLeftCls;
						break;
					case maxCol:
						cls = bc.bgRightCls;
						break;
					default:
						cls = bc.bgMiddleCls;
					}
				}
				$dom.addClass(cls);
				$dom.attr({row:row, col:col});//便于Dom查找,增加DOM的row与col属性.
				$ele.append($dom);
				(function(row, col, $dom, me){
					$dom.click(function(event){
						if($(event.target).is('.' + bc.cls)){
							me.onBoardClick(row, col, $dom, event);
						}
					});
				})(row, col, $dom, me);
			}
		}
		var $child = $ele.children().eq(0), cw = $child.outerWidth(), ch = $child.outerHeight();
		$ele.css({width:cw * col + 'px', height: ch * row + 'px'});
		$ele.unbind('mouseleave').bind('mouseleave',function(event){
			$hover.hide();
			$hover.removeData('target');
		});
		$ele.children('.' + bc.cls).bind('mouseenter', function(event){//绑定落子点鼠标覆盖事件
			var $dom = $(this);
			$hover.show().offset($dom.offset());
			$hover.data('target', $dom);
		});
		$hover.bind('mouseleave click mouseout dblclick', function(event){//代理落子点事件
			var $tar = $hover.data('target');
			if($tar && $tar.trigger){
				event.target = $tar.get(0);
				$tar.trigger(event);
			}
		});
	},
	hideHover : function(){//隐藏hover显示
		this.eleHover.hide();
	},
	getHoverPosition : function(){//返回[行号,列号], 如果没有hover则返回false
		var $tar = this.eleHover.data('target');
		if($tar && $tar.attr){
			return [$tar.attr('row'), $tar.attr('col')];
		}else{
			return false;
		}
	},
	onBoardClick : function(row, col, $dom, event){//棋盘落子点被点击
		var me = this;
		event.stopPropagation();
		event.preventDefault();
		me.ele.triggerHandler('boardClick', [row, col, $dom, event]);
	},
	bindBoardClick : function(callback){//绑定棋盘落子点点击事件
		this.ele.bind('boardClick', callback);
	},
	unbindBoardClick : function(callback){//取消绑定棋盘落子点点击事件
		this.ele.unbind('boardClick', callback);
	}
};



五子棋游戏

有了棋盘了,下面就是要进行游戏设计了.在设计之前我们先把棋子相关的配置信息写出来。

棋子配置信息

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;
    }
};

程序源代码下载

程序源码及资源文件下载地址:http://download.csdn.net/detail/zyb134506/6928955



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值