轉(zhuǎn)自:http://7dot9.com/?p=528

Unity3D 的 Mecanim 動(dòng)畫系統(tǒng)可以直接復(fù)用 3DS MAX 中制作的動(dòng)畫文件中的位移,這個(gè)就是通過(guò) applyRootMotion 來(lái)達(dá)成的,我們只需要在使用 Animator 控制動(dòng)畫播放的同時(shí),設(shè)置 Animator 的 applyRootMotion 字段為 True 就 OK 了。

那么怎么來(lái)利用這個(gè)特性達(dá)成我們想要的一些效果呢?這個(gè) applyRootMotion 到底指的是啥呢?

ApplyRootMotion,從字面上理解來(lái)看,是『應(yīng)用根節(jié)點(diǎn)的運(yùn)動(dòng)』,聽(tīng)起來(lái)貌似像那么一回事。可是我們可以從官方文檔上看到這樣一段話:

The Root Transform is a projection on the Y plane of the Body Transform and is computed at runtime. At every frame, a change in the Root Transform is computed. This change in transform is then applied to the Game Object to make it move.

翻譯過(guò)來(lái)的意思,應(yīng)該是這樣的:

根節(jié)點(diǎn)的運(yùn)動(dòng)變換其實(shí)就是整個(gè)物體運(yùn)動(dòng)變換通過(guò) Y 軸垂直在水平面上的一個(gè)投影。根節(jié)點(diǎn)的運(yùn)動(dòng)變換在動(dòng)畫的每一幀中都會(huì)進(jìn)行計(jì)算。計(jì)算出來(lái)的根節(jié)點(diǎn)變換結(jié)果都會(huì)應(yīng)用在播放動(dòng)畫的對(duì)象上,讓該對(duì)象按照根節(jié)點(diǎn)的運(yùn)動(dòng)變換進(jìn)行移動(dòng)。

這段話大體的意思就是,RootMotion 這個(gè)玩意就是作用于動(dòng)畫物體在 X 軸和 Z 軸上的位移的,而且這個(gè)位移是根據(jù)實(shí)際播放的動(dòng)畫中每一幀物體的位移在 X 和 Z 軸上投影計(jì)算出來(lái)的。

這個(gè)特性非常贊特別是對(duì)于某些技能動(dòng)畫,整個(gè)動(dòng)畫是有一定位移的,但是動(dòng)畫的位移是動(dòng)作設(shè)計(jì)師在設(shè)計(jì)時(shí)根據(jù)動(dòng)作需要調(diào)出來(lái)的,位移是跟動(dòng)作的幅度直接相關(guān)和匹配的。

那么在釋放技能的時(shí)候就只需要直接播放動(dòng)畫,并且應(yīng)用這個(gè) Root Motion 的特性就可以很好的完成角色在播放攻擊動(dòng)作的同時(shí)進(jìn)行移動(dòng),動(dòng)作播放完畢之后就在動(dòng)畫結(jié)束幀角色所在的位置,切換為待機(jī)動(dòng)作就 OK 了。

看起來(lái)很牛逼的樣子對(duì)不對(duì)?是的,確實(shí)很牛逼。但是還有很多事情需要我們都一一了解以后,我們才能做出我們想要的東西的。

