JS自制简易版数字华容道小游戏
JS自制简易版数字华容道小游戏最近复习了下前端基础知识HTML+JS(是真的非常基础那种,久不用,基本忘完了)。复习了一小部分内容,闲暇之余打算利用刚复习的js基础知识做个小游戏,刚好手机上下载的游戏集中就有一个数字益智游戏——数字华容道,个人感觉这游戏还是挺不错的,工作之余或者饭后可以耍一耍,动动脑子老的快。哈哈哈。数字华容道游戏界面就是一个九宫格的形式(3x3就是标准的九宫格,但是还有4x4、
最近复习了下前端基础知识HTML+JS(是真的非常基础那种,久不用,基本忘完了)。
复习了一小部分内容,闲暇之余打算利用刚复习的js基础知识做个小游戏,刚好手机上下载的游戏集中就有一个数字益智游戏——数字华容道,个人感觉这游戏还是挺不错的,工作之余或者饭后可以耍一耍,动动脑子老的快。哈哈哈。
数字华容道游戏界面就是一个九宫格的形式(3x3就是标准的九宫格,但是还有4x4、5x5、6x6等),在这些小方格中均有一块写有数字的卡片,从左到右从上到下依次递增,最后一个小方格是空的,也就是华容道,一次仅允许一块小方格通过该空格位置移动,即通过不断地移动小方块,交换数字方块的位置,最终完成数字的排除。估计这是很多人小时候体验过的小游戏。话不多说,直接上货。
1、使用html+css来完成基本的游戏界面
首先通过div标签来确定九宫格的外层大小和内层大小,并依照顺序设置标签的子文本节点内容为递增的数字,该数字即为待移动和排序的数字,通过最终的升序排序,以确定游戏是否顺利通关。其实在最开始的设计中,我是打算使用带有数字的图片来实现每个小方格的数字展示的,后面发现一个问题:如果这些图片大小不是一致的话,在快速移动时,会肉眼可见图片之间规格大小不一致,很是影响体验。
将整体的背景色设置为灰色,其中每个小方格之间设置一定的间隔,然后小方格的背景颜色设置为棕色(伪高仿木制卡片),这样整体看起来就有模有样了。
在九宫格上面设置提示“目标图案”,当玩家一进入游戏时,可以明确知道游戏的目标,此时的游戏界面是已经按照顺序排好数字的最终效果,一眼便知。
在九宫格底部,设置游戏开始按钮,通过该按钮启动游戏;按钮右侧设置计时器并每秒刷新当前累计时间。
刚进入游戏时,静态界面如下所示:
静态页面的代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>5x5数字华容道</title>
<style type="text/css">
.div1{
width:518px;
height: 518px;
background-color: #6C6C6C;
}
.div2{
width:100px;
height: 100px;
float: left;
margin-left: 3px;
margin-top: 3px;
background-color: #FFA042;
text-align: center;
font-size: 80px;
color: #842B00;
}
.div3{
margin-top: 20px;
width:512px;
height: 80px;
/*border: solid;*/
border-color: #000000;
text-align: center;
}
.buttonStyle{
width:150px;
height: 50px;
font-size: 30px;
margin-top: 15px;
border-radius: 5%;
background-color: #97CBFF;
}
.timeText{
margin-left: 60px;
font-size: 30px;
}
.inputStyle{
float: auto;
width:100px;
height: 30px;
font-size: 30px;
}
</style>
<script type="text/javascript">
</script>
</head>
<body>
<div class="div3">
<span style="font-size: 50px;color: #D94600;" id="promptText">目标图案</span>
</div>
<div class="div1">
<div class="div2" id="index_0">1</div>
<div class="div2" id="index_1">2</div>
<div class="div2" id="index_2">3</div>
<div class="div2" id="index_3">4</div>
<div class="div2" id="index_4">5</div>
<div class="div2" id="index_5">6</div>
<div class="div2" id="index_6">7</div>
<div class="div2" id="index_7">8</div>
<div class="div2" id="index_8">9</div>
<div class="div2" id="index_9">10</div>
<div class="div2" id="index_10">11</div>
<div class="div2" id="index_11">12</div>
<div class="div2" id="index_12">13</div>
<div class="div2" id="index_13">14</div>
<div class="div2" id="index_14">15</div>
<div class="div2" id="index_15">16</div>
<div class="div2" id="index_16">17</div>
<div class="div2" id="index_17">18</div>
<div class="div2" id="index_18">19</div>
<div class="div2" id="index_19">20</div>
<div class="div2" id="index_20">21</div>
<div class="div2" id="index_21">22</div>
<div class="div2" id="index_22">23</div>
<div class="div2" id="index_23">24</div>
<div class="div2" style="background-color: #6C6C6C;" id="index_24"></div>
</div>
<div class="div3">
<button class="buttonStyle" id="startButton" name="button" value="StartButton">开始游戏</button>
<span class="timeText">时间</span>
<input class="inputStyle" type="text" name="outputTime" id="outputTime" value="" readonly="readonly" />
<span class="timeText" style="margin-left: 0px;">s</span>
</div>
</body>
</html>
2、设计思路分析
总的来说,游戏的玩法就是将乱序的数字,通过唯一的一个空格位置交换顺序,每次点击空格周围的数字卡片,均能交换两者的位置;不断地交换空格和数字卡片的位置,完成数字的升序排序,最后顺利通关。
(1)点击游戏启动按钮后,将九宫格内的数字打乱顺序,空格出现在九宫格中的位置也是随机的;按钮点击一次之后不能再次点击(再次点击无任何作用),防止不断刷新数字卡片位置;
(2)游戏开始后,启动计时器,并不断刷新当前所用时间,将其展示在时间文本中,时间间隔为1秒钟;同时隐藏九宫格上方的文字提示;
(3)游戏中点击数字卡片,若数字卡片旁边有空格,则使两者交换位置,若旁边没有空格,则保持该数字卡片位置不变;点击空格无任何作用(因为空格旁边至少有两个数字卡片,此时不能确定需要移动位置的是哪个数字卡片);
(4)每次移动一次数字卡片的位置,均需要检测一次当前位置更新完成后是否已经顺利通过;若已顺利通过,则弹框提示玩家已经顺利通过,并将所用时间展示出来;若未通过,则继续游戏;
(5)当游戏结束后,应该将启动游戏的按钮和计时器复位,显示时间的文本框根据需要决定是否置为0;
3、上完整代码
根据上面分析的设计思路,依次完成每一个功能点,再根据自己的想法稍微调整调试一下,一个数字华容道小游戏就完成了。
全部的代码,如下面所示;在js代码中的很多地方均有对应注释,方便调试和修改。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>5x5数字华容道</title>
<style type="text/css">
.div1 {
width: 518px;
height: 518px;
background-color: #6C6C6C;
}
.div2 {
width: 100px;
height: 100px;
float: left;
margin-left: 3px;
margin-top: 3px;
background-color: #FFA042;
text-align: center;
font-size: 80px;
color: #842B00;
}
.div3 {
margin-top: 20px;
width: 512px;
height: 80px;
/*border: solid;*/
border-color: #000000;
text-align: center;
}
.buttonStyle {
width: 150px;
height: 50px;
font-size: 30px;
margin-top: 15px;
border-radius: 5%;
background-color: #97CBFF;
}
.timeText {
margin-left: 60px;
font-size: 30px;
}
.inputStyle {
float: auto;
width: 100px;
height: 30px;
font-size: 30px;
}
</style>
<script type="text/javascript">
/**
* 5x5数字华容道,一共有25个格子,我们可以为每一个格子标记下标,通过下标来找到对应的div元素对象
* 格子下标从0开始,每个格子的下标如下所示:
* 0, 1, 2, 3, 4,
* 5, 6, 7, 8, 9,
* 10, 11, 12, 13, 14,
* 15, 16, 17, 18, 19,
* 20, 21, 22, 23, 24
*/
window.onload = function() {
/**
* 定义数字数组numArray,值为0~25,该数组中的元素则为每个格子中显示的数字
* 同时定义div元素节点对象的数组divObjArray,每一个数组元素均为div元素节点对象
* 通过遍历方式,依次对每个数组元素赋值
*
* 为何div元素节点对象通过getElementsByClassName()方法一次性获取到一个对象数组呢?这样明显是更简洁啊
* 因为IE浏览器8.x及以下均不支持getElementsByClassName()方法,为了兼容IE 8.x以下,就放弃了该方式
*/
var numArray = new Array(25);
var divObjArray = new Array(25);
for(var i = 0; i < divObjArray.length; i++) {
numArray[i] = i + 1;
divObjArray[i] = document.getElementById("index_" + i);
}
/**
* 定义“开始游戏”按钮的元素节点对象startButton
* 开始游戏按钮绑定单击响应函数,当玩家点击按钮后,更改按钮显示字样,更改按钮颜色;
* 游戏顺利通关后,复位按钮;
* 定义计时器timer
* 定时器设置计时函数timing,
* 当玩家点击开始按钮进入游戏后,定时器开始计时;
* 游戏顺利通关后,复位计时器;
* 定义计时器所示时间文本的元素节点对象timeText
* 游戏快开始后,每秒显示当前所用时间;
* 游戏结束后,时间文本复位置空
* 定义提示文字“目标图案”的元素节点对象promptText
* 当玩家进入游戏后,将该提示文字隐藏起来;
* 游戏结束后,提示文字再显示
*/
var startButton = document.getElementById("startButton");
var timer;
var curTime = 0;
var timeText = document.getElementById("outputTime");
var promptText = document.getElementById("promptText");
startButton.onclick = function() {
if(startButton.innerHTML == "游戏中...") {
return;
}
// 更改按钮显示字样,更改按钮颜色
startButton.innerHTML = "游戏中...";
startButton.style.backgroundColor = "#FF7575";
// 随机分布1~25数字所在位置
numArray.sort(function() {
return Math.random() > 0.5 ? -1 : 1;
});
for(var i = 0; i < divObjArray.length; i++) {
if(numArray[i] == 25) {
divObjArray[i].innerHTML = "";
divObjArray[i].style.backgroundColor = "#6C6C6C";
continue;
}
divObjArray[i].innerHTML = numArray[i];
divObjArray[i].style.backgroundColor = "#FFA042";
}
// 定时器开始计时
setTimeout(timing, 1000);
// 同步显示时间文本
curTime = 0;
timeText.value = curTime;
// 将顶部的“目标图案”字样隐藏
promptText.style.display = "none";
}
// 计时器
function timing() {
curTime++;
timeText.value = curTime;
timer = setTimeout(timing, 1000);
}
/**
* 使用25代表空白格,通过变量emptyIndex存储该值所在格子的下标
* 并声明更新25空白格位置的函数updateEmptyFun
*/
var emptyIndex = 24;
// 获取空白格所在下标
function updateEmptyFun() {
emptyIndex = numArray.indexOf(25);
}
/**
* 为每一个格子绑定单击响应函数
* 这些格子按照点击移动时,是否有规律、规律是否一致,可以分为6组
* 第1组,没有统一规律的一组,下标分别为0,4,20,24的格子
* 第2组,移动规律为可以移动左、下、右,下标分别为1,2,3的格子
* 第3组,移动规律为可以移动左、上、右,下标分别为21,22,23的格子
* 第4组,移动规律为可以移动上、下、右,下标分别为5,10,15的格子
* 第5组,移动规律为可以移动上、下、左,下标分别为9,14,19的格子
* 第6组,移动规律为可以移动上、下、左、右,下标分别为6,7,8,11,12,13,16,17,18的格子
*
* 将绑定单击响应函数公共部分抽取出来,作为公共函数updatePositionFun,减少冗余代码
* 公共函数部分包含更新25(即空白格子)在数组numArray中的位置,同时更新格子交换后的颜色、数值
*
* 在每次位置更新结束之后,需要判断是否已经完成数字排序;方法为isGameOver
* 若当前空白格未在最后一位,则可直接认为游戏为通关
* 若当前空白格已经在最后一位,则判断前面的所有数字均按照升序排序
*/
function updatePositionFun(divIndex) {
if(startButton.innerHTML == "开始游戏") {
return;
}
numArray[emptyIndex] = divObjArray[divIndex].innerHTML;
numArray[divIndex] = 25;
divObjArray[emptyIndex].innerHTML = divObjArray[divIndex].innerHTML;
divObjArray[emptyIndex].style.backgroundColor = "#FFA042";
divObjArray[divIndex].innerHTML = "";
divObjArray[divIndex].style.backgroundColor = "#6C6C6C";
if(divIndex == 24) {
isGameOver();
}
}
// 判断游戏是否通关
function isGameOver() {
for(var i = 0; i < numArray.length; i++) {
if(numArray[i] != i + 1) {
return;
}
}
// 游戏通关,弹框告知玩家游戏顺利通关,以及所用时间
alert("通关啦! 用时:" + timeText.value + "s");
// 将“开始游戏”按钮复位
startButton.innerHTML = "开始游戏";
startButton.style.backgroundColor = "#97CBFF";
// 将计时器复位
clearTimeout(timer);
// 将顶部的“目标图案”字样设置为显示
promptText.style.display = "block";
}
// 下标为0的格子的单击响应函数
divObjArray[0].onclick = function() {
updateEmptyFun();
if(emptyIndex == 1 || emptyIndex == 5) {
updatePositionFun(0);
}
}
// 下标为4的格子的单击响应函数
divObjArray[4].onclick = function() {
updateEmptyFun();
if(emptyIndex == 3 || emptyIndex == 9) {
updatePositionFun(4);
}
}
// 下标为20的格子的单击响应函数
divObjArray[20].onclick = function() {
updateEmptyFun();
if(emptyIndex == 15 || emptyIndex == 21) {
updatePositionFun(20);
}
}
// 下标为24的格子的单击响应函数
divObjArray[24].onclick = function() {
updateEmptyFun();
if(emptyIndex == 19 || emptyIndex == 23) {
updatePositionFun(24);
}
}
// 其他下标的格子的单击响应函数
function resFun() {
var curIndex;
for(var i = 1; i < divObjArray.length; i++) {
if(i >= 1 && i <= 3) {
divObjArray[i].onclick = function() {
updateEmptyFun();
curIndex = divObjArray.indexOf(this);
if(emptyIndex == curIndex - 1 || emptyIndex == curIndex + 1 || emptyIndex == curIndex + 5) {
updatePositionFun(curIndex);
}
}
continue;
}
if(i >= 21 && i <= 23) {
divObjArray[i].onclick = function() {
updateEmptyFun();
curIndex = divObjArray.indexOf(this);
if(emptyIndex == curIndex - 1 || emptyIndex == curIndex + 1 || emptyIndex == curIndex - 5) {
updatePositionFun(curIndex);
}
}
continue;
}
if(i == 5 || i == 10 || i == 15) {
divObjArray[i].onclick = function() {
updateEmptyFun();
curIndex = divObjArray.indexOf(this);
if(emptyIndex == curIndex - 5 || emptyIndex == curIndex + 5 || emptyIndex == curIndex + 1) {
updatePositionFun(curIndex);
}
}
continue;
}
if(i == 9 || i == 14 || i == 19) {
divObjArray[i].onclick = function() {
updateEmptyFun();
curIndex = divObjArray.indexOf(this);
if(emptyIndex == curIndex - 5 || emptyIndex == curIndex + 5 || emptyIndex == curIndex - 1) {
updatePositionFun(curIndex);
}
}
continue;
}
divObjArray[i].onclick = function() {
updateEmptyFun();
curIndex = divObjArray.indexOf(this);
if(emptyIndex == curIndex - 5 || emptyIndex == curIndex + 5 || emptyIndex == curIndex - 1 || emptyIndex == curIndex + 1) {
updatePositionFun(curIndex);
}
}
}
}
resFun();
}
</script>
</head>
<body>
<div class="div3">
<span style="font-size: 50px;color: #D94600;" id="promptText">目标图案</span>
</div>
<div class="div1">
<div class="div2" id="index_0">1</div>
<div class="div2" id="index_1">2</div>
<div class="div2" id="index_2">3</div>
<div class="div2" id="index_3">4</div>
<div class="div2" id="index_4">5</div>
<div class="div2" id="index_5">6</div>
<div class="div2" id="index_6">7</div>
<div class="div2" id="index_7">8</div>
<div class="div2" id="index_8">9</div>
<div class="div2" id="index_9">10</div>
<div class="div2" id="index_10">11</div>
<div class="div2" id="index_11">12</div>
<div class="div2" id="index_12">13</div>
<div class="div2" id="index_13">14</div>
<div class="div2" id="index_14">15</div>
<div class="div2" id="index_15">16</div>
<div class="div2" id="index_16">17</div>
<div class="div2" id="index_17">18</div>
<div class="div2" id="index_18">19</div>
<div class="div2" id="index_19">20</div>
<div class="div2" id="index_20">21</div>
<div class="div2" id="index_21">22</div>
<div class="div2" id="index_22">23</div>
<div class="div2" id="index_23">24</div>
<div class="div2" style="background-color: #6C6C6C;" id="index_24"></div>
</div>
<div class="div3">
<button class="buttonStyle" id="startButton" name="button" value="StartButton">开始游戏</button>
<span class="timeText">时间</span>
<input class="inputStyle" type="text" name="outputTime" id="outputTime" value="" readonly="readonly" />
<span class="timeText" style="margin-left: 0px;">s</span>
</div>
</body>
</html>
4、写在最后
平时没有写博客的习惯,平时的工作学习中,有需要记录的东西都是本地文档简单记录一下,时间久了要么不记得记录过或者找不到了。。。写博客是个好习惯,希望能慢慢养成这个习惯吧。共勉!
还有,这个小游戏还是有很多地方可以优化的,现在刚整完这个小游戏,就先这样记录一下吧。
更多推荐
所有评论(0)