接上篇~~第一次写这种技术博客,发现把自己做的东西介绍出来还是一件脑力活,不是那么轻松啊,好吧,想到哪写到哪,流水记录之,待完成之后再根据大家的意见进行修改吧。

游戏实现

根据对扫雷游戏的体验和分析,游戏的实现过程有三个比较繁琐的地方:

1. 棋盘布局及初始化,这个过程主要完成游戏棋盘的生成,布局雷点的位置并记录存储,根据雷点的位置及雷点与数值之间的对应关系,遍历雷点8个方向上的8个节点,查找这个8个节点各自周围的雷点数之和S,最终记录并存储节点的数值(S),完成棋盘上各非雷点的数值计算后,根据节点和图片对象的关系呈现用户界面给玩家。

2. 玩家鼠标左键点击棋盘上某个节点之后,程序根据节点在棋盘上的坐标,分析该节点的属性,如果该节点为空(即其周围没有雷点,数值为0),则遍历节点8个方向上的节点(这8个节点必然是非雷点)并展开,如果这8个节点中有空节点,则重复上述过程,递归查找并遍历所有。

3.  棋盘上某节点(数值为N)被标记展开,且在其周围已经标记了M个雷点,当玩家用鼠标左键和右键一起点击时,需要分三种情况考虑,当M=N时,如果玩家雷点标记正确,则遍历该点8个方向上的节点并展开(此时8个方向上已没有雷点),如果玩家雷点标记错误,则游戏结束。当M>N或者M时,表示玩家多或少标记了雷点,此时需要提醒玩家修改,闪烁该点在8个方向上没有展开的节点。

为了更清晰的表达和理解,棋盘、雷点底层数据结构、雷点表层图片对象在页面上的关系如下图:

游戏过程中出现的节点描述及示例如下图:

下面根据上述三个难点的分析,逐个进行编程解决:

 一、棋盘的布局及初始化

该过程包括棋盘的生成、雷点的布置、非雷点数值计算、棋盘展现四个过程,主要流程见下图:

我们通过定义棋盘类(BombObjectList)来实现,类的初步定义(主要实现了该类基本属性的定义)具体如下:

 //雷点集合棋盘展现的主控操作类
function BombObjectList(PlaceId, XCount, YCount, BombCount) {
//保存当前对象,以防函数嵌套时指代不清
var me = this; me.ContentId = PlaceId; //展现棋盘容器的元素id
me.ContentObj = null; //展现棋盘容器的元素对象
me.ObjList = []; //棋盘节点集合对象列表
me.xNum = XCount; //棋盘x轴长度
me.yNum = YCount; //棋盘y轴长度
me.bombNum = BombCount; //雷点的个数
me.PlateNum = [this.xNum]; //棋盘节点集合数组二维数组
me.BombList = []; //雷点集合列表
me.enmbVal = ["North", "NorthEast", "East", "EastSouth", "South", "SouthWest", "West", "WestNorth"]; //8方位踩点的枚举值
}

为了实现上述四个过程,这里为该类定义了一个初始化函数,标记为Initial

me.Initial = function() {

第一步,生成棋盘,根据玩家定义的宽度(XCount)和高度(YCount)初始化棋盘,棋盘中的节点对象(BombObject)均采用默认值,节点数值初始化为0:

       //初始化模板数据,默认数据
for (var i = 0; i < me.xNum; i++) {
me.PlateNum[i] = [];
for (var j = 0; j < me.yNum; j++) {
me.ObjList.push(new BombObject(i, j, 0, XCount, YCount));
me.PlateNum[i][j] = 0;
}
}

第二步,布置雷点,在棋盘节点范围内,根据玩家定义的雷点个数(BombCount)采用随机数动态生成坐标值对并记录(重复的要重新生成),然后修改棋盘中对应该坐标的节点数值:

        //布雷
for (var s = 0; s < this.bombNum; ) {
var x = parseInt(Math.random() * me.xNum);
var y = parseInt(Math.random() * me.yNum);
//判断是否已经标记为雷,是则再次获取,不是则进行标记
if (me.PlateNum[x][y] !== -1) {
//获取该点在节点集合中的对象,并修改
var CheckResult = me.CheckObjItem(x, y).obj;
CheckResult.IsBomb = true;
CheckResult.DisplayNum = -1;
CheckResult.ImgObj.SetImgNum(-1);
//更新点数棋盘
me.PlateNum[x][y] = -1;
//更新雷点数组
me.BombList.push(CheckResult);
//取下一个雷点
s++;
}
}

第三步,计算非雷点数值,遍历上一步产生的雷点,计算雷点周围8个位置上节点(是雷点的除外)的数值,然后修改棋盘中对应该非雷点的节点数值,方位遍历顺序见上图:

    //计算数值
for (var r in me.BombList) {
var tempObj = me.BombList[r];
//对当前雷点进行八方位踩点,计算其周围的节点数值,计算过就不再计算,且不算雷点
for (var i = 0; i < me.enmbVal.length; i++) {
var _Obj = eval("tempObj." + me.enmbVal[i]);
//如果该方位的节点是否存在
if (_Obj != null) {
var _X = _Obj.X;
var _Y = _Obj.Y;
//获取该点在节点集合中的对象
var tempObjEx = me.CheckObjItem(_X, _Y).obj;
//如果该方位点的数值为0(非0表示计算过或者是雷点),表示需要计算其周围8个点雷的总数
if (tempObjEx.DisplayNum === 0) {
var num = me.CountAroundNum(tempObjEx);
tempObjEx.DisplayNum = num;
tempObjEx.ImgObj.SetImgNum(num);
//更新点数棋盘
me.PlateNum[_X][_Y] = num;
}
}
}
}

至此棋盘的初始化完成,返回this对象,方便链式调用:

    //返回当前this对象,以实现链式调用
return me;
}