下面我們先岔開(kāi)一下話題,好好說(shuō)說(shuō)這個(gè) Animation Import Settings 中『Animations』Tab 頁(yè)中各項(xiàng)設(shè)置的作用。

  • Import Animation,勾選這個(gè)才可以導(dǎo)入動(dòng)畫到 Unity 工程中;
  • Bake Animations,這個(gè)選項(xiàng)只在使用 Humanoid 動(dòng)畫并且使用到了 IK 特性的時(shí)候才可用;
  • Anim.Compression,這個(gè)是關(guān)于動(dòng)畫壓縮選項(xiàng)的,默認(rèn)會(huì)選擇 Keyframe Reduction 這個(gè)是『壓縮關(guān)鍵幀』,就是 Unity 會(huì)自行重采樣動(dòng)畫的關(guān)鍵幀,還有兩個(gè)選項(xiàng)『Off 和 Optimal』,一個(gè)是關(guān)閉動(dòng)畫壓縮,一個(gè)是最優(yōu)化壓縮(應(yīng)該是壓縮效率最高,動(dòng)畫效果失真度可能也較高)
  • 選擇了 Keyframe Reduction 或者 Optimal 壓縮選項(xiàng),就會(huì)有三個(gè)用于控制壓縮選項(xiàng)的系數(shù)配置, Rotation Error,Position Error 和 Scale Error,這個(gè)三個(gè)參數(shù)默認(rèn)都是 0.5,越小呢精度就越高也就是說(shuō)動(dòng)畫的失真度越小。
  • Clips,這個(gè)下面列出了這個(gè) FBX 文件下包含的所有動(dòng)畫,我們?cè)谀J(rèn)的動(dòng)畫文件基礎(chǔ)上新建和刪除動(dòng)畫片段 (Animation Clip),當(dāng)然每個(gè)動(dòng)畫片段都是可以指定起始幀和結(jié)束幀的; 以下的設(shè)置都是針對(duì)單個(gè)動(dòng)畫片段滴:
    • Loop Time,勾選這個(gè)選項(xiàng)之后,如果 Animator 處于播放這個(gè)動(dòng)畫狀態(tài)時(shí),在播放完第一遍這個(gè)動(dòng)畫片段之后,會(huì)自動(dòng)循環(huán)從起始幀再次開(kāi)始播放動(dòng)畫,如此循環(huán)往復(fù)。如果我們不勾選這個(gè)選項(xiàng),例如 Animator 一直處于播放這個(gè)動(dòng)畫的狀態(tài),那么動(dòng)畫會(huì)定格在動(dòng)畫的結(jié)束幀,直到我們通過(guò) Animator 切換這個(gè) Animator 狀態(tài)機(jī)的狀態(tài),切換到其他的動(dòng)畫;
      • Loop Pose 和 Cycle Offset,在勾選了 Loop Time 之后生效的兩個(gè)選項(xiàng),Loop Pose 用于控制動(dòng)畫循環(huán)播放時(shí),從結(jié)束幀切換到起始幀時(shí),動(dòng)畫的動(dòng)作可以無(wú)縫的銜接上,Cycly Offset 就是用于控制循環(huán)的時(shí)候起始幀偏移用的;
    • Root Transform Rotation,根節(jié)點(diǎn)的旋轉(zhuǎn)信息
      • Bake Into Pose,勾選后會(huì)將根節(jié)點(diǎn)每一幀的旋轉(zhuǎn)方向信息烘焙到動(dòng)畫的骨骼運(yùn)動(dòng)中,在整個(gè)動(dòng)畫播放的過(guò)程中,根節(jié)點(diǎn)的旋轉(zhuǎn)信息就不會(huì)在通過(guò) Root Motion 作用到播放該動(dòng)畫的 GameObject 上了,這就意味著這個(gè)動(dòng)畫播放的過(guò)程中,該物體的 Transform 中的 Rotation 值不會(huì)因?yàn)閯?dòng)畫中物體做了任何旋轉(zhuǎn)而發(fā)生改變,而是會(huì)保持一個(gè)恒定的值,和該動(dòng)畫播放之前的旋轉(zhuǎn)值保持一致;
      • Based Upon (at Start) 或者 Based Upon,根節(jié)點(diǎn)旋轉(zhuǎn)的參考基準(zhǔn),有兩個(gè)選項(xiàng)『Original 和 Root Node Rotation』這兩個(gè)分別指的是動(dòng)畫文件中指定的旋轉(zhuǎn)值和根節(jié)點(diǎn)旋轉(zhuǎn)信息,其實(shí)我更愿意將 Original 理解為動(dòng)畫中原點(diǎn)的旋轉(zhuǎn)值,因?yàn)樵谡麄€(gè)動(dòng)畫播放的過(guò)程中,所有骨骼肯定都會(huì)有旋轉(zhuǎn)和位移的變換,但是動(dòng)畫的原點(diǎn)其實(shí)一定都是確定的,這樣理解感覺(jué)更簡(jiǎn)單也更形象一些,勾選了 Bake Into Pose 之后,就會(huì)變成 Based Upon 而不勾選 Bake Into Pose 就會(huì)保持為 Based Upon (at Start),這個(gè)目前還木有理解為啥;
      • Offset,旋轉(zhuǎn)角度與參考基準(zhǔn)的偏移(以度為單位);
    • Root Transform Position(Y),根節(jié)點(diǎn)位移信息(Y 軸)
      • Bake Into Pose,勾選后會(huì)將根節(jié)點(diǎn)每一幀在垂直 Y 軸方向上的運(yùn)動(dòng)信息烘焙到動(dòng)畫的骨骼運(yùn)動(dòng)中,在整個(gè)動(dòng)畫播放的過(guò)程中,根節(jié)點(diǎn)在 Y 軸方向的所有位移信息不會(huì)通過(guò) Root Motion 作用到播放該動(dòng)畫的 GameObject 上,這就意味著我們?cè)趫?chǎng)景中看到物體在 Y 軸上有位移,例如向上或者向下移動(dòng),但是該物體的 Transform 中的 Position 信息不會(huì)發(fā)生改變,會(huì)跟動(dòng)畫播放之前的 Position 信息保持一致;
      • Based Upon 或者 Based Upon (at Start),這個(gè)貌似有點(diǎn)不一樣哦,在選中 Bake Into Pose 之后會(huì)變成 Based Upon (at Start),不勾選的時(shí)候是 Based Upon,不過(guò)這個(gè)就能理解了。不烘焙的話,那么 Root Motion 中 Y 軸的變化就依賴于選擇的『Original 或者 Root Node Position』的 Y 軸位移變化,如果選擇烘焙的話,那么就以這個(gè)動(dòng)畫的起始幀的 Y 軸作為整個(gè)動(dòng)畫 Root Motion 的 Y 軸位移,在整個(gè)動(dòng)畫播放的過(guò)程中,Y 軸的位移都是恒定不變的;
      • Offset,垂直方向上的偏移;
    • Root Transform Position(XZ),根節(jié)點(diǎn)位移信息(水平面,XZ 軸)
      • Bake Into Pose,勾選后會(huì)將根節(jié)點(diǎn)每一幀在水平面(X 和 Z 軸)方向上的運(yùn)動(dòng)信息烘焙到動(dòng)畫的骨骼運(yùn)動(dòng)中,在整個(gè)動(dòng)畫播放的過(guò)程中,根節(jié)點(diǎn)在 X 和 Z 軸方向的所有位移信息不會(huì)通過(guò) Root Motion 作用到播放該動(dòng)畫的 GameObject 上,這就意味著我們?cè)趫?chǎng)景中看到物體在水平面上移動(dòng),但是該物體的 Transform 中的 Position 信息不會(huì)發(fā)生改變,會(huì)跟動(dòng)畫播放之前的 Position 信息保持一致,假如動(dòng)畫中物體會(huì)向前移動(dòng) 3 米,我們會(huì)看到物體在整個(gè)動(dòng)畫播放過(guò)程中確實(shí)在向前移動(dòng),播放到最后一幀時(shí)確實(shí)向前移動(dòng)了 3 米,但是當(dāng)這個(gè)動(dòng)畫播放完畢之后,切換到任何其他的動(dòng)畫時(shí),物體會(huì)直接閃回這個(gè)動(dòng)畫播放前物體所在的位置,所以通常我們需要保留動(dòng)作位移的動(dòng)畫都不會(huì)勾選這個(gè)選項(xiàng)。那這個(gè)選項(xiàng)有神馬用捏?例如某些待機(jī)動(dòng)畫,我們其實(shí)希望物體只是做一個(gè)待機(jī)動(dòng)作,但是實(shí)際上不想讓物體在水平方向上有位移,這個(gè)時(shí)候就可以勾選這個(gè)選項(xiàng)了,到時(shí)候看起來(lái)物體就像是釘在水平面上了;
    • Mask,這個(gè)掩碼主要是用于控制動(dòng)畫播放過(guò)程中,各個(gè)骨骼之間的運(yùn)動(dòng)變換的
      • Definition,可以選擇從動(dòng)畫文件創(chuàng)建也可以選擇使用其他動(dòng)畫文件中已經(jīng)創(chuàng)建好的配置;
      • Transform,這個(gè)就是動(dòng)畫文件中所有骨骼的層級(jí)關(guān)系,可以選擇勾選那些需要應(yīng)用動(dòng)畫中運(yùn)動(dòng)變換的骨骼;
    • Curves,這個(gè)主要用于設(shè)置某些跟動(dòng)畫相關(guān)的參數(shù)用,例如控制整個(gè)動(dòng)畫播放過(guò)程中的速度參數(shù)之類的,在動(dòng)畫播放的過(guò)程中可以通過(guò) Animator.GetFloat(ParamName) 函數(shù)來(lái)讀取曲線的值,曲線的 X 軸為動(dòng)畫的時(shí)間軸,Y 軸為曲線的值,曲線可以通過(guò)曲線編輯器進(jìn)行增加關(guān)鍵點(diǎn),調(diào)整曲線斜率進(jìn)行編輯,讀取時(shí)默認(rèn)會(huì)根據(jù)當(dāng)前動(dòng)畫播放的進(jìn)度作為 X 軸的值進(jìn)行讀取,一個(gè)動(dòng)畫片段可以有多個(gè)曲線;
    • Events,這個(gè)是用于在動(dòng)畫播放的過(guò)程中觸發(fā)事件的,例如整個(gè)動(dòng)畫中有起跳和落地兩個(gè)事件需要在準(zhǔn)確的時(shí)間點(diǎn)觸發(fā)并通知到游戲中其他的對(duì)象,那么就可以在 Events 時(shí)間軸上新增事件通知,設(shè)置好觸發(fā)的方法名稱和參數(shù),在播放該動(dòng)畫的 GameObject 上確保有某個(gè)腳本中有與該事件通知的方法簽名一致的方法就好了,當(dāng)動(dòng)畫播放到觸發(fā)通知時(shí)間時(shí),就會(huì)向 GameObject 廣播該時(shí)間通知,腳本中方法簽名一致的方法就會(huì)被回調(diào)了,那我們就可以做我們需要做的事情了。

