Nurture 安卓版中的嬰兒動畫是怎麼實現的?
Nurture是我們為準媽媽專門準備的app。在app首頁有一個炫(dāi)酷(méng)的嬰兒動畫。這篇blog就來聊一聊這個動畫是怎麼實現的以及我們走過哪些坑。
如果你還沒有親手體驗過這個動畫,你可以戳下面的連結下一個感受一下:
動畫格式
嬰兒肢體動畫的製作使用的是 Flash 並匯出為 LWF格式。這樣做的優點是對於設計師來說,所設計的動畫在最終的產品的效果基本上是完全一樣的。
LWF 官方提供了基於 UIKit 的渲染器,所以在 iOS 上工作完全沒問題。不過因為其並沒有直接提供 Android 的支援,所以我們在 Android 版本的開發中就遇到了坑。
嘗試過的方案
我們嘗試過下面幾種方案:
- 使用視訊檔案代替。 但是因為支援透明通道的視訊格式不多,而且轉換出來效果也不好,所以最後並沒有使用這個方案。
- 使用 Pure2D。 Pure2D 是一個純Java的遊戲引擎,而且LWF提供官方支援(因為是一個人寫的)。不過這個引擎的文件和成熟度都讓人很著急,最後不得不放棄。
- 使用 Cocos2d-x。這是一個很成熟的遊戲引擎,做過遊戲的小夥伴們應該都聽說過它的。LWF 提供官方支援,不過因為是 C++ 的引擎,所以在整合的時候需要多費一些力氣,因此我們一開始並沒有選擇這個方案。
我們最後選用的是最後一個方案。
對於上面的三個方案,它們都有這麼一個大坑:
要顯示視訊,需要用到 VideoView
;而直接繪製 OpenGL 內容則需要用到 GLSurfaceView
。他們都是 SurfaceView
的子類。因為一些原因(或許是 Android 系統的限制),SurfaceView
如果不放在整個 View Hierarchy 的最上面(通過設定 setZOrderOnTop
) 就不能夠提供一個帶透明部分的 View ,但是程式中很多地方其它 UI 控制元件是會需要把動畫遮住的。所以我們只能夠把顯示動畫的 View ,放在 View Hierarchy 的最底下。但這就帶來另外一個問題,就是使用者自定義的背景圖片就不能簡單地通過一個 ImageView
Cocos2d-x
改造
Cocos2d-x 提供了 Cocos2dxActivity
來承載整個遊戲。但是我們其實希望展示動畫的介面是一個 Fragment
而不是一個 Activity
,這樣動畫介面才不會對我們原本的程式有太大的侵入,同時也方便我們在動畫上面放上其它的UI控制元件。因此對其進行一些改造。
具體的來說,我們建立了 AnimationFragment
,裡面基本照搬了 Cocos2dxActivity
中的方法,同時把之前需要 Cocos2dxActivity
型別作為引數的改成了需要 Context
或者 Fragment
,比如將 Cocos2dxHandler
的建構函式從 Cocos2dxHandler(Cocos2dxActivity activity)
改成了 Cocos2dxHandler(Context context)
;同時移除了一些我們不需要的部分,比如和 Video, Webview,Dialog 這些功能相關的程式碼。你可以在本文後面提供的 demo 中找到這些更改後的程式碼。
執行緒安全
我們需要從 Java 中通過 JNI 告訴遊戲引擎現在需要顯示第幾周的動畫或者其他資訊。直接呼叫看起來是可以的,動畫確實也會做相應的響應,但是 Java 的 UI 圖片資源卻時不時的會消失掉。經過分析,我們認為這是因為我們在非 OpenGL 的執行緒進行了不安全的操作。
為解決這一問題,我們實現了一個簡單的訊息佇列(通過 std::function
以及 std::bind
)。我們在 JNI 呼叫時將需要進行的操作放入佇列,在遊戲每一幀的回撥中去檢查並執行。
優點 VS 缺點:
這樣的實現其實並不算是一個完美的解決方案,但是基本上解決了我們的需求。 它的優點有:
- Cocos2d-x 有完善的社群和文件,遇到問題心裡不慌。
- 可以使用 Xcode 或者 Visual Studio 開發動畫的部分。至少從模擬器的啟動速度上來說,效率是很高的。
當然缺點也很明顯:
- 由於是遊戲引擎,同一時間在介面上只能出現一個
Cocos2dxGLSurfaceView
。 同時由於Fragment
的生命週期問題,有些轉場也會顯得不自然。 - 在生產環境中如果遇到 C++ 程式碼中的 Crash 很難收集和處理。
Demo
最後,你可以克隆這個巨大的 Demo。裡面使用 LWF
和 Fragment
實現了一個簡單的嬰兒動畫,同時可以看到動畫和其它UI控制元件在一起的效果。需要在真機上執行哦~