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


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

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

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

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

16.分數往上飄動畫

17.形狀飛入動畫

18.其他動畫表現添加

19.游戲結束界面

20. 添加LOGO

21. 渲染優化

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

十六. 分數往上飄的動畫

在表現加分時,分數會有個縮放的效果,然后往上移動並淡出。這些效果可以通過Tween Group來組合實現。

1. 在board節點下,創建UIText節點,取名為score,屬性設置如下:

  • 文本居中顯示
  • 文本顏色為白色,大小40,外加6像素描邊

2. 為score節點,添加TweenScale組件,控制縮放動畫,屬性設置如下圖(注意設置Tween Group=1):

3. 為score節點,添加TweenPosition組件,控制節點向上移動,屬性設置如下圖(注意設置Tween Group=1):

4. 為score節點,添加TweenAlpha組件,控制節點淡出,屬性設置如下圖(注意設置Tween Group=1):

5. 將score節點拖拽到Assets/prefab目錄,創建預制。然后從場景中刪除。

6. 在Scripts/ui下新建腳本:FlyScore.js

 1 /**
 2   * 分數飄出來的特效
 3   */
 4  var FlyScore = qc.defineBehaviour('qc.tetris.FlyScore', qc.Behaviour, function() {
 5      var self = this;
 6  }, {
 7      scorePrefab: qc.Serializer.PREFAB
 8  });
 9 
10  /**
11   * 開始播放冒分數動畫
12   */
13  FlyScore.prototype.play = function(pos, score) {
14      var self = this;
15 
16      var scoreOb = self.game.add.clone(self.scorePrefab, self.gameObject);
17      scoreOb.text = '' + score;
18      var tp = scoreOb.getScript('qc.TweenPosition');
19      scoreOb.anchoredX = qc.Tetris.board.data[pos].x;
20      scoreOb.anchoredY = qc.Tetris.board.data[pos].y;
21      tp.from = new qc.Point(scoreOb.x, scoreOb.y);
22      tp.to = new qc.Point(tp.from.x, tp.from.y - 80);
23      tp.resetGroupToBeginning();
24      tp.playGroupForward();
25      self.game.timer.add(600, function() {
26          scoreOb.destroy();
27      });
28  };

 

7. 將FlyScore腳本掛載到board節點,設置scorePrefab屬性為Assets/prefab/score.bin。保存場景。

 

十七. 形狀飛入動畫

1. 雙擊Assets/prefab/Blocks.bin,編輯預制

2. 選中Blocks節點,添加TweenPosition組件,屬性設置如下:

3. 保存預置

4. 打開Scripts/ui/Pool.js,添加flyIn接口,處理飛入動畫的播放

 1 /**
 2   * 播放飛入的動畫
 3   */
 4  Pool.prototype.flyIn = function(index) {
 5      var self = this, o = self.gameObject, children = o.children;
 6      var offset = o.width * (0.5 - 0.165);
 7 
 8      // 先確保位置都正確
 9      self.resize();
10 
11      if (index === 0) {
12          var o = children[0], c = o.getScript('qc.tetris.BlocksUI');
13          c.flyIn(offset);
14      }
15      if (index === 0 || index === 1) {
16          var o = children[1], c = o.getScript('qc.tetris.BlocksUI');
17          c.flyIn(offset);
18      }
19 
20      var o = children[2], c = o.getScript('qc.tetris.BlocksUI');
21      c.flyIn(offset);
22  };

 

5. 打開Scripts/ui/BlocksUI.js,添加flyIn接口,處理單個形狀飛入動畫

 1  /**
 2   * 飛入動畫
 3   */
 4  BlocksUI.prototype.flyIn = function(offset) {
 5      var self = this,
 6          tp = self.getScript('qc.TweenPosition');
 7 
 8      tp.delay = 0.5;
 9      tp.to = new qc.Point(self.gameObject.x, self.gameObject.y);
10      tp.from = new qc.Point(tp.to.x + offset, tp.to.y);
11      tp.resetToBeginning();
12      tp.playForward();
13  };

 

6. 運行游戲,查看下效果:已經可以正常游戲了不是?

 

十八. 其他動畫表現添加

目前還缺少兩個表現:加分動畫(數字跳動)、形狀回彈效果。其方法和之前講述的大致一樣,這里簡略做個說明。

加分動畫

1. 修改CurrentScore.js代碼,添加動畫播放代碼:

 1 var CurrentScore = qc.defineBehaviour('qc.tetris.CurrentScore', qc.Behaviour, function() {
 2      var self = this;
 3      self.runInEditor = true;
 4  }, {
 5  });
 6 
 7  /**
 8   * 初始化處理
 9   */