說(shuō)了這么多貌似跟 Root Motion 不是很相關(guān)的東西,那么究竟我們今天的主題是啥呢?肯定還是 Root Motion 這貨。主要因?yàn)閯?dòng)畫導(dǎo)入時(shí)的設(shè)置對(duì)于 Root Motion 的應(yīng)用影響非常直接,所以前面絮絮叨叨地把這個(gè)動(dòng)畫導(dǎo)入設(shè)置都羅列了一遍。

回到正題,Generic 動(dòng)畫應(yīng)用 Root Motion 有以下幾個(gè)特點(diǎn):

  1. Root Motion 僅僅作用于 GameObject 在 X 和 Z 軸上的位移變換,不影響 Y 軸上的位移。例如現(xiàn)在播放一個(gè)從地上向前空翻之后落地的動(dòng)畫,設(shè)置 Animator 的 applyRootMotion 為 True,也就是應(yīng)用 Root Motion,那么動(dòng)畫在播放過(guò)程中,物體會(huì)在水平方向和垂直方向上都按照實(shí)際動(dòng)畫的運(yùn)動(dòng)軌跡進(jìn)行運(yùn)動(dòng),如果將 applyRootMotion 設(shè)置為 False,那么我們就只能看到動(dòng)畫在原地起跳然后再落地,動(dòng)畫中原本應(yīng)有的在水平方向的位移就沒(méi)有了;
  2. Root Motion 與導(dǎo)入動(dòng)畫時(shí)設(shè)置 Root Transform Position(XZ) 是直接相關(guān)的,如果我們選擇了將 X 和 Z 軸方向上根節(jié)點(diǎn)的位移烘焙到動(dòng)畫骨骼運(yùn)動(dòng)中的話,那么動(dòng)畫播放過(guò)程中不論我們是否將 Animator 的 applyRootMotion 設(shè)置為 True 還是 False,動(dòng)畫播放過(guò)程中物體在 X 和 Z 上的移動(dòng)是一定的,因?yàn)檫@個(gè)已經(jīng)被烘焙到骨骼動(dòng)畫中,只要?jiǎng)赢嫴シ?,物體就會(huì)移動(dòng),但是在動(dòng)畫播放的過(guò)程中 GameObject 的 Position 值不會(huì)改變,在動(dòng)畫結(jié)束后我們切換到其他動(dòng)畫的時(shí)候,其他動(dòng)畫開(kāi)始播放時(shí)的 GameObject 的位置會(huì)回到這個(gè)動(dòng)畫播放前的位置,所以如果我們需要對(duì)某個(gè)動(dòng)畫應(yīng)用 Root Motion 的話,那么這個(gè)動(dòng)畫在導(dǎo)入的時(shí)候就不要烘焙其在 X 和 Z 軸方向上的 Root Transform Position,讓 Unity 自行根據(jù)動(dòng)畫中根節(jié)點(diǎn)的位移進(jìn)行位移計(jì)算 GameObject 的位置信息;
  3. 注意 Root Motion 與 Rigidbody.Velocity 屬性的關(guān)系,如果有兩個(gè)動(dòng)畫 A 和 B,播放 A 動(dòng)畫的時(shí)候,希望 A 動(dòng)畫應(yīng)用 Root Motion,而在播放 B 動(dòng)畫的時(shí)候不想應(yīng)用 Root Motion,那么就直接在切換到動(dòng)畫 B 的時(shí)候,將 Animator 的 applyRootMotion 設(shè)置為 False 就 OK 了。但是如果播放動(dòng)畫的 GameObject 帶有 Rigidbody 組件,那么需要注意一點(diǎn),在播放 A 動(dòng)畫時(shí) Rigidbody 的 Velocity 并不會(huì)在切換到 B 動(dòng)畫時(shí)清零,也就是說(shuō)如果 A 動(dòng)畫的運(yùn)動(dòng)速度較快,那么切換到 B 動(dòng)畫的時(shí)候,如果希望 B 動(dòng)畫播放的時(shí)候 GameObject 按照自己的設(shè)定軌跡運(yùn)動(dòng),就需要自行手動(dòng)在切換到 B 動(dòng)畫之前將 Rigidbody 的 Velocity 屬性清零,防止 GameObject 按照 A 動(dòng)畫的運(yùn)動(dòng)慣性繼續(xù)運(yùn)動(dòng)。這個(gè)問(wèn)題在沒(méi)有 Rigidbody 組件的 GameObject 上不會(huì)存在;

