日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

骨骼动画实现秘密!闲鱼 Flutter 互动引擎告诉你

發布時間:2024/9/3 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 骨骼动画实现秘密!闲鱼 Flutter 互动引擎告诉你 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡介: 代表骨骼動畫是一種通過控制骨骼參數來實現多幀動畫的方式,區別于 GIF 的不連貫和序列幀的體積大,骨骼動畫有較好的靈活性和流暢性。目前骨骼動畫已經被大規模地在游戲和動畫中所使用,大有一種取代幀動畫的趨勢,Candy 互動引擎對骨骼動畫的支持自然是必不可少的一環。

作者|馬驍(塵蕭)
出品|阿里巴巴新零售淘系技術部

前言

代表骨骼動畫是一種通過控制骨骼參數來實現多幀動畫的方式,區別于 GIF 的不連貫和序列幀的體積大,骨骼動畫有較好的靈活性和流暢性。目前骨骼動畫已經被大規模地在游戲和動畫中所使用,大有一種取代幀動畫的趨勢,Candy 互動引擎對骨骼動畫的支持自然是必不可少的一環。

從工具入手

動畫是互動中很重要的一環,通過恰到好處的動畫形式往往可以給用戶更加新鮮的體驗。由于動畫制作是一項需要和 UED 高度合作的工作,面對 UED 無盡的參數變更,選擇一個好用的工具就變得至關重要,畢竟誰也不希望自己在開開心心的敲代碼的時候 UED 過來找你調動畫參數。參考行業目前的工具鏈體系,并從中選擇出一套適合我們的工具是比較好的選擇。

先看曾被 Flutter 官方推薦過的骨骼動畫制作工具 Flare,其優勢在于對于 Flutter 的支持較為完善。但是問題在于這套工具對于設計師來說比較陌生,而且.flr格式是一個全新的格式不夠通用,所以最終我們放棄了這個方案。

為了配合集團現有的互動工具鏈,我們把目光放到了前端領域,白鷺引擎(Egret)是一個在前端領域小有名氣的游戲引擎,其配套的工具體系包括DragonBone(骨骼動畫)、Feather(粒子動畫)等。

這套工具在互動領域已經被使用了多年,對于設計師來說比較好上手。使用這套體系最大的問題是在 Flutter 上沒有相應的實現,但是其產物的文檔非常完善,所以我們最終選擇在 Flutter 上解析并實現相應的 Runtime。

基礎知識

要進行骨骼動畫的制作必然要先對骨骼動畫本身要有一個基礎了解,骨骼動畫的詳細介紹可以參考 :

  • DragonBone 官方教程,對于骨骼的幾個關鍵概念我們還是必須先進行了解。
  • 骨架(Armature):骨架是骨骼的集合,骨架中至少包含一個骨骼。
  • 骨骼(Bone):骨骼是骨骼動畫的基本組成部分,骨骼之間存在父子關系,父親的變換會影響到孩子。一般通過骨骼的旋轉、縮放、平移等變換即可形成動畫。
  • 插槽(Slot):插槽是圖片的容器,是骨骼和圖片的橋梁。一根骨骼可以掛載多個插槽,可以視作骨骼是插槽的父節點,骨骼的變換會影響插槽。
  • 顯示對象(DisplayData):顯示對象通常為圖片。一個插槽中可以有多個顯示對象,但同時只會有一個被顯示,通過修改當前顯示的對象可以形成幀動畫。
  • 顧名思義”骨骼“就是骨骼動畫的核心部件,正是因為這種模仿生物的骨骼的設計,使得設計師可以通過調整骨骼的參數,讓角色做出豐富且自然的動作。

我們要進行骨骼動畫的渲染肯定不能脫離 Candy 游戲系統去完成,那么隨之我們的第一個問題就誕生了,骨骼動畫的核心部件“骨骼”在 Candy 中到底應該扮演一個什么樣的角色呢?

