JS開發HTML5游戲《神奇的六邊形》(一)


 

近期出現一款魔性的消除類HTML5游戲《神奇的六邊形》,今天我們一起來看看如何通過開源免費的青瓷引擎(www.zuoyouxi.com)來實現這款游戲。

 (點擊圖片可進入游戲體驗)

因內容太多,為方便大家閱讀,所以分成四部分來講解。

本文為第一部分,主要包括:

1. 功能分析

2. 創建工程與場景

3. 玩家分數管理

4. 棋盤設計與實現

5. 屏幕布局

若要一次性查看所有文檔,也可點擊這里

 

功能分析

首先分析游戲的功能點、算法和數據,然后依此制訂代碼組織結構。如下圖:

主要功能點

  1. 棋盤的數據結構與繪制
  2. 3個形狀的生成
  3. 形狀拖拽填入棋盤
  4. 行消除判定與死亡判定
  5. 各種表現,例如消除動畫、加分動畫等

代碼結構

將游戲邏輯(例如棋盤數據結構、死亡判定等)和界面邏輯分開,分別置於logic和ui界面。所有的UI界面交給UIManager腳本統一維護管理。

二. 創建工程與場景

創建工程Tetris和空的主場景Main,設置如下:

本工程中,畫布背景(background)設置為透明

游戲入口與游戲初始化

在Scripts目錄下創建文件:Tetris.js。代碼如下:

 1  /**
 2      * 游戲入口
 3      */
 4     window.Tetris = qc.Tetris = {
 5         // 所有的操作指令集合
 6         operation: {}
 7     };
 8 
 9     // 游戲邏輯初始化
10     qc.initGame = function(game) {
11         // 將游戲實例記錄下來,便於訪問
12         Tetris.game = game;
13 
14         // 幀率顯示為60幀(滿幀)
15         game.time.frameRate = 60;
16     };

 

 設置此腳本為入口腳本:


此腳本首先定義了名字空間,將全局的數據都記錄在qc.Tetris。
游戲入口中,記錄了game的實例並將幀率限定為60幀(默認在手機下為30幀)


三. 玩家分數管理

  1. 創建腳本:Scripts/logic/Score.js:

     1  /**
     2   * 維護分數信息
     3   */
     4  var Score = qc.Tetris.Score = function() {
     5      var self = this;
     6      self._current = 0;
     7      self._best = 0;
     8 
     9      // 將本地數據讀取出來
    10      var game = qc.Tetris.game;
    11      var current = game.storage.get('current'),
    12          best = game.storage.get('best');
    13      if (current) self._current = current;    
    14      if (best) self._best = best;
    15  };
    16  Score.prototype = {};
    17  Score.prototype.constructor = Score;
    18 
    19  Object.defineProperties(Score.prototype, {
    20      current: {
    21          get: function() { return this._current; },
    22          set: function(v) {
    23              this._current = v;
    24              if (this.best < v) this.best = v;
    25          }
    26      },
    27 
    28      best: {
    29          get: function() { return this._best; },
    30          set: function(v) {
    31              this._best = v;
    32              var storage = qc.Tetris.game.storage;
    33              storage.set('best', v);
    34              storage.save();
    35          }
    36      }
    37  });

     

    Score類維護了兩個數據:current(當前玩家的分數)、best(玩家的歷史最高分)

  2. 實例化Score類
    打開Tetris.js腳本,在initGame方法中,加入代碼:

     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  };

     

 

四. 棋盤設計與實現

棋盤為一邊長為5的正六變形,為了方便計算,我們如下設定棋盤的坐標系(下文稱為:格子邏輯坐標):