這邊再岔開(kāi)一下,說(shuō)說(shuō)這個(gè)動(dòng)畫跟 Rigidbody 之間的關(guān)系:

  1. 如果我們沒(méi)有將 Root Transform Position 的 Y 和 XZ 軸進(jìn)行烘焙的話,那么在動(dòng)畫播放的過(guò)程中,Rigidbody 將會(huì)自動(dòng)獲得動(dòng)畫中物體運(yùn)動(dòng)的速度信息,直接通過(guò) Rigidbody.Velocity 屬性就可以獲得;
  2. 如果我們將 Y 軸進(jìn)行烘焙,那么 Rigidbody.Velocity 在 Y 軸上的值將會(huì)一直為 0,對(duì)于 XZ 軸也是一樣的,如果烘焙了 XZ 軸的位移,那么整個(gè)動(dòng)畫播放過(guò)程中,Rigidbody.Velocity 在 X 和 Z 軸上的值都會(huì)為 0;
  3. 如果播放動(dòng)畫的物體沒(méi)有 Rigidbody 組件,那么動(dòng)畫的運(yùn)動(dòng)都會(huì)僅僅按照動(dòng)畫實(shí)際的位移來(lái)進(jìn)行逐幀播放,不會(huì)出現(xiàn)上文中提到的動(dòng)畫播放切換之后還存在的運(yùn)動(dòng)慣性問(wèn)題,因?yàn)槲锢硪嬉蕾囉?Rigidbody 組件,如果沒(méi)有該組件,所有動(dòng)畫的播放都只是逐幀播放動(dòng)畫,不會(huì)存在速度的概念只有移動(dòng)位移。
  4. Rigidbody 使用使用重力對(duì)于動(dòng)畫在 Y 軸上的位移沒(méi)有任何影響,不論是否對(duì) Root Transform Position 的 Y 軸進(jìn)行了烘焙。