最近复习了下前端基础知识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、写在最后

平时没有写博客的习惯,平时的工作学习中,有需要记录的东西都是本地文档简单记录一下,时间久了要么不记得记录过或者找不到了。。。写博客是个好习惯,希望能慢慢养成这个习惯吧。共勉!

还有,这个小游戏还是有很多地方可以优化的,现在刚整完这个小游戏,就先这样记录一下吧。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