骨架渲染

問題1:每一根骨骼在 Candy 中的角色是什么?

上一篇文章中也有提到 Candy 游戲系統是由四大元素構成的:

  • Game:游戲類,負責整個游戲的管理,Scene的加載管理以及各子系統管理與調度。
  • Scene:游戲場景類,負責游戲場景中各游戲對象的管理。
  • GameObject:游戲對象類,游戲世界中游戲對象的最小單位,游戲世界中的任何物體都是GameObject。
  • Component:游戲組件類,表示游戲對象的能力屬性,比如SpriteComponent表示精靈組件,表示繪制精靈的能力。
  • 由于骨骼是包含了父子關系的樹形結構,而 GameObject 也是一個樹形結構,我們很自然地會想到每一根骨骼就是一個 GameObject 每一個插槽就是對應的 Component 。

因為在繪制時,后繪制的對象一定是覆蓋在最上層的,所以以樹狀結構進行繪制最大的問題就是——父子間的繪制的順序是一定的。如下圖的繪制順序是身體 -> 衣服 -> 披風,或者是衣服 -> 披風 -> 身體,無論是哪一種顯然都是錯誤。

我們的解決方法是將這顆樹進行拍平為列表,我們把每一個插槽(Slot)都作為了一個 GameObject ,并根據 Zorder 進行排序,那么我們最終會得到一個排好序的插槽列表,在渲染的時候根據插槽列表依次進行渲染即可。

這樣的做法會帶來一個新的問題,插槽的位置信息數據都是相對數據,在使用樹狀的結構進行渲染的時候并不是問題,但是現在拍平之后,渲染的位置該如何確定呢?

問題2:骨骼中的位置信息和最終渲染的位置信息如何對應?

因為骨骼中的參數都是相對值,這樣做的好處在于在改變父骨骼位置時,子骨骼天然就會受到父骨骼的影響變換位置。

所以其實這個問題就是如何把相對值變為絕對值,我們可以通過一些數學計算來完成這件事,具體的原理就不在此展開講解。

在 Flutter 中,通過自定義了一個 Transform 類并封裝了相應的變換函數來即可實現坐標的轉換,這樣做的好處在于可以重載相應的運算符以便做動畫的時候進行使用。

解決了上述兩個問題,我們其實就已經知道了該如何渲染一個骨架。下面這張 Candy 實現骨骼動畫的架構圖,其中分為三個部分。

  • Parser層:考慮到骨骼動畫的編輯器有很多,為了兼容市面上不同的編輯器,我們增加了一層解析層將不同編輯器生成的產物,轉化為我們預定好的相對通用的骨骼結構數據。
  • Data層:Data層是一個相對通用的骨架數據,其內部包括了骨骼數據、插槽數據、展示對象數據、動畫數據等,通過骨架數據我們可以知道最終應該渲染什么內容。由于我們第一個兼容的編輯器是Dragonbone,所以這些數據中屬性的定義大多參照了Dragonbone中的定義,這里就不將每一個屬性都展開來說了。
  • Render層:一個骨架就是一個獨立的GameObject,骨架中的每一個插槽都會對應一個子GameObject。骨架中的骨骼起到的是輔助計算渲染坐標的作用,我們通過插槽所屬的骨骼計算出渲染時要用的絕對坐標并填到相應的TransformComponent中。最后,顯示對象中的圖片使用SpriteComponent進行渲染到正確的位置上。

動畫實現

骨骼動畫其實是由每一根骨骼的多個屬性動畫復合而成的,簡單骨骼動畫針對每一根骨骼及插槽其實可以拆分為以下幾個動畫:

  • 骨骼(插槽)的位移動畫
  • 骨骼(插槽)的旋轉動畫
  • 骨骼(插槽)的縮放動畫
  • 插槽的透明度動畫

這些簡單動畫都可以歸納為補間動畫,我們只需要在游戲每一次Update的時候將對應的屬性值改變,自然就形成了動畫的效果。

