zoukankan      html  css  js  c++  java
  • JS开发HTML5游戏《神奇的六边形》(二)

    近期出现一款魔性的消除类HTML5游戏《神奇的六边形》,今天我们一起来看看如何通过开源免费的青瓷引擎(www.zuoyouxi.com)来实现这款游戏。

    (点击图片可进入游戏体验)

    因内容太多,为方便大家阅读,所以分成部分来讲解。

    本文为第二部分,主要包括:

    6. 历史最高分显示

    7. 当前分数显示

    8. 绘制棋盘

    9. 形状池设计与实现

    10. 形状预制的实现

    若要一次性查看所有文档,也可点击这里

    六. 历史最高分显示

    对于DOM节点,其实就是个div,可以指定其样式,指定其样式表类名,也可以内嵌html元素。

    1. 在Scripts/ui下新建文件:BestScore.js,处理最高分数的显示逻辑,并将此逻辑脚本挂载到UIRoot/best节点上。

     1 /**
     2   * 绘制最高分数
     3   */
     4  var BestScore = qc.defineBehaviour('qc.tetris.BestScore', qc.Behaviour, function() {
     5      var self = this;
     6      self.runInEditor = true;
     7  }, {
     8  });
     9 
    10  /**
    11   * 初始化处理
    12   */
    13  BestScore.prototype.awake = function() {
    14      var self = this, div = self.gameObject.div;
    15      div.className = 'score_best';
    16      self.setScore(qc.Tetris.score.current);
    17  };
    18 
    19  /**
    20   * 更新最新的高分
    21   */
    22  BestScore.prototype.setScore = function(best) {
    23      this.gameObject.div.innerHTML = 'Best: ' + best;
    24  };

       本脚本可以在编辑器下运行(方便在编辑状态下查看效果),首先设置DOM的样式表类名为score_best,然后获取最高分数并显示之。

     2. 增加score_best样式表。打开Assets/css/style.css,添加样式表:

       

    1 .score_best {
    2      font-weight: 60;
    3      font-size:30px;
    4      color: #ffffff; text-align: right;
    5  }

    3.刷新下页面,查看效果:

    4. 注意:更改样式表目前需要刷新页面才能看到效果。

     

    七. 当前分数显示

    1. 在Scripts/ui下新建文件:CurrentScore.js,处理当前分的显示逻辑,并将此逻辑脚本挂载到UIRoot/score节点上。

     1 /**
     2   * 绘制当前分数
     3   */
     4  var CurrentScore = qc.defineBehaviour('qc.tetris.CurrentScore', qc.Behaviour, function() {
     5      var self = this;
     6      self.runInEditor = true;
     7  }, {
     8  });
     9 
    10  /**
    11   * 初始化处理
    12   */
    13  CurrentScore.prototype.awake = function() {
    14      var self = this, div = self.gameObject.div;
    15 
    16      div.className = 'score_current';
    17      self.setScore(qc.Tetris.score.current);
    18  };
    19 
    20  /**
    21   * 更新最新的分数
    22   */
    23  CurrentScore.prototype.setScore = function(best) {
    24      this.gameObject.div.innerHTML = '' + qc.Tetris.score.current;
    25  };

    本脚本可以在编辑器下运行(方便在编辑状态下查看效果),首先设置DOM的样式表类名为score_current,然后获取当前分数并显示之。

     

    2.增加score_current样式表。打开Assets/css/style.css,添加样式表:

    1 .score_current{
    2      color: #ffffff;
    3      font-weight: 100;
    4      font-size:50px;
    5      text-align: center;
    6  }

    3.刷新下页面,查看效果

    八. 绘制棋盘

    DOM的层次

    DOM节点可以绘制在最底层(当工程背景设置为非透明时,将不可见),也可以绘制在最上层(在所有精灵、UIImage等节点之上)。
    本棋盘放在最底层,因为棋盘上面还需要拖拽形状、显示可放入指示等。方法是:选中board节点,在Inspector中设置Pos=BACK:

    开始绘制棋盘

      1. 棋盘中的每个格子都作为一个div元素,采用绝对定位。
        格子可能表现为7种图片形式(空时为灰色,其他为形状的对应颜色),这7种图片通过不同的样式类来区分。

      2. 打开Assets/css/style.css,加入如下几个样式表:

     1 .board { width: 61px; height: 67px; position: absolute; background-repeat: no-repeat; }
     2  .board_gray {
     3      background-image: url("../raw/gray.png");
     4  }
     5  .board_blue {
     6      background-image: url("../raw/blue.png");
     7  }
     8  .board_cyan {
     9      background-image: url("../raw/cyan.png");
    10  }
    11  .board_green {
    12      background-image: url("../raw/green.png");
    13  }
    14  .board_lightyellow {
    15      background-image: url("../raw/lightyellow.png");
    16  }
    17  .board_red {
    18      background-image: url("../raw/red.png");
    19  }
    20  .board_yellow {
    21      background-image: url("../raw/yellow.png");
    22  }

          这些样式表指明了棋盘格子中的图片

    3. 编辑Tetris.js,加入IMAGES常量,指明在不同的值下面格子所使用的className

     1 window.Tetris = qc.Tetris = {
     2      // 棋盘的大小(半径)
     3      SIZE: 4,
     4 
     5      // 棋盘中,每个格子的宽度和高度
     6      BLOCK_W: 61,
     7      BLOCK_H: 67,
     8 
     9      // 所有的格子图片
    10      IMAGES: [
    11          'gray',        // 0
    12          'blue',        // 1
    13          'cyan',        // 2
    14          'green',       // 3
    15          'lightyellow', // 4
    16          'red',         // 5
    17          'yellow'       // 6
    18      ],
    19 
    20      // 所有的操作指令集合
    21      operation: {}
    22  };

    4. 在Scripts/ui下创建脚本BoardUI.js,负责棋盘的绘制逻辑:

     1 var s = qc.Serializer;
     2 
     3  /**
     4   * 管理棋盘的数据并绘制棋盘
     5   */
     6  var BoardUI = qc.defineBehaviour('qc.tetris.BoardUI', qc.Behaviour, function() {
     7      var self = this;
     8 
     9      /**
    10       * 棋盘的棋子元素
    11       */
    12      self.pieces = {};
    13 
    14      // 本脚本在编辑模式下可以运行
    15      self.runInEditor = true;
    16  }, {
    17      linePrefab: s.PREFAB
    18  });
    19 
    20  /**
    21   * 初始化处理
    22   */
    23  BoardUI.prototype.awake = function() {
    24      var self = this;
    25      self.reset();
    26 
    27      // 立刻重绘制下
    28      self.redraw();
    29 
    30      // 设置游戏画布的背景色
    31      self.gameObject.div.parentNode.style.background = '#1F1E1E';
    32 
    33      // 缓存图片,防止在图片切换过程中出现卡顿
    34      if (self.game.device.editor) return;
    35      qc.Tetris.IMAGES.forEach(function(c) {
    36          var div = document.createElement('div');
    37          div.className = 'board board_' + c;
    38          div.style.left = '-2000px';
    39          div.style.left = '-2000px';
    40          self.gameObject.div.appendChild(div);
    41      });
    42  };
    43 
    44  /**
    45   * 重绘棋盘
    46   * @private
    47   */
    48  BoardUI.prototype.redraw = function() {
    49      var self = this;
    50 
    51      // 绘制背景
    52      self.drawBackground();
    53  };
    54 
    55  /**
    56   * 绘制棋盘背景
    57   */
    58  BoardUI.prototype.drawBackground = function() {
    59      var self = this;
    60 
    61      for (var pos in self.pieces) {
    62          var div = self.pieces[pos];
    63          div.className = 'board board_' + qc.Tetris.IMAGES[qc.Tetris.board.data[pos].value];
    64      }
    65  };
    66 
    67  /**
    68   * 初始化棋盘
    69   */
    70  BoardUI.prototype.reset = function() {
    71      var self = this, o = self.gameObject;
    72 
    73      // 构建棋盘数据
    74      if (o.children.length === 0) {
    75          for (var pos in qc.Tetris.board.data) {
    76              var info = qc.Tetris.board.data[pos];
    77              var div = self.pieces[pos] = document.createElement('div');
    78              div.className = 'board board_' + qc.Tetris.IMAGES[info.value];
    79              div.style.left = Math.round(info.x + (o.width - qc.Tetris.BLOCK_W) / 2) + 'px';
    80              div.style.top = Math.round(info.y + (o.height - qc.Tetris.BLOCK_H) / 2) + 'px';
    81              o.div.appendChild(div);
    82          }
    83      }
    84      else {
    85          o.children.forEach(function(child) {
    86              self.pieces[child.name] = child;
    87          });
    88      }
    89  };

    本脚本运行在编辑模式下,这样就可以实时看到棋盘。对本脚本做一些解释:

    • 属性pieces存放了下面所有的div元素(显示格子),key为其逻辑坐标
    • 初始化时,会将底下的所有格子创建出来,并设置上合适的位置(由于在Board.js中计算坐标时,是以棋盘中心为原点;但格子div是以棋盘的左上角为原点,因此代码中做了换算)
    • redraw方法中,以格子的值获取对应的className,对应的设置到格子div就可以了
    • 工程设置的背景是为透明的,因此在初始化时,顺便将游戏背景的颜色设置为:1F1E1E(最底层div的background-color属性)
    • 另外,初始时我们把所有格子的图片都加载进来(方法是创建个屏幕内不可见的div),这样在className切换过程中,就可以直接从本地缓存读取图片,避免加载图片影响体验

    5. 将此脚本挂载到board节点上,保存场景并刷新页面,就可以看到效果了:

     

    九. 形状池设计与实现

    1. 本游戏中,总共有23种形状,每种类型的形状,其颜色是不同的。
    在Scripts/logic下创建脚本Shapes.js,负责各种形状的配置、抽取等。

     1 var Shapes = qc.Tetris.Shapes = {
     2      // 所有可能的形状
     3      tiles: [
     4          // 1个点的
     5          {
     6              value: 1,
     7              list: [[[0, 0]]]
     8          },
     9 
    10          {
    11              value: 2,
    12              list: [
    13                  [[1, -1], [0, 0], [1, 0], [0, 1]],
    14                  [[0, 0],  [1, 0], [-1, 1], [0, 1]],
    15                  [[0, 0], [1, 0], [0, 1], [1, 1]]
    16              ]
    17          }, {
    18              value: 3,
    19              list: [
    20                  [[0, -1], [0, 0], [0, 1], [0, 2]],
    21                  [[0, 0], [1, -1], [-1, 1], [-2, 2]],
    22                  [[-1, 0], [0, 0], [1, 0], [2, 0]]
    23              ]
    24          }, {
    25              value: 4,
    26              list: [
    27                  [[0, 0], [0, 1], [0, -1], [-1, 0]],
    28                  [[0, 0], [0, -1], [1, -1], [-1, 1]],
    29                  [[0, 0], [0, 1], [0, -1], [1, 0]],
    30                  [[0, 0], [1, 0], [-1, 0], [1, -1]],
    31                  [[0, 0], [1, 0], [-1, 0], [-1, 1]]
    32              ]
    33          }, {
    34              value: 5,
    35              list: [
    36                  [[0, 0], [0, 1], [0, -1], [1, -1]],
    37                  [[0, 0], [1, -1], [-1, 1], [-1, 0]],
    38                  [[0, 0], [1, -1], [-1, 1], [1, 0]],
    39                  [[0, 0], [1, 0], [-1, 0], [0, -1]],
    40                  [[0, 0], [1, 0], [-1, 0], [0, 1]]
    41              ]
    42          }, {
    43              value: 6,
    44              list: [
    45                  [[0, -1], [-1, 0], [-1, 1], [0, 1]],
    46                  [[-1, 0], [0, -1], [1, -1], [1, 0]],
    47                  [[0, -1], [1, -1], [1, 0], [0, 1]],
    48                  [[-1, 1], [0, 1], [1, 0], [1, -1]],
    49                  [[-1, 0], [-1, 1], [0, -1], [1, -1]],
    50                  [[-1, 0], [-1, 1], [0, 1], [1, 0]]
    51              ]
    52          }
    53      ],
    54 
    55      /**
    56       * 重新开始的逻辑
    57       */
    58      restart: function() {
    59          qc.Tetris.Shapes.pool = [];
    60          for (var i = 0; i < 3; i++) {
    61              qc.Tetris.Shapes.pool.push(qc.Tetris.Shapes.random());
    62          }
    63      },
    64 
    65      /**
    66       * 随机抽取一个形状
    67       */
    68      random: function() {
    69          // 先抽取分类
    70          var math = qc.Tetris.game.math;
    71          var shapes = Shapes.tiles;
    72          var shape = shapes[math.random(0, shapes.length - 1)];
    73 
    74          // 再抽子类
    75          var list = shape.list[math.random(0, shape.list.length - 1)];
    76          return {
    77              color: shape.color,
    78              value: shape.value,
    79              list: list
    80          };
    81      },
    82 
    83      /**
    84       * 当前的pool数据
    85       */
    86      pool: []
    87  };

    代码说明如下:

      • value指明了格子应该使用哪个图片
      • list包含了多个形状,形状由数组组成,每个元素指明了逻辑坐标
      • pool属性存储了当前屏幕上3个形状的数据信息
    1. 修改Tetris.js的qc.initGame方法,最后面添加3个形状的初始化逻辑:

     1  qc.initGame = function(game) {
     2      // 将游戏实例记录下来,便于访问
     3      Tetris.game = game;
     4 
     5      // 帧率显示为60帧(满帧)
     6      game.time.frameRate = 60;
     7 
     8      // 初始化分数信息
     9      Tetris.score = new qc.Tetris.Score();
    10 
    11      // 构建棋盘对象
    12      Tetris.board = new qc.Tetris.Board();
    13 
    14      // 3个形状
    15      qc.Tetris.Shapes.restart();
    16  };

    十. 形状预制的实现

    本章节我们进行形状的绘制实现,如下图:

    1. 在UIRoot/pool节点下,创建一空的Node节点,设置如下图

    
    
    • 定位在父亲的中心点
    • 大小为200*100
    • pivot设置为:(0.5, 0.5)
    • 本对象需要可以交互(需要被拖放),勾选Interactive。并设置碰撞盒类型为Rectangle(正方形),按图示设置大小(碰撞盒大小会比节点实际大小更大)

    2.在Scripts/ui创建文件BlocksUI.js

     1 /**
     2   * 绘制一个形状
     3   */
     4  var BlocksUI = qc.defineBehaviour('qc.tetris.BlocksUI', qc.Behaviour, function() {
     5      var self = this;
     6 
     7      // 格子的预置,一个形状下有多个格子
     8      self.blockPrefab = null;
     9 
    10      // 下属所有的格子
    11      self._blocks = {};
    12  }, {
    13      blockPrefab: qc.Serializer.PREFAB
    14  });
    15 
    16  Object.defineProperties(BlocksUI.prototype, {
    17      /**
    18       * 关联的数据
    19       */
    20      data: {
    21          get: function() { return this._data; },
    22          set: function(v) {
    23              this._data = v;
    24              this.redraw();
    25          }
    26      },
    27 
    28      /**
    29       * 第几个?
    30       */
    31      index: {
    32          get: function() {
    33              return this.gameObject.parent.getChildIndex(this.gameObject);
    34          }
    35      }
    36  });
    37 
    38  /**
    39   * 初始化
    40   */
    41  BlocksUI.prototype.awake = function() {
    42      // 点击时的偏移量
    43      var self = this;
    44      self.offsetY = self.game.device.desktop ? 0 : 50;
    45  };
    46 
    47  /**
    48   * 重新绘制区块
    49   */
    50  BlocksUI.prototype.redraw = function() {
    51      var self = this;
    52      var frame = qc.Tetris.IMAGES[self.data.value];
    53      self.data.list.forEach(function(pos) {
    54          var x = pos[0], y = pos[1];
    55          var block = self.game.add.clone(self.blockPrefab, self.gameObject);
    56          block.find('block').frame = frame + '.png';
    57          block.name = x + '_' + y;
    58          self._blocks[qc.Tetris.makePos(x, y)] = block;
    59      });
    60      self.reset();
    61  };
    62 
    63  /**
    64   * 重设区块大小和排列下属格子的位置
    65   */
    66  BlocksUI.prototype.reset = function() {
    67      var self = this, o = self.gameObject;
    68      for (var pos in self._blocks) {
    69          var p = qc.Tetris.readPos(pos);
    70          var pt = qc.Tetris.board.toWorld(p, qc.Tetris.POOL_DISTANCE_NORMAL);
    71          var block = self._blocks[pos];
    72          block.anchoredX = pt.x;
    73          block.anchoredY = pt.y;
    74      }
    75  };
    • 本脚本根据形状的数据,动态创建出格子并显示出来
    • blockPrefab为格子的预置,后续步骤创建
    • 形状的大小会比棋盘中显示小,因此计算格子的屏幕坐标时,指明了常量:qc.Tetris.POOL_DISTANCE_NORMAL
    • 在初始化时,设置了非PC模式下需要做偏移为50。(在手机上拖拽时方式被手指完全挡住,从而看不清形状)

    3. 修改Tetris.js,加入POOL_DISTANCE_NORMAL配置:

     1  window.Tetris = qc.Tetris = {
     2      // 棋盘的大小(半径)
     3      SIZE: 4,
     4 
     5      // 棋盘中,每个格子的宽度和高度
     6      BLOCK_W: 61,
     7      BLOCK_H: 67,
     8 
     9      // 没有点击时,格子之间的距离
    10      POOL_DISTANCE_NORMAL: 45,
    11 
    12      ...

    4.在上述Blocks节点下,创建空的节点Node,名字修改为block2,属性为:

    • 相对于父亲居中显示
    • pivot=(0.5, 0.5)

    5. 在block2节点下,创建Image节点,名字修改为shadow,属性设置如下:

    • 相对于父亲居中显示
    • pivot=(0.5, 0.5)
    • 大小为60*65
    • 使用ui图集

    6. 在block2节点下,创建Image节点,名字修改为block,属性设置如下:

          

      • 相对于父亲居中显示
      • pivot=(0.5, 0.5)
      • 大小为40*45
      • 使用ui图集
        至此,你的场景应该是:

     7. 将block2节点拖入到目录Assets/prefab,创建预置。然后将节点从场景中删除。

    8. 将BlocksUI.js挂载到Blocks节点,并设置blockPrefab的值为上步骤创建的预制。设置完成后,将此节点拖入Assets/prefab目录,创建预置。

    9. 至此,形状的预置创建完毕了

    上一篇:JS开发HTML5游戏《神奇的六边形》(一) 

    下一篇:JS开发HTML5游戏《神奇的六边形》(三)

  • 相关阅读:
    ASP.NET 中Request.QueryString 中的key
    我要爱死这个markdown 了
    WindowsPhone 8 开发 之 本地数据库应用
    java对象详解
    java内存分析
    java 成长之路
    springboot hessian
    zabbix 3.0 安装 ubuntu环境
    dubbo+zookeeper简单环境搭建
    大型网站及架构演进过程
  • 原文地址:https://www.cnblogs.com/qici/p/4956215.html
Copyright © 2011-2022 走看看