您在這裡

動態漸變公式(easing equation)的製作

4 篇文章 / 0 新
最新文章
動態漸變公式(easing equation)的製作

因緣際會之下,在和半路、Faust Li、KWL 聊天的時候談到了遊戲中動態程式化的方法,這對 Flash 或擅長使用 Javascript 做動態的開發者不算新聞,但是好像很少看到人在 native language 裡使用又有拿出來討論的,所以就趁還沒忘記這件事的時候來筆記一番吧 XD

最近幾年在開發 Flash 的開發者一定用過 TweenerTweenMax 之類的 library,而後來 Javascript 夠快了以後,做網頁前端動態的開發者當然也有此需求,所以就有了各種 js porting 出爐。Flash 後來應該在 CS3 or CS4 以後(不太確定,一陣子沒用到 Flash 了)也把他自家的漸變 API 開放出來,但記得剛開始的時候評價是很爛,大家都還是繼續用 Tweener / TweenMax etc。現在的各種新出爐的遊戲開發工具的 API,也幾乎都有類似的功能(通常叫 transition API 之類的,譬如說 CoronaSDK 就有)

不過當時我們在 C++ 端開發自己的作品有這需求,所以曾經花了點時間把它 port 到 C++ 來。

當然,理想上來說,所有動態都能交給美術人員去調,或是有專門的動態編輯器,可以把動畫輸出成檔案是最好,可是總是不可能全部動態都剛好有 editor 給你用,或是動態需求剛好是很模稜兩可的地方,譬如說 UI 物件的滑進滑出,這該誰做?美術?UI 程式?如果要改,又要美術改完動態又重新交給程式?有時也碰到需要大量產生的隨機(但模式雷同)的動態效果,要如何有效率的自動化產生又方便管理、修改?

所以可以有一個程式模組來專門做 over time 的數值漸變(你想得到的數值都可以,三維XYZ、旋轉、縮放、顏色、甚至動畫影格 etc...)就很重要。

目前已知有名的漸變公式系列,應該都衍伸自 Robert Penner 這裡,最關鍵的參數就是數值起點、終點,與 delta-time(時間變量)。有了這些,就能去跑如網頁中 Visualizer 所展示的公式。

當然那頁 Visualizer 有點不夠一目了然,可以看看後來發揚光大的版本(此處為 Tweener)的公式預覽長怎樣:

我的資訊與記憶可能有點舊了,不知道後來有沒有做的更好的公式預覽表 ...

Anyway,說好的 C++ 版在此:
EasingEquations.hpp

重點在於 C++ 是個 static + strong typed language,這個 tweening library 要包裝到和 actionscript / javascript / lua 等一樣好用,當然是──幾乎不可能!最關鍵的問題就像,一維數值好處理、但是二、三維向量都要用同一套公式、程式也不要複製貼上的情況下,在 C++ 中該怎麼辦?用我們當年對 C++ template 一知半解的狀態,還是硬幹出了一套很醜(和底層 3D 引擎 couple 在一起了)但是還算堪用的模組。

Accessors.hpp
這邊我想辦法描述了通用化的 getter / setter,並設定這些 getter / setter 實際上是處理 int? float? 還是引擎所提供的 vector2d / vector3d? 當然這樣寫轉接那麼多次,效能沒辦法太好,雖然我們也沒遇過效能問題就是了。

比較大的問題還是這系統目前不只相依到引擎的型別,他也必需相依底層引擎的 Animator 實作邏輯(繼承自 Irrlicht),然後得依靠引擎的 tick 去餵 delta-time 給它:
CustomAnimator.hpp

以上的東西,透過了一些我無法三言兩語在這邊解釋的噁心包裝之後,終端程式終於可以這樣寫:

view::pSprite sp = view::Sprite::create(省略);
sp->tween<IOExpo, Pos2D>(start, end, duration, loop, callback, delay);

來描述一個動作。到很後來很後來,把這個 API binding 到 Lua 後因為實在沒辦法直接對應 C++ template 的關係,改用字串來描述使用的公式與 getter/setter:

local sp = view.new_sprite(省略)
sp:tween("IOExpo", "Pos2D", start, end, duration, loop, callback, delay)

還好這個呼叫只是「指定動作」對電腦而言,久久才做一次,真正會有效能問題的地方也當然不在這裡。在下面我們的作品 CuBeat demo 影片中,所有有在動的東西全部都是透過這樣方法讓它動的,除了玩家操作的 1p 紅色游標,與正常緩速落下的方塊(這屬於遊戲邏輯,而非視覺動態)