那么每一個時刻的值應該是多少呢?這就需要一個插值器來告訴我們,Flutter的Animation對于插值器提供了很好的支持,回憶一下使用Animation的時候,是不是通過每一次觸發刷新了之后從Animation中取出value值來賦值到相應的地方,同理使用在這也是一樣的。

因為骨骼動畫會有很多的關鍵幀,所以這里使用了Flutter中的一種特殊的Animation——TweenSequence。TweenSequence 可以傳入一個 List>items 每一個TweenSequenceItem都可以設置一個補間動畫和相應的權重。

在保證每一個骨骼的動畫總幀數相同的情況下,可以直接使用每兩個關鍵幀之間包含的幀數作為權重,相應的前一關鍵幀幀的值則為起始值,后一幀關鍵幀的作為終止值。舉個例子:

///Transform2 為自己定義的一個數據結構,只要重載了相應的運算符,一樣可以被Animation所使用 TweenSequenceItem<Transform2> _parseTransformAnimation( TransformFrame cur, TransformFramenext, { int duration, }) { if(cur != null&& next!= null&& cur.duration != null) { finalAnimatable<Transform2> tween = Tween<Transform2>( begin: cur.transForm, end: next.transForm, ); if((cur.duration != null&& cur.duration > 0) || (duration != null&& duration > 0)) { returnTweenSequenceItem<Transform2>(tween: tween,weight:duration == null? cur.duration?.toDouble() : duration.toDouble(), ); } } returnnull; }

動畫效果

這里以閑魚幣中的撈魚小人為例子(可以通過 “閑魚首頁 -> 右上角簽到圖標” 進入閑魚幣池塘進行體驗)。

性能表現

我們使用干凈的 Demo 工程渲染多個上圖中的小人進行測試,測試機型:iPhoneXs。

骨骼數量能一定程度上也能衡量動畫的復雜度(還與變換次數相關),可以發現大于1000根骨骼時性能開始出現衰減,并隨著骨骼數量的增加逐漸明顯,在3000根骨骼以上時出現明顯卡頓。這一性能已經完全可以滿足App中內嵌中小型游戲的需求(其中內存增加問題會在后續的性能篇中進行闡述)。

現狀和展望

目前Candy已經實現了對基礎骨骼動畫、粒子動畫、屬性動畫的支持,并且已經在閑魚幣業務中落地使用,后續會應用在更多的場景之中。隨著場景的增加,我們面臨的挑戰也就越來越多。

? 動畫賦能 app
一個 App 必定不會有很多的游戲內容,我們實現的動畫如果僅在游戲場景下使用那其實落地的場景就會很有限,所以我們將 Candy 引擎中的動畫部分封裝為了Widget,使得Flutter App可以天然無縫地使用動畫。

? 更多動畫能力的支持
Lottie 是一種深受設計師以及開發同學喜愛的方式,對于 Lottie 的支持我們也已經開始進行開發,等待完成后再與大家分享。

? 從動畫升級為互動
互動是由一個個動畫組合而成的,與傳統的動畫最大的差距在于互動需要有可交互性,在用戶發生不同交互時要進行不同動畫的切換,這也就意味著我們需要有編排各個動畫之間的關系的能力。

這是一套很完整的體系,需要有相應的邏輯編排工具以及端側對于動態邏輯編排的實現,我們目前正在與集團中前端互動小組的同學合作復用前端現有的工具鏈,但是前端比起 Flutter 在動態邏輯方面有天生的優勢,所以我們希望結合閑魚團隊的 Fass 以及 Flutter-dx 去構建這套體系。

在完成之后也會有相應的文章與大家分享我們的做法和心路歷程,同時也歡迎大家和我們一起進行探討,碰撞出更多的火花。

總結

以上是生活随笔為你收集整理的骨骼动画实现秘密!闲鱼 Flutter 互动引擎告诉你的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。