10  CurrentScore.prototype.awake = function() {
11      var self = this, div = self.gameObject.div;
12 
13      div.className = 'score_current';
14      self.setScore(qc.Tetris.score.current);
15  };
16 
17  /**
18   * 更新最新的高分
19   */
20  CurrentScore.prototype.setScore = function(best) {
21      best = best || qc.Tetris.score.current;
22 
23      // 做動畫表現,從old -> best
24      var old = this.gameObject.div.innerHTML * 1;
25      this.delta = best - old;
26      if (this.delta <= 0)
27          this.gameObject.div.innerHTML = '' + best;
28 
29      // 0.2s內需要播放完畢,計算每秒增加的數量
30      this.step = this.delta / 0.2;
31 
32      // 播放縮放動畫
33      var ts = this.getScript('qc.TweenScale');
34      ts.resetToBeginning();
35      ts.playForward();
36  };
37 
38  /**
39   * 動畫表現
40   */
41  CurrentScore.prototype.update = function() {
42      if (this.delta <= 0) {
43          // 動畫表現完畢了
44          return;
45      }
46 
47      var step = Math.round(this.step * this.game.time.deltaTime / 1000);
48      this.delta -= step;
49      var old = this.gameObject.div.innerHTML * 1 + step;
50      if (old > qc.Tetris.score.current) {
51          old = qc.Tetris.score.current;
52          this.delta = 0;
53      }
54      this.gameObject.div.innerHTML = '' + old;
55  };

 

2. 為場景節點UIRoot/score添加TweenScale組件,屬性設置如下:

形狀回彈效果

修改BlocksUI.js代碼,添加backAni方法:

 1 /**
 2      * 退回到原來位置的動畫表現
 3      */
 4     BlocksUI.prototype.backAni = function(x, y) {
 5         var self = this, o = self.gameObject,
 6             tp = self.getScript('qc.TweenPosition');
 7         if (tp.enable) return;
 8         tp.delay = 0;
 9         tp.from = new qc.Point(x, y);
10         tp.to = new qc.Point(self.gameObject.x, self.gameObject.y);
11         tp.stop();
12         tp.resetToBeginning();
13         self.gameObject.interactive = false;
14         tp.onFinished.addOnce(function() {
15             self.gameObject.interactive = true;
16             o.parent.getScript('qc.tetris.Pool').resize();
17         });
18         tp.playForward();
19     };

 在onDragEnd方法中,當無法放入棋盤的分支中,加入backAni的調用:

 1 BlocksUI.prototype.onDragEnd = function(e) {
 2         var self = this,
 3             o = self.gameObject;
 4         self.drag = false;
 5 
 6         if (self.flagBlocks.visible && self.lastPos) {
 7             // 放到這個位置中去
 8             self.drop = true;
 9             qc.Tetris.operation.putIn(self.index, self.lastPos, self.data);
10         }
11         else {
12             // !!!!!!
13             // 在這個分支中修改為如下代碼
14             var x = o.x, y = o.y;
15             self.reset();
16             o.parent.getScript('qc.tetris.Pool').resize();
17             self.backAni(x, y);
18         }
19 
20         // 顯示標記可以干掉了
21         self.flagBlocks.destroy();
22         delete self.flagBlocks;
23     };

 

   

 

十九. 游戲結束界面

游戲界面包含2個頁面:

 

這兩個頁面使用html+css元素快速搭建(DOM節點)。步驟如下:

1. 在UIRoot下創建Dom節點,取名GameOver

       

  • 居中顯示,大小為340*441
  • 縮放1.5倍
  • 設置節點可以交互(碰撞范圍非常大,這樣底部游戲所有的元素都無法接收事件了)
  • 設置className=gameover