字彈出來、旋轉、淡入淡出、人物動畫(漸變標的為「動畫影格數」)、方塊飛來飛去 etc etc...

看下來大家也看得出全部的關鍵就是那個 easing equation 漸變公式,後面的變化會因各自使用語言、程式架構等等的不同,而有完全不一樣的因應方法,而後面提到太多東西都是相依我們自己的架構,如果還有人有興趣瞭解可以直接在 github 上翻一翻 code,但對大家的幫助應該非常有限,就不再加篇幅說明。或是可以回文詢問 ~

 

把後來在 G+ 上做的一些後續延伸討論整理起來:
https://plus.google.com/u/0/108250149397624187704/posts/iy1zpe7Q28V

當然類似的 library porting 不會僅限於 Actionscript / Javascript

Java
這套後來想起來應該看過,但是因為個人過去沒有什麼機會寫 Java,所以大概忘了。實際上非常完整強大:
http://www.aurelienribon.com/blog/projects/universal-tween-engine/
http://code.google.com/p/java-universal-tween-engine/
不過或許有些人會覺得太龐雜了,那可以看看:
https://github.com/mattdesl/slim/blob/master/slim/src/slim/easing/Easing.java

C++
雖然正文中提到的我們自己寫的 tween API 也是公開的,但畢竟與底層引擎有藕合,除非進行改寫,不然他人也很難直接利用。這套 C++ 版 tweener 風格有點囉嗦,不過簡單場合應該都還堪用:
http://libclaw.sourceforge.net/tweeners.html
而且也有進入 boost 體系的打算:
https://groups.google.com/forum/#!topic/boost-developers-archive/eS5PhphX3D0

Lua
我們的 tween API 也有包給 Lua,但實際上只是做指定的動作,本身並不在 Lua 層運行,若要增加功能或改變既有行為,就還是得改 C++,不過感謝半路找到了 Lua 版:
1) LuaTweener  
http://sourceforge.net/projects/luatweener/
2) tweener
https://github.com/EmmanuelOga/tweener

基本上既然 Actionscript 與 Javascript 中都夠快,Lua 其實應該也夠,而且有 LuaJIT 的幫忙,Pure Lua 版的 tweener 應該效能會有辦法接近 native language 量級的速度。

另一方面來說,就我們自己的經驗,最極端狀況下,每一個 frame 通常大概會有數百個物件在跑漸變動態,然而這些物件本身的 rendering 成本,遠遠超過漸變動態「一個物件 1 frame 算一次」的成本。所以實際上雖然看起來是數值運算吃重,實際上 tweener 類的 API 應該還是要以使用自由度與彈性為優先考量 ... 效能通常不是重點。

 

我有用到的技巧是在向量繪圖軟體上編輯路徑,用外掛把它的方程式輸出成程式碼,遊戲裡的物體就可以照這個路徑移動。目前這樣就夠用所以沒考慮用比較龐大而靈活的系統,不過以後或許需求增加就要研究一下。

貝茲曲線是很好用的工具,可以近似大部分的曲線,而且很多向量繪圖軟體都有支援,可利用現有軟體編輯。

x=x0*(1-t)^3 + 3x1(1-t)^2*t + 3x2(1-t)*t^2 + x3*t^3
t=0~1
(好像不能用上下標所以用比較難看的寫法,^是指乘方)

效能的話除非是彈幕遊戲上千顆子彈+特效的情況,應該不是問題

Ok,過了這麼久我終於發現之前向 littleshan@ptt 請教是否可轉貼他的 link 過來這件事,居然是隔了一整個月都忘記做。

http://electronic-blue.herokuapp.com/blog/2012/09/quick-animation-by-easing-functions/

說起來非常巧妙,很剛好都在這個時間點在討論一模一樣的問題,但是 littleshan@ptt 的文章中有詳細地解釋了漸變公式的基本構成,以及其舉例是為 Unity 主,若剛好是以 Unity 在開發遊戲的朋友可能很適合參考。

但最重要的是,littleshan 的文章中提到了一個公開的漸變公式產生器!
http://timotheegroleau.com/Flash/experiments/easing_function_generator.htm

包含 source,所以看是你想要手動把產生出的公式程式碼改成你使用的語言,或是直接把 generator port 到可以產生目標語言應該都做得到。唯一的問題大概就是產生出的公式其實運算量是比一些「基本款」高出不少的,若有大量使用的需求時最好先 benchmark 一下。