第四步,棋盘的展现,遍历棋盘中所有节点对象,并将节点包裹一层图片对象,然后输出到页面的文档中,这里通过定义一个展现函数实现,标记为Display:

me.Display = function() {
//输出
var oFragment = document.createDocumentFragment();
for (var h = 0; h < me.ObjList.length; h++) {
var strObj = me.ObjList[h].ImgObj.ImgObj;
oFragment.appendChild(strObj);
if ((h + 1) % me.yNum === 0) {
oFragment.appendChild(document.createElement("br"));
}
}
//清空掉已有的元素
for (var i = me.ContentObj.childNodes.length - 1; i >= 0; i--) {
var tempNode = me.ContentObj.childNodes[i];
tempNode.parentNode.removeChild(tempNode);
}
//赋值新生成的元素
me.ContentObj.appendChild(oFragment);
}

以上四步完成了游戏棋盘部分的生成和展现,其中用到两个辅助函数CheckObjItem和CountAroundNum,前者是根据x、y坐标获取棋盘中对应的节点对象,后者是计算雷点某方位上节点的数值,其代码如下:

 //根据x、y坐标获取节点集合中节点
me.CheckObjItem = function(x, y) {
//验证x、y坐标
if (x >= 0 && x < me.xNum && y >= 0 && y < me.yNum) {
try {
//查找该点
var tempObj = me.ObjList[x * me.yNum + y];
if (tempObj.EqualsEx(x, y)) {
if (tempObj.DisplayNum === -1) {
//如果该点是雷点
return { obj: tempObj, IsIn: true };
}
else {
return { obj: tempObj, IsIn: false };
}
}
}
catch (e) {
//找不到该点
return { obj: null, IsIn: false };
}
}
else {
throw new Error("the input is validate.");
return { obj: null, IsIn: false };
}
};
//计算非雷点的数值并存储
me.CountAroundNum = function(obj) {
if (!(obj instanceof BombObject) || obj.constructor !== BombObject || obj == null) {
throw new Error("the obj is not allowed.");
return 0;
}
else {
var result = 0; //对当前非雷区进行八方位踩点,计算周围雷点个数
for (var i = 0; i < me.enmbVal.length; i++) {
var _Obj = eval("obj." + me.enmbVal[i]);
//判断该方位是否存在
if (_Obj != null) {
var _X = _Obj.X;
var _Y = _Obj.Y;
//判断是不是雷
if (me.PlateNum[_X][_Y] === -1) {
//如果是雷,就加1
result++;
}
}
}
return result;
}
};

接下篇~~~

