近期出現一款魔性的消除類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隱藏掉
《神奇的六邊形》 到此就分享完畢了,感謝各位的耐心閱讀,若發現問題,還請大家及時指出,幫助我們不斷完善。以后還將陸續分享其他好游戲的開發經驗,望大家繼續關注,謝謝!