zoukankan      html  css  js  c++  java
  • web版扫雷小游戏(二) Fish

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

    游戏实现

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

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

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

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

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

     

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

     

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

     一、棋盘的布局及初始化

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

     

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

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

     为了实现上述四个过程,这里为该类定义了一个初始化函数,标记为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坐标获取棋盘中对应的节点对象,后者是计算雷点某方位上节点的数值,其代码如下:

     1 //根据x、y坐标获取节点集合中节点
     2     me.CheckObjItem = function(x, y) {
     3         //验证x、y坐标
     4         if (x >= 0 && x < me.xNum && y >= 0 && y < me.yNum) {
     5             try {
     6                 //查找该点
     7                 var tempObj = me.ObjList[x * me.yNum + y];
     8                 if (tempObj.EqualsEx(x, y)) {
     9                     if (tempObj.DisplayNum === -1) {
    10                         //如果该点是雷点
    11                         return { obj: tempObj, IsIn: true };
    12                     }
    13                     else {
    14                         return { obj: tempObj, IsIn: false };
    15                     }
    16                 }
    17             }
    18             catch (e) {
    19                 //找不到该点
    20                 return { obj: null, IsIn: false };
    21             }
    22         }
    23         else {
    24             throw new Error("the input is validate.");
    25             return { obj: null, IsIn: false };
    26         }
    27     };
    28     //计算非雷点的数值并存储
    29     me.CountAroundNum = function(obj) {
    30         if (!(obj instanceof BombObject) || obj.constructor !== BombObject || obj == null) {
    31             throw new Error("the obj is not allowed.");
    32             return 0;
    33         }
    34         else {
    35             var result = 0;
    36 
    37             //对当前非雷区进行八方位踩点,计算周围雷点个数
    38             for (var i = 0; i < me.enmbVal.length; i++) {
    39                 var _Obj = eval("obj." + me.enmbVal[i]);
    40                 //判断该方位是否存在
    41                 if (_Obj != null) {
    42                     var _X = _Obj.X;
    43                     var _Y = _Obj.Y;
    44                     //判断是不是雷
    45                     if (me.PlateNum[_X][_Y] === -1) {
    46                         //如果是雷,就加1
    47                         result++;
    48                     }
    49                 }
    50             }
    51             return result;
    52         }
    53     };
    View Code

    接下篇~~~ 

  • 相关阅读:
    WPF之感触
    C# WinForm 给DataTable中指定位置添加列
    MyEclipse 8.6 download 官方下载地址
    将博客搬至CSDN
    Building Microservices with Spring Cloud
    Building Microservices with Spring Cloud
    Building Microservices with Spring Cloud
    Building Microservices with Spring Cloud
    Building Microservices with Spring Cloud
    Building Microservices with Spring Cloud
  • 原文地址:https://www.cnblogs.com/freshfish/p/3386782.html
Copyright © 2011-2022 走看看