【JS小游戏】游戏编程初体验之扫雷
前言
昨天接触到一个叫做net的游戏,类似接点灯的那种类型,在一个方阵中随机分配了一些点,然后给了一些管道让玩家将其连通才算过关,完了几把就上瘾了。但是这个游戏是PC端的单机游戏,不方便我在其他地方随时可以玩,于是想自己写一个类似的游戏出来,但是发现自己水平有限,一时没有想出该组合实现管道分布的算法,就只能留着后面慢慢想了,我就退而求其次的写了一个扫雷游戏,也算是进入了游戏这个行业了吧(自我安慰而已啦)。扫雷的游戏相对net来少了那个管道的分布算法,是随机的将雷分不到方阵中,然后由用户一步一步的确定雷的位置。算法相对来说较为简单,具体算法我就不想多做赘述了,在文中末尾记录该小游戏的完整js代码,请各位看官雅正。
界面截图
测试demo页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
</head>
<body>
<div>
<label><span>x:</span><input id="x"/></label>
<label><span>y:</span><input id="y"/></label>
<label><span>mine:</span><input id="mine"/></label>
<input type="submit" id="submit"/>
<div id="game_container">
</div>
</div>
</body>
<script type="text/javascript" src="js/jquery-1.11.0.js"></script>
<script type="text/javascript" src="js/sweeper.js"></script>
<script>
$("#submit").click(function () {
sweeper.init({
x: $("#x").val(),
y: $("#y").val(),
mine: $("#mine").val(),
container: $("#game_container"),
start: function (data) {
},
pass: function (data) {
},
over: function (data) {
}
})
})
</script>
</html>
核心的js代码 sweeper.js
(function (w, $) {
var sweeper = {
data: {
globalData: { isStarted: false, real: 0, rest: 0 }
},
/**
* 初始化游戏
* @param {Object} params初始化参数对象
*/
init: function (params) {
//初始化参数
var globalData = sweeper.data.globalData
globalData.x = params && params.x ? params.x : 10
globalData.y = params && params.y ? params.y : 10
globalData.mine = params && params.mine ? params.mine : 30
globalData.container = params && params.container ? params.container : $("#game_container")
globalData.start = params && params.start ? params.start : function (data) { }
globalData.pass = params && params.pass ? params.pass : function (data) { alert("you are winner") }
globalData.over = params && params.over ? params.over : function (data) { alert("game over") }
globalData.matrix = new Array()
//初始化游戏容器
globalData.container.html("")
globalData.container.css("line-height", "0px")
$("head").append("<style>.grid {width: 1.2em;height: 1.2em;font-size: 1em;margin: 0px;padding: 0px;}</style>")
//计算雷区面积和地雷出现概率
var maxGrid = globalData.x * globalData.y
var mineP = globalData.mine / maxGrid
//构建雷区
for (var i = 0; i < globalData.y; i++) { for (var j = 0; j < globalData.x; j++) { //当前位置随机生成地雷 var isMine = Math.random() <= mineP ? true : false //如果生成了地雷,将游戏全局变量的真实地雷数加1 if (isMine) { globalData.real++ } //构造位置对象,并保存当前位置是否有雷和排雷状态 var grid = { x: j, y: i, isMine: isMine, isSweeped: false } globalData.matrix.push(grid); //向页面游戏容器加入位置格子,并未格子绑定排雷事件 globalData.container.append("<button class='grid' onclick='sweeper.sweep(" + j + "," + i + ")'> </button>") } globalData.container.append("<br />") } //保存页面格子,并初始化当前雷区格子剩余量 globalData.butMatrix = globalData.container.children("button") globalData.rest = globalData.butMatrix.length }, /** * 排雷操作 * @param {Object} x 当前位置横向索引 * @param {Object} y 当前位置纵向索引 */ sweep: function (x, y) { var globalData = sweeper.data.globalData /** * 获取指定位置的详细信息 * @param {Object} x 位置横向索引 * @param {Object} y 位置纵向索引 */ function getGrid(x, y) { if (x < 0 || x >= globalData.x) { return } else if (y < 0 || y >= globalData.y) { return } else { return globalData.matrix[globalData.y * y + x] } } /** * 获取指定位置的格子对象 * @param {Object} x 位置横向索引 * @param {Object} y 位置纵向索引 */ function getGridBut(x, y) { if (x < 0 || x >= globalData.x) { return } else if (y < 0 || y >= globalData.y) { return } else { return $(globalData.butMatrix[globalData.y * y + x]) } } /** * 获取指定位置周边位置详情列表 * @param {Object} x 中心位置横向索引 * @param {Object} y 中心位置纵向索引 */ function listSrd(x, y) { var srdGrid = new Array(); function pushGrid(obj) { if (obj) { srdGrid.push(obj) } } pushGrid(getGrid(x - 1, y - 1)) pushGrid(getGrid(x - 1, y)) pushGrid(getGrid(x - 1, y + 1)) pushGrid(getGrid(x + 1, y - 1)) pushGrid(getGrid(x + 1, y)) pushGrid(getGrid(x + 1, y + 1)) pushGrid(getGrid(x, y + 1)) pushGrid(getGrid(x, y - 1)) return srdGrid } /** * 踩雷,游戏结束 */ function dead() { globalData.over('') //踩雷调用 for (var i = 0; i < globalData.matrix.length; i++) { var grid = globalData.matrix[i] if (!grid.isSweeped) { var but = getGridBut(grid.x, grid.y) if (grid.isMine) { but.css("background-color", "black") } but.attr("disabled", "disabled") } } } /** * 地雷完全排除,顺利通关 */ function pass() { globalData.pass('') //通关调用 for (var i = 0; i < globalData.matrix.length; i++) { var grid = globalData.matrix[i] if (!grid.isSweeped) { var but = getGridBut(grid.x, grid.y) if (grid.isMine) { but.css("background-color", "red") } but.attr("disabled", "disabled") } } } /** * 在未踩雷的情况下进行排雷操作 * @param {Object} grid 排雷位置 */ function compute(grid) { if (!grid.isSweeped) { grid.isSweeped = true globalData.rest-- if (globalData.rest == globalData.real) { pass() } var srdGrid = listSrd(grid.x, grid.y) var gridBut = globalData.butMatrix[globalData.y * grid.y + grid.x] $(gridBut).css("background-color", "white") $(gridBut).attr("disabled", "disabled") var count = 0; for (var i = 0; i < srdGrid.length; i++) { if (srdGrid[i].isMine) { count++ } } if (count == 0) { for (var i = 0; i < srdGrid.length; i++) { compute(srdGrid[i]) } } else { $(gridBut).html(count) } } } // 首次点击时算开始 if (!globalData.isStarted) { globalData.start('') //开始调用 globalData.isStarted = true } //排雷 var grid = getGrid(x, y) if (grid) { if (grid.isMine) { dead() } else { compute(grid) } } } } w.sweeper = sweeper })(window, $)