web版扫雷小游戏(二)的更多相关文章

  1. web版扫雷小游戏(一)

    作为一名程序猿,平时的爱好也不多,说起游戏,我不太喜欢大型的网游,因为太耗时间,偶尔玩玩经典的单机小游戏,比如windows下自带的游戏扫雷(秀一下,高级下最高纪录110s). 现阶段正在致力于web ...

  2. web版扫雷小游戏(四)

    ~~~接上篇,游戏的主体框架完成了,接下来我们对游戏中存在的两个主要实体进行分析,一个是雷点类BombObject(节点对象),一个是节点对象对应的图片对象BombImgObject,根据第一篇的介绍 ...

  3. web版扫雷小游戏(三)

    ~~~接上篇,上篇介绍了游戏实现过程中第一个比较繁琐的地方,现在展现在玩家面前的是一个有血有肉的棋盘,从某种意义上说玩家已经可以开始游戏了,但是不够人性化,玩家只能一个一个节点的点开,然后判断,然后标 ...

  4. jQuery实践-网页版2048小游戏

    ▓▓▓▓▓▓ 大致介绍 看了一个实现网页版2048小游戏的视频,觉得能做出自己以前喜欢玩的小游戏很有意思便自己动手试了试,真正的验证了这句话-不要以为你以为的就是你以为的,看视频时觉得看懂了,会写了, ...

  5. 扫雷小游戏PyQt5开发【附源代码】

    也没啥可介绍哒,扫雷大家都玩过. 雷的分布算法也很简单,就是在雷地图(map:二维数组)中,随机放雷,然后这个雷的8个方位(上下左右.四个对角)的数字(非雷的标记.加一后不为雷的标记)都加一. 如何判 ...

  6. 用Kotlin破解Android版微信小游戏-跳一跳

    前言 微信又更新了,从更新日志上来看,似乎只是一次不痛不痒的小更新.不过,很快就有人发现,原来微信这次搞了个大动作——在小程序里加入了小游戏.今天也是朋友圈被刷爆的缘故. 看到网上 有人弄了一个破解版 ...

  7. Angular4 扫雷小游戏

    扫雷小游戏,可以升级过关,难度随关卡增加.但是有很明显的bug,以后有时间会继续优化! HTML: <div class="mainContent"> <div ...

  8. C++扫雷小游戏(基于CMD命令行)

    这个小游戏是笔者在大一C语言课程设计的时候写的,基于命令行,为了显得漂亮一些,特别加上了彩色特效~~~ 注意:Win10系统须将命令行调为旧版命令行,否则有可能会显示乱码! 代码示例: #includ ...

  9. JavaScript版拼图小游戏

    慕课网上准备开个新的jQuery教程,花了3天空闲时间写了一个Javascript版的拼图小游戏,作为新教程配套的分析案例 拼图游戏网上有不少的实现案例了,但是此源码是我自己的实现,所以不做太多的比较 ...

随机推荐

  1. Windows Server 2008 网站访问PHP响应慢的解决方法

    公司新上了一个网站,但是在配置完PHP环境之后却发现了问题,访问HTML速度飞快,而访问PHP网页时就要卡顿1秒,响应很慢的样子,排除了带宽的因素之后,在百度上搜了一圈竟然解决了,现在将方法转载给大家 ...

  2. MVC 5 第二章 项目结构

    通过本章学习,你将了解到一个MVC 5应用程序的项目组成以及项目文件的相关信息,从而更好地架构设计出自己的项目结构. 单从MVC的字面意思我们便能够注意到M-模型, View-视图, Controll ...

  3. cocoaPods教程

    <#这个不是命令,包括尖括号#> 一.源设置 1. 查看源,终端输入:  sudo gem sources -l 2. 删除已有的源,如:  sudo gem sources --remo ...

  4. 三分钟读懂Oracle数据库容灾架之DataGuard

    Oracle数据库目前依然处于商用数据库的霸主地位. 运行在Oracle数据库上的核心业务及核心数据的安全性尤为重要. 目前市场上针对Oracle数据库常见的容灾产品大致可以分为两大类. Oracle ...

  5. MyBatis完全使用指南

    MyBatis完全使用指南 转 http://www.jianshu.com/p/1c7c7d1bba33 MyBatis MyBatis作为一个轻量的SQL映射框架,确实很简单,但是知识点挺多,实际 ...

  6. linux 设置 jdk环境变量

    1.安装jdk  tar zxvf jdk-8u112-linux-i586.tar.gz 2.设置环境变量:vi /etc/profile 输入i加入内容如下:export JAVA_HOME=/u ...

  7. OCCI处理CHAR类型字符串变量的不同

    问题背景: 一个旧应用,原先应用是用proc写的,9i的库,如今应用须要改为使用OCCI,当中有一段查询逻辑:select ... where upper(state)=upper(:1). (此处请 ...

  8. Android&Java的学习新的等

    学习资料 很久没写博客了.适逢这次Srtp项目学弟学妹们要学习Java与Android,我就总结一些学习的教材.方法,分享写材料与心得.纯个人观点,如有不周之处欢迎指出,大家共同探讨提高. *** 教 ...

  9. IDL计算儒略日

    遥感数据还有一些文章中使用数据的时候,经常使用儒略日(Julian day),即计算该天是一年中的第几天.正好有时间,就用IDL写了段儿小代码,方便使用.   ;+   ; :Author: caoz ...

  10. Sql Server 中事务(begin tran/commit tran/rollback tran)的用法

    ALTER PROCEDURE [dbo].[Proc_Test_commit1]     @result int output, --成功 1; 失败 0     @message nvarchar ...