今天實現的內容:
經過代碼運用動畫自帶的Root Motion
在這篇博客以前,攻擊時是不會出現位移的,由於咱們沒有運用動畫自帶的Root Motion。爲了更好的動畫效果,咱們接下來將會運用到Root Motion。
上圖中,紅色的標識指示了動畫的Root Motion量(具體我也不是太懂,反正就是個量),當咱們運用Root Motion時,系統會將Root Motion量套用到網絡遊戲對象的transform上,注意是Animator所在的遊戲對象,咱們的項目中,Animator被放到控制器次級的模型對象上,因此移動的只是模型。
要使用Root Motion,咱們須要設置Animator組件下的Apply Root Motion爲true,可是若是咱們直接就這麼作了,那些自己帶有Root Motion可是咱們並不想要運用的動畫也會被用上,好比說移動動畫,並且因爲咱們的模型只是控制器對象的子對象,運用動畫的Root Motion會致使模型脫離父對象的原點。爲了解決這些問題,咱們仍是須要用腳原本運用Root Motion。下面的圖片展現的就是我剛剛提到的問題。
咱們的代碼將使用OnAnimatorMove方法(MonoBehaviour.OnAnimatorMove),該方法會在動畫機算完Root Motion值以後的每一幀調用(可是仍是在IK計算以前)。在這裏咱們甚至能夠對動畫機算完以後的Root Motion作修改。爲何是算完以後?由於在算以前修改等於沒改。
此次咱們不會去修改Root Motion,咱們將利用OnAnimatorMove的系統調用時機,首先獲得動畫機計算的deltaPosition,就是動畫機計算出的模型的Root Motion的位移(Root Motion不單單是位移還有旋轉deltaRotation,可是此次咱們只要位移就行),繼續發揮傳統藝能,在OnAnimatorMove中將deltaPosition,經過發送消息傳遞給PlayerController,由PlayerController中的OnUpdateRootMotion根據deltaPosition去移動Rigidbody。畢竟Rigidbody在同一級,位移相關的全部操做仍是要交給PlayerController。這樣一來,咱們就經過腳本運用了攻擊動畫的Root Motion。segmentfault
public class RootMotionController : MonoBehaviour { private Animator anim; private void Awake() { anim = GetComponent<Animator>(); } void OnAnimatorMove() { SendMessageUpwards("OnUpdateRootMotion", (object)anim.deltaPosition); } }
能夠發現當咱們用了OnAnimatorMove之後,Apply Root Motion變成了Handled by Script。
下面是PlayerController中的OnUpdateRootMotion具體是如何運用Root Motion的。注意,咱們是在每一物理幀中才進行Rigidbody的位置修改,因此在每個動畫幀中要作的事情是累加Root Motion的位移量。網絡
// Root Motion的位移量 用於腳本運用Root Motion private Vector3 m_deltaPos; // 處理剛體的操做 private void FixedUpdate() { // 運用Root Motion 要放到修改rb.velocity之前進行 rb.position += m_deltaPos; // ... // 清零當前物理幀累積的m_deltaPos m_deltaPos = Vector3.zero; } // 經過腳本運用動畫的Root Motion // 經過RootMotionController腳本中的OnAnimatorMove調用 public void OnUpdateRootMotion(object _deltaPos) { // 當前處於attack_oneHand_C動畫纔會運用Root Motion位移 if (CheckState("attack_oneHand_C", "Attack")) { // 更新m_deltaPos爲動畫機的Root Motion 之因此用累加是由於物理幀和動畫幀不同 在物理幀的最後會將m_deltaPos清零 // 根據我那點可憐的C#基礎知識 這一步會致使拆箱 OnAnimatorMove中的那一步會致使裝箱 損耗計算資源 m_deltaPos += (Vector3)_deltaPos; } }
最後的關鍵一步,咱們要識別當前處在什麼動畫狀態,只有在攻擊時if (CheckState("attack_oneHand_C", "Attack"))才更新Root Motion,這樣作完之後,就規避了直接Apply Root Motion帶來的問題。動畫
按照以前的辦法自制位移
若是動畫沒有自帶位移,咱們就只能使用以前曲線加衝量的老辦法自制了。可是效果確定就很難有美術們作Root Motion的效果那麼好了,畢竟美術大大們在這方面仍是比咱們厲害的。url
// 在Attack層的動畫節點attack_oneHand_B更新時執行的方法 // 經過PlayerController動畫機中的attack_oneHand_B節點上掛載的FSMOnUpdate調用 public void OnAttack_oneHandBUpdate() { // 計算攻擊時的衝量 m_thrustVec = model.transform.forward * anim.GetFloat("attackOneHandAVelocity"); ; } // 在Attack層的動畫節點attack_oneHand_C更新時執行的方法 // 經過PlayerController動畫機中的attack_oneHand_C節點上掛載的FSMOnUpdate調用 public void OnAttack_oneHandCUpdate() { // 計算攻擊時的衝量 m_thrustVec = model.transform.forward * anim.GetFloat("attackOneHandAVelocity"); }