原點在六邊形中心點,半徑為4。

  1. 修改Tetris.js文件,增加棋盤的配置信息:

     1  window.Tetris = qc.Tetris = {
     2      // 棋盤的大小(半徑)
     3      SIZE: 4,
     4 
     5      // 棋盤中,每個格子的寬度和高度
     6      BLOCK_W: 61,
     7      BLOCK_H: 67,
     8 
     9      // 所有的操作指令集合
    10      operation: {}
    11  };

     

    棋盤格子的大小 = 格子圖片的大小,后續導入資源后可以看到其大小為61*67。

  2. 在Scripts/logic下創建文件Board.js,維護棋盤的數據,代碼如下:

     1  var Board = qc.Tetris.Board = function() {
     2      var self = this,
     3          size = qc.Tetris.SIZE,
     4          len = qc.Tetris.BLOCK_H;
     5 
     6      // 構建用來轉換格子坐標的矩陣
     7      var m = self.m = new qc.Matrix();
     8      m.a = len;
     9      m.c = len / 2;
    10      m.d = len * (Math.sqrt(3) / 2);
    11 
    12      // 初始化棋盤數據
    13      self.data = {};
    14      for (var i = -size; i <= size; i++) {
    15          for (var j = -size; j <= size; j++) {
    16              // 這些格子落在六邊形外,忽略掉
    17              if (i * j > 0 && Math.abs(i + j) > size) continue;
    18              if (i * j < 0 && (Math.abs(i) > size || Math.abs(j) > size)) continue;
    19 
    20              // 計算格子的坐標和對應屏幕上的偏移
    21              var pos = Tetris.makePos(i, j);
    22              var pt = self.toWorld(new qc.Point(i, j));
    23              self.data[pos] = {
    24                  value: 0,
    25                  x: pt.x,
    26                  y: pt.y
    27              };
    28          }
    29      }
    30  };
    31  Board.prototype = {};
    32  Board.prototype.constructor = Board;
    33 
    34  Object.defineProperties(Board.prototype, {
    35      /**
    36       * @property {boolean} die - 當前是否已經死亡了
    37       * @readonly
    38       */ 
    39      die: {
    40          get: function() {
    41              // TODO: 等待實現
    42          }
    43      }
    44  });
    45 
    46  /**
    47   * 清空棋盤
    48   */
    49  Board.prototype.clear = function() {
    50      for (var pos in this.data) {
    51          this.data[pos].value = 0;
    52      }
    53  };
    54 
    55  /**
    56   * 重新開始游戲
    57   */
    58  Board.prototype.restart = function() {
    59      this.clear();
    60  };
    61 
    62  // 判定形狀可以放進來不
    63  // pos: 目標邏輯坐標
    64  // list: 形狀的信息
    65  Board.prototype.checkPutIn = function(pos, list) {
    66      // TODO: 等待實現
    67  };
    68 
    69  // 把某個形狀放進來
    70  Board.prototype.putIn = function(pos, list, value) {
    71      // TODO: 等待實現
    72  };
    73 
    74  // 根據格子的邏輯坐標,算出所在的屏幕坐標
    75  // distance: 兩個格子中心點之間的距離
    76  Board.prototype.toWorld = function(p, distance) {
    77      if (!distance)
    78          return this.m.apply(p);
    79 
    80      var m = new qc.Matrix();
    81      m.a = distance;
    82      m.c = distance * 0.5;
    83      m.d = distance * (Math.sqrt(3) * 0.5);
    84      return m.apply(p);
    85  };
    86 
    87  // 根據格子的屏幕坐標,反算格子的邏輯坐標
    88  Board.prototype.toLocal = function(p) {
    89      return this.m.applyInverse(p);
    90  };

     

  3. 修改Tetris.js,在qc.initGame方法中,實例化本對象:

     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  };

     

    同時,在本文件中實現兩個函數:makePos和readPos:

     1  // 構建坐標
     2  window.Tetris.makePos = function(x, y) {
     3      return x + '_' + y;
     4  };
     5 
     6  // 獲取坐標
     7  window.Tetris.readPos = function(pos) {
     8      var arr = pos.split('_');
     9      return new qc.Point(arr[0]*1, arr[1]*1);
    10  };

     

五. 屏幕布局

在美術設計時,以640*960分辨率(iPhone4)進行設計,其他分辨率的屏幕需要自適應。如下圖:




  • 整個界面分為標題欄(Top)、棋盤(Board)、3個形狀(Shape)
    • Top:高度在iPhone4上為130。這里有兩個信息:當前分數與歷史最高分數
    • Board:棋盤,其大小為600*580
    • Shape:3個形狀,大小為600*230,距離底部20
  • 自適應方案:
    • 以 640*960為基准,等比縮放,確保所有內容都能全部顯示
    • 當分辨率比較瘦長時(即Height/Width > 960/640)時,Board和Shape保持和底部位置不變(方便單手操作)。Top高度自動增加
    • 當分辨率比較寬時(即Height/Width < 960/640)時,Board和Shape保持居中,兩邊留白

導入資源

  1. 新建文件夾:Assets/atlas/ui@atlas,將以下文件拖入並打包圖集(圖片請在示例工程中查看)
    blue.png、cyan.png、gray.png、green.png、lightyellow.png、red.png、shadow.png、white.png、yellow.png
    darkblue.png、darkcyan.png、darkgreen.png、darklightyellow.png、darkred.png、darkyellow.png等,具體請參考示例工程
    格子在沒有數據時,顯示gray.png。其他形狀的格子顏色,有6種(blue、cyan、green、lightyellow、yellow、red)

  2. 將以下文件拖入文件夾Assets/raw(raw目錄下的資源都不會被打包,例如圖片直接原樣保留,適用於css樣式表指定資源)
    blue.png、cyan.png、gray.png、green.png、lightyellow.png、red.png、yellow.png等,具體請參考示例工程,各圖片的用途在后續中會說明。

界面布局

      1. 創建UIRoot,並設置Reference Resolution(參考分辨率)為 640*960,Manual Type為Expand

         

         簡單的理解:設置了以后,就可以認為屏幕的寬度>=640,高度>=960

2. 創建棋盤。棋盤大部分情況下是“靜態”的,只是在有新的形狀放入時才會變化。如果棋盤的每個格子作為UIImage進行貼圖,則每幀都需要重繪幾十個格子圖片,對渲染效率會有所影響。這里我們適用DOM方案,里面每個格子使用div進行繪制。因此創建一個DOM節點,設置其大小為:600*580,同時由於棋盤距離底部的位置固定,因此在布局上:水平居中、垂直距離底部250,自身中心點在底部中心位置。如下圖:

3. 添加一個Node節點,掛載3個形狀。Node大小為 600*230,距離底部20。如下圖:

4. 創建DOM節點顯示歷史最高分(不常變化,因此不用UIText,使用Dom更高效)。本節點大小為200*60,距離屏幕右邊20,頂部20:

5. 創建DOM節點顯示當前分(不常變化,因此不用UIText,使用Dom更高效)。本節點大小為200*80,水平居中,頂部頂部29:

 

下一篇:JS開發HTML5游戲《神奇的六邊形》(二)


免責聲明!

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



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