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游戲《神奇的六邊形》(三)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM