webGL動畫

在作這個項目以前,我也和不少人的想法同樣以爲:H5作動畫性能不行,只能完成簡單動畫,但是事實並不是如此。因此藉此篇分享振奮下想在H5下作酷炫遊戲的人心。ios

 

體驗遊戲請長按二維碼識別:web

 

好吧,知道你懶。不想掃碼的能夠看下面的視頻:chrome

 

初識入門編程

 

什麼是骨骼動畫,本篇先簡單作下科普,其餘你們自行百度哦。json

 

比幀動畫:它比幀動畫大幅節省了資源空間,也比幀動畫對手機性能有更高的要求,WebGL下能達到最佳的展現效果。canvas

 

編輯器選擇:業界比較主流的骨骼動畫編輯器有SPINE和DragonBones(龍骨,egret白鷺公司在維護)。咱們聯繫到CP使用的SPINE編輯器比較多,而又須要一樣一個資源文件,三端公用(iOS,安卓,HTML5)。SPINE的運行庫選擇更多,因此咱們選用了SPINE編輯器,雖然都是骨骼動畫,可是他們的動畫原理是略有差異的。設計模式

 

運行庫選擇:SPINE官網上有各類語言運行庫的推薦,單js有10餘種運行庫,到底選哪一個遊戲引擎,工欲善其事必先利其器,選擇很重要。選擇恐懼症的話仍是在科學的量化的方式選擇更適合咱們項目的遊戲引擎。主要有性能,是否支持webgl,庫體積,論壇活躍度,API健全度等5個維度綜合分析後,選擇了PIXI引擎。附件會附上對比表格。PIXI的使用上比較便利,官網上有豐富的例子讓你熟悉它的使用,這裏不作詳述。api

 

深刻了解骨骼動畫原理數組

 

在新接手這個項目以前,公司km上還鮮有骨骼動畫H5的實踐,聽了IEG T4專家david分享的遊戲開發經驗,雖然沒有提到H5上的經驗,可是他建議的熟讀遊戲引擎源碼非常受用,熟讀代碼既能讓你實現功能的時候更駕輕就熟(一下就找到最優方案,而不是不停返工),也能在性能優化時有的放矢,因此就這樣,一手api,一手源碼開始了空間寵物的開發。瀏覽器

 

先讓咱們解開骨骼動畫神祕的面紗吧~純手畫給你們奉上我對於骨骼動畫的理解,這裏僅僅是SPINE編輯器下的原理,不一樣的編輯器有略微的不一樣,這裏很少餘說明。若是當時有人給我這張圖,能節省我1天工做量,嗚嗚~

 

 

圖解:

 

一、骨骼動畫中的人物是由有骨骼,插槽,附件部分組成,這三部分都是1對N的關係:

  1. 骨骼是一個樹狀結構,有個明顯的好處就是,若是動畫時要設置位移,只要設置根節點的位移,整個任務都能一塊兒位移。

  2. 附件其實人物外表的展現,主要有三種類型:圖片,蒙皮,權重蒙皮。圖片好理解,就好像是一張貼花貼着,很僵硬,就像下面圖一的那個長槍,圖二就是蒙皮,能夠定義形變,讓整個長槍動得更加天然流暢

 

圖一

圖二

 

爲何蒙皮能自由形變呢?由於它有頂點,邊緣,三角區域這三個概念,能對圖片某個區域變形,這中特性在webgl是原生支持的,但canvas2d下是引擎本身寫的,這個就是說骨骼動畫對性能要求更高就是由於這個。爲何這三個概念能自由變形呢?看下面的圖解。

只要移動了那個頂點就能拉長鼻子

 

二、骨骼動畫中的動畫,是基於時間軸的,定義某個時間點顯示那個附件,骨骼的位移和旋轉等等,如我手畫的下圖所示:

 

三、引擎播放骨骼動畫流程。

  1. 引入SPINE編輯器導出的json文件

  2. 引擎自動引入同名的atlas和png文件

  3. 解析json和atlas文件,生成spine對象

  4. 加入到容器裏面

  5. 定時器渲染,播放動做

 

 

四、其餘,主要的就上面3點了,其餘的自行了解咯。

 

空間寵物關鍵技術實現

 

一、實時換裝

換裝功能跟附件息息相關,圖片類型的附件引擎自帶換裝方式,可是蒙皮類型的附件卻沒有!

 

最簡單的方式,hack引擎從atlas讀取出來的附件信息,修改它的texture指向換裝以後texture(webgl渲染用的紋理)。再new 一遍 Spine對象,這樣雖然能實現需求,可是畫面會有閃動,體驗很差

 

因此在熟讀了一遍PIXI代碼以後,找到了更優雅的方式,pixi有一個Texture.fromCanvas的接口,能夠把一個canvas做爲一個紋理繪製,因此:把canvas代替png繪製,若是有換裝,就用canvas的clearRect擦除和drawImage覆蓋之前位置的圖片(須要注意的是旋轉這個參數)。bingo,換裝功能完成,換裝時就再也不閃動了,並且圖片類型的附件仍是使用引擎自帶換裝方式,更快!流程大體以下:

 

 

經驗教訓:實現代碼以前務必熟讀引擎源碼,理解原理流程,打通任督二脈,找到最佳實現方法。

 

二、組合動做編輯  用戶對於寵物不一樣部分的動做自由組合,造成特點動做

 

實現原理:

animation.state['setAnimationByName'|'addAnimationByName'](track, act_name, loop, delay);

 

能夠基於軌道track(int)來作動做的疊加

 

亮點:

 

  1. 實現播放動做有限次

  2. 維持一個播放隊列

 

三、快照分享

 

問題1:WebGL截圖空白

答:

  1. WebGL獲取上下文時,有一個關鍵參數:preserveDrawingBuffer,默認爲false,表示在繪圖完成後不保留繪圖緩衝區。若是設置成true,會影響性能

  2. 在定時器裏面,同步截圖,DONE!

 

問題2:毛玻璃效果怎麼實現?須要從新引入庫來解決嗎?

答:否,pixi引擎自帶支持哦,Container對象底下有個filters參數可實現毛玻璃效果

 

四、分享gif,由於咱們原本是骨骼動畫,若是要分享出去必須截圖再合成gif。

方案1:單次播放法,滿幀截取的狀況下,ios能達到要求,可是安卓上不一樣機型,不一樣機器現狀均可能截出不一樣效果的gif,差的時候只能2,3張,效果差並且不穩定

 

方案2:屢次播放截圖法,咱們一般用的定時器計數的方式比較多,可是瀏覽器每輪播放狀態沒法作到一致,因此計數截圖法不可取,因此就要用到計時的方式,所幸requestAnimationFrame裏面回調會傳過來一個time,引擎會轉化當前動做已經播放了多長時間t,咱們能夠經過t來截取。

返回:由Math.floor(t*fps)生成的index截圖組成的數組;最終的截圖數組長度 >= fps * 動畫總時長就中止截圖  最終的效果也是很讚的。

這種截圖方式損失的時間,那會不會截圖的時間過長呢,我針對截圖時間作了下統計:

 

動畫的平均長度爲1.8s,在兩個移動平臺上的截圖耗時:

iOS: 2025ms

Android: 4535ms

 

這個分享頁面須要用戶填寫心情之類的,4s之內的截圖耗時能夠接受。

 

引擎優化

 

第一次用PIXI,第一次用PIXI-SPINE。做爲一個要上線承載上億用戶的產品,開發過程,遇到了一些引擎水土不服的地方,主要有:

一、播放動畫的時候展現錯亂,臉部五官漂移

定位:是由於同一個插槽下面有多種類型的附件。

解決:看源碼時,發現引擎在定時器更新遍歷插槽時間軸的時候,在region切換到mesh類型的時候或者mesh切到region的時候,引擎沒有隱藏以前的附件。因此就會產生漂移。以後若是還要展現以前的附件只須要從新設置可見性便可。修改代碼:

if (type === spine.AttachmentType.region)        {               if (slot.currentMesh) {                slot.currentMesh.visible = false;                slot.currentMesh = undefined;                slot.currentMeshName = undefined;            } .... }
...
if (type === spine.AttachmentType.skinnedmesh || type === spine.AttachmentType.mesh || type === spine.AttachmentType.linkedmesh)        {              if (slot.currentSprite) {                slot.currentSprite.visible = false;                slot.currentSprite = undefined;                slot.currentSpriteName = undefined;            }
.... }

 

二、畫面閃動,沒法正常顯示動畫

定位:把webgl渲染強行切成canvas2d的,顯示就正常了,說明仍是webgl下某種特性兼容性的問題。

解決:把PIXI升級到v4.0.2解決。至於具體緣由這裏不作具體說明。

 

三、增長圖片超時邏輯

解決:PIXI加載圖片的邏輯就是先聲明一個img,img.onload以後再觸發loaded事件,業務再去處理依賴於這個圖片的邏輯,沒有超時邏輯,會一直等待。頁面就是不可點擊的狀態,對於一個成熟的產品必須有很高的可用性,因此必須有完善的錯誤兼容的邏輯!就在源碼里加上了超時邏輯,業務能正常執行。

 

性能調優

功能開發好不容易完了,兼容性也OK了,可是性能卻挺糟糕,crash,發熱,進入遊戲慢,性能bug單狂轟濫炸,靜下心來各個擊破,最終項目各個性能指標達標,在外網穩定運行。

 

工欲善其事必先利其器,定位性能問題,要經過工具去分析哪裏是性能瓶頸,纔能有的放矢,雖然經過chrome的性能分析工具已經能發現大部分問題,可是ios和安卓上由於實現的差別是否是存在其餘的問題,也須要測試一下才放心。這裏蒐羅了一下經常使用的性能分析工具,供你們參考。

 

 

一、Crash

緣由:頁面內存佔用太高是主要因素,什麼資源最佔用內存呢,經過Chrome Profiles面板分析便知。

 

54%的內存都消耗在動做的Timeline數據上了。用排除法分析了一下一個動做的json佔用的內存數。

每一個動做原始數據+解析成數組總共佔用390kB。每一個用戶每次用的動做有限,並不須要把完整的動做數據加載出來,因此解決Crash能夠經過一下方法

 

a. 首次只加載模型json以及必須的2個動做數據,其他的按需加載,解析完塞到寵物的動做數據裏面(這裏須要改寫源碼暴露讀取動做數據的接口)

效果:內存減小:49M減少到18M,減小了80%(Chrome上測試)。

 

b. iOS上啓用wkWebView,大幅提升Webview的穩定性。

 

二、發熱

緣由:發熱跟CPU和GPU佔用息息相關,WebView做爲一個比較高層的應用,對CPU和GPU的佔用是要比原生App的佔用高不少,這應該也是HTML 5遊戲發展的瓶頸所在。減小資源大小,將Canvas裏面固定的圖片獨立出來等等措施都收效甚微。只要WebGL在requestAnimationFrame裏面不停渲染就會發熱嚴重。

解決:降級策略看起來這裏惟一行得通的解決方案了,沒有渲染就沒有發熱。因此就要把渲染用在刀口上。損失非關鍵體驗,安卓停掉默認動做中止渲染,須要時再打開渲染,改爲隔3秒播放一次動做讓用戶感受也是在一直動。

效果:有效解決手機發熱的問題。

 

三、幀耗時-FPS

PIXI是支持自動識別瀏覽器是否支持WebGL來選擇是Canvas2D,仍是WebGL來渲染動畫,先科普下WebGL的市場佔用率吧。

 

iOS:iOS 7.1以上都是支持WebGL的。

Android:安裝了TBS的機器支持WebGL。

 

注:TBS是騰訊QQ瀏覽器研發的Android平臺WebView內核,目前普遍用於手機QQ、微信、QQ空間中。

 

由於 TBS是熱更新的,新安裝的APP沒有TBS,就不支持WebGL,而又是咱們推廣的關鍵時期。因此Canvas2D下的渲染也須要作好它的優化。

 

解決:Canvas2D下的幀耗時的優化能夠經過將Canvas2D中不變的背景獨立到DOM上,不放在Canvas裏面渲染

 

效果:優化了40%左右。

 

四、遊戲啓動速度優化

 

解決:

 

a. 不採用通常遊戲進入先loading資源,採用背景和寵物初步加載的方式。

 

效果:背景可見時間減小4s 8.4->4.2s

b. 動做JSON按需加載

 

效果:寵物可交互的時間減小了2.8s  8.4->5.6s

 

五、WebGL內存泄漏

表現:安卓上黑屏,iOS上由於用了wkWebView會從新loading。

 

定位:在排行榜頻繁切換好友的時候會必現,排行榜的設計模式是每一個好友都是獨立的,每次都銷燬上一個寵物,再添加下一個寵物,這是面向對象編程基本的思路。

 

但是這樣切換幾回後就會異常,直覺告訴我是跟WebGL或者內存有關。這裏先科普下瀏覽器的內存佔用分幾塊:

js heap和Dom內存的佔用經過chrome的profiles和timelines面板能夠看出來,可是其餘的內存佔用能夠在哪裏看呢?經過chrome的更多工具 -> 資源管理器便可!

 

有了這個強大的工具,問題定位將不是問題,其實主要是兩個問題:

 

  1. js內存上漲,切換12個好友js內存上漲了18M,定位下來增加的內存是附件mesh等等信息。

  2. GPU內存只升不降,GPU內存就是WebGL佔用的內存,若是有獨立顯卡的話他的內存佔用是和瀏覽器內存獨立的,可是手機端沒有獨顯,可能會跟瀏覽器有共享內存,這個按機型而異。可是GPU內存增加太多毫不是好事,它可能會影響瀏覽器申請更多的GPU,形成頁面黑屏,也會致使瀏覽器佔用內存過多,被原生app殺掉或者重啓。因此這裏就是頻繁申請釋放GPU內存形成的內存泄漏。

 

解決:既然已經發了問題,解決方法就應運而出啦

 

a. 同一種寵物模型對象複用,切換下一個好友的寵物就是換裝,解決js內存過分增加!

 

效果:切換12個好友內存增加了5M而已

 

b. 換裝紋理複用,不銷燬。由於換裝紋理是一個canvas,徹底能夠擦除以後再回收再利用。解決了GPU的內存泄漏。

 

效果:

之前的方案,5次切換好友以後,GPU內存直接飆升了90MB,從166MB到255MB。

 

複用紋理後,5次切換好友,GPU只上漲了30MB,第二次切換後並無上漲。從166M到199M。

 

c. 從排行榜切換回來以後,銷燬好友的寵物模型。這個是PIXI官方提供的銷燬接口。

 

pet.stage.destroy(true)

銷燬後,GPU內存恢復到最初狀態170MB左右。

 

通過上述三次改造後,內存就維護在一個比較穩定的狀態,黑屏和reload的問題就修復好了。

 

總結

 

  1. 作遊戲,須要熟悉引擎源碼以及WebGL。越熟悉越好。

  2. 項目須要的內存越多,可以運行它的終端就越少,因此必定要千方百計的定位內存佔用大頭,逐個擊破。

  3. 可使用降級策略,降級策略要能因機制宜,切忌大刀闊斧。根據機器的剩餘內存和GPU核數或者當前的FPS值來作降級策略。

相關文章
相關標籤/搜索