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

游戏实现

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

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. leetcode72. Edit Distance

    Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2 ...

  2. lightoj 1031 区间dp

    题目链接: http://lightoj.com/volume_showproblem.php?problem=1031 #include<cstdio> #include<cstr ...

  3. 第十七章、程序管理与 SELinux 初探

    ---恢复内容开始--- 什么是程序 (process) 在 Linux 底下所有的命令与你能够进行的动作都与权限有关, 而系统依据UID/GID以及文件的属性相关性判定你的权限!在 Linux 系统 ...

  4. Magic of David Copperfield II(奇偶性)

    题目大意:这是一个魔术游戏,首先把你的手指放在一个左上角的格子里面,然后魔术师说你可以移动K1步,移动完之后,他会删除一些方格,并且说,你肯定不在这里,删除的方格不可以再去了,然后让你再走K2步,继续 ...

  5. Intersecting Lines - POJ 1269(判断平面上两条直线的关系)

    分析:有三种关系,共线,平行,还有相交,共线和平行都可以使用叉积来进行判断(其实和斜率一样),相交需要解方程....在纸上比划比划就出来了....   代码如下: ================== ...

  6. 通过代码来执行testng.xml

    大多数时候,我们都是通过Eclipse IDE上的操作命令来执行testng 框架下的case 运行.那如果我们不想通过这种方式,而是想通过代码调用来实现执行该怎么办?下面是我搜集的两种方式供大家参考 ...

  7. memcached与redis 对比

    一. 综述 读一个软件的源码,首先要弄懂软件是用作干什么的,那memcached和redis是干啥的?众所周知,数据一般会放在数据库中,但是查询数据会相对比较慢,特别是用户很多时,频繁的查询,需要耗费 ...

  8. return的用处

    #include "stdio.h" main() { ,c=; ;a<;a++) { c=c+a; } printf("%d",c); return ; ...

  9. c#基础语言编程-正则表达式基础

    引言 正则表达式是一种用高度抽象的字符串来描述字符串特征,进而实现对字符串的匹配.提取.替换等等.正则表达式(以下简称正则,Regex)通常不独立存在,各种编程语言和工具作为宿主语言提供对正则的支持, ...

  10. JAVA学习.java.sql.date 与java.util.date以及gettime()方法的分析

    java.sql.Date 是针对SQL语句使用的,它只包含日期而没有时间部分. java.util.Date 就是在除了SQL语句的情况下面使用. 它都有getTime方法返回毫秒数,返回的是自19 ...