2. 在Scripts/ui下新建腳本:GameOverUI.js

 1 /**
 2   * 游戲結束界面
 3   */
 4  var GameOverUI = qc.defineBehaviour('qc.tetris.GameOverUI', qc.Behaviour, function() {
 5      var self = this;
 6      qc.Tetris.gameOver = self;
 7      self.runInEditor = true;
 8  }, {
 9      shareClue: qc.Serializer.PREFAB
10  });
11 
12  GameOverUI.prototype.awake = function() {
13      var div = this.gameObject.div;
14      var score = qc.Tetris.score.current;
15      var percent = 40;
16 
17      this.rawHtml =
18          '<div class="gameover_title">Game Over</div>' +
19          '<div class="gameover_score">__SCORE__</div>' +
20          '<div class="gameover_pos">你擊敗了全球__PERCENT__%的玩家</div>' +
21          '<div class="gameover_desc">讓朋友們來膜拜大神吧!</div>' +
22          '<div class="gameover_share" onclick="qc.Tetris.gameOver.share()" ontouchstart="qc.Tetris.gameOver.share()">馬上告訴他們</div>' +
23          '<div class="gameover_restart" onclick="qc.Tetris.gameOver.restart()" ontouchstart="qc.Tetris.gameOver.restart()">再玩一次</div>' +
24          '<div class="gameover_act">' +
25          '    <div class="gameover_logo"></div><div class="gameover_act_desc">點擊關注送好禮</div> ' +
26          '</div>' +
27          '<div class="clear"></div>';
28      this.rawHtml = this.rawHtml.replace('__SCORE__', '' + score);
29      this.rawHtml = this.rawHtml.replace('__PERCENT__', '' + percent);
30      div.innerHTML = this.rawHtml;
31  };
32 
33  GameOverUI.prototype.onDestroy = function() {
34      delete qc.Tetris.gameOver;
35  };
36 
37  GameOverUI.prototype.share = function() {
38      // 打開share界面
39      this.game.add.clone(this.shareClue, this.gameObject.parent);
40  };
41 
42  GameOverUI.prototype.restart = function() {
43      this.gameObject.destroy();
44      qc.Tetris.operation.restart();
45  };

本界面主要通過內置的DOM來進行處理,具體不多作解釋(您需要有一定的web前端開發基礎)

1. 打開Assets/css/style.css,添加如下樣式表:

 1 /* Game Over */
 2  .gameover {
 3      text-align: center;
 4      width: 100%;
 5      font-family: arial, sans serif;
 6      background: url("../raw/bg.png") no-repeat;
 7      color: #000000;
 8  }
 9  .gameover_title {
10      font-size: 40px;
11      margin-top: 10px;
12      height: 50px;
13      text-align: center;
14  }
15  .gameover_score {
16      font-size: 90px;
17      margin-top: -15px;
18      height: 98px;
19      text-align: center;
20  }
21  .gameover_pos {
22      text-align: center; font-size: 28px;
23      height: 40px;
24  }
25  .gameover_desc {
26      text-align: center; color: #ffffff;
27      height: 30px; font-size: 20px; line-height: 100%;
28  }
29  .gameover_share {
30      background: url("../raw/btn_blue.png") center no-repeat;
31      height: 76px;
32      line-height: 76px;
33      font-size: 30px;
34      color: #ffffff;
35      text-align: center;
36  }
37  .gameover_restart {
38      background: url("../raw/btn_yellow.png") center no-repeat;
39      text-align: center; color: #ffffff;
40      height: 76px;
41      line-height: 76px;
42      font-size: 30px;
43      margin-top: 10px;
44  }
45 
46  .gameover_logo {
47      float: left;
48      background: url("../raw/logo.png") no-repeat;
49      width: 64px;
50      height: 62px;
51      margin: 8px 0px 0px 2px;
52  }
53  .gameover_act_desc {
54      color: #ffffff;
55      float: right;
56      width: 250px;
57      text-align: left;
58      height: 62px;
59      line-height: 62px;
60      margin-top: 8px;
61      font-size: 28px;
62  }
63  .clear { clear: both; }

 

 

2. GameOverUI.js腳本掛載到GameOver對象,刷新查看下效果。

3. 將GameOver節點拖拽到Assets/prefab下,創建預制。然后從場景中刪除。游戲結束界面就完成了。下面構建分享頁面

4. 在UIRoot下新建Dom節點:shareClue,參數設置如下:

 

大小設置為鋪滿整個屏幕,className=share

5.在shareClue新建Dom節點:arraw,用來顯示箭頭,定位為右上角,參數設置如下:

 

6. 在shareClue新建Dom節點:desc,用來顯示提示語內容。參數設置如下:

7. 在Scripts/ui新建文件ShareClue.js

 1 /**
 2   * 分享提示頁面
 3   */
 4  var ShareClue = qc.defineBehaviour('qc.tetris.ShareClue', qc.Behaviour, function() {
 5      var self = this;
 6      self.runInEditor = true;
 7  }, {
 8      descNode: qc.Serializer.NODE
 9  });
10 
11  /**
12   * 初始化
13   */
14  ShareClue.prototype.awake = function() {
15      this.descNode.div.innerHTML = '請點擊右上角<br/>分享給您的好友吧<br/>看下他們能取得多少分';
16  };
17 
18  /**
19   * 點擊時干掉本頁面
20   */
21  ShareClue.prototype.onClick = function() {
22      this.gameObject.destroy();
23  };

 

8. 將ShareClue腳本掛載到shareClue節點上,設置Desc Node為下面的desc節點

9. 編輯Assets/css/style.css,添加樣式表:

 

 

 1 /* 分享提示 */
 2 .share {
 3     background-color: #000000;
 4     opacity:0.5;
 5     filter:alpha(opacity=50);
 6     text-align: center; color: #ffffff;
 7 }
 8 .share_clue {
 9     background-image: url("../raw/arrows.png");
10 }
11 .share_desc {
12     color: #ffffff; font-size: 60px; text-align: center;
13     line-height: 70px;
14 }

 

10. 保存場景並刷新之,查看效果。

11. 將shareClue拖拽到Assets/prefab,創建預置,然后從場景中刪除。

12. 選中UIRoot節點,設置UIManager組件的gameOverPrefab=Assets/prefab/GameOver.bin

13. 雙擊Assets/prefab/GameOver.bin編輯預置,設置shareClue = Assets/prefab/shareClue.bin

 

14. 打開Scripts/logic/Board.js,補齊死亡邏輯:

 1 Object.defineProperties(Board.prototype, {
 2     /**
 3      * @property {boolean} die - 當前是否已經死亡了
 4      * @readonly
 5      */ 
 6     die: {
 7         get: function() {
 8             // 如果有單個點形狀的,一定死不了
 9             var pool = qc.Tetris.Shapes.pool;
10             for (var i = 0; i < pool.length; i++) {
11                 if (pool[i].list.length === 1) return false;
12             }
13 
14             // 逐一檢查,各形狀能否扔來進來
15             for (var pos in this.data) {
16                 for (var i = 0; i < pool.length; i++) {
17                     if (this.checkPutIn(pos, pool[i].list)) return false;
18                 }
19             }
20             return true;
21         }
22     }
23 });

 

15. 在Scripts/operation創建腳本Restart.js,處理重新開始游戲的邏輯:

 1 /**
 2  * 請求重新開始游戲
 3  */
 4 qc.Tetris.operation.restart = function() {
 5     var game = qc.Tetris.game,
 6         ui = game.ui;
 7 
 8     // 清空棋盤信息
 9     qc.Tetris.board.restart();
10 
11     // 當前分數清0
12     qc.Tetris.score.current = 0;
13 
14     // 3個形狀重新替換掉
15     qc.Tetris.Shapes.restart();
16 
17     // 界面初始化
18     ui.restart();
19 };

 

16. 游戲的整體邏輯已經完成,測試看看

 

二十. 添加Logo

具體請自行參考工程理解,涉及的場景節點有:UIRoot/logo和UIRoot/company
涉及的腳本有:Scripts/ui/Company.js
涉及的樣式表:

1    /* logo */
2     .logo {
3         background-image: url("../raw/qici.png");
4     }
5     .company {
6         color:#ffffff;font-size: 24px;
7     }

 

 

二十一. 渲染優化

本游戲,使用圖片比較少,並且大部分都采用DOM的方式渲染,非常高效。
唯一可以做優化的是3個形狀:每個形狀下面掛載了多個格子節點,並且大部分時間是保持不變的,因此可以將這些格子節點緩存到canvas以提升渲染效率(多耗了點內存)。步驟如下:

1. 雙擊Assets/Prefab/Blocks.bin編輯預置,在預置根節點添加內置組件:CacheAsBitmap,設置屬性如下圖:

 

這個組件的用途是將節點渲染到獨立的canvas上並緩存起來。

2. 保存預制。當內部的格子發生變化時,需要“設臟”(確保緩存能被刷新)。打開BlocksUI.js,在reset方法最后面加入:

 

 1 self.getScript('qc.CacheAsBitmap').dirty = true; 

 

添加幀率查看

在UIRoot掛載Dom節點:debug,屬性設置如下:

2. 編輯style.css,添加樣式表:

 1 .debug { color:green; font-size:18px; } 

3. 為debug掛載內置腳本:DebugViewer,刷新下頁面,就可以查看幀率情況了:

其中,FPS為游戲實際運行幀率;Total為游戲一次主循環所使用的時間;Logic為游戲邏輯損耗的時間(即Preupdate、Update等),Render為渲染時間。

4. 在最終發布時,需要將debug隱藏掉

 

    《神奇的六邊形》 到此就分享完畢了,感謝各位的耐心閱讀,若發現問題,還請大家及時指出,幫助我們不斷完善。以后還將陸續分享其他好游戲的開發經驗,望大家繼續關注,謝謝!

 

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

 

 

 

 


免責聲明!

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



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