在Unity3D的Legacy動畫系統中應用Root Motion
來源:程序員人生 發布時間:2014-12-17 08:20:43 閱讀次數:3930次
最近仔細比較了Unity3D目前版本中的兩套動畫系統:Legacy和Mecanim。Mecanim系統功能較之Legacy要強大很多,但是使用AnimatorController著實不方便(雖然使用AnimatorOverrideController可以免重復編輯狀態機),是由于游戲邏輯層面常常要用1個狀態機或類似的機制來控制角色的狀態,而角色層面的狀態邏輯和動畫層面是沒法逐一對應的,兩套復雜的狀態機要配合起來。。。想一想就覺得蛋疼啊!難怪很多朋友現在還在使用Legacy動畫系統。Legacy動畫系統其實功能也很全面了,包括Layer、過渡混合、上下身混合之類的功能完全能夠勝任,而且控制起來就直接的多了。惟獨Root
Motion這個我很需要特性沒有支持,本文就探討1下如何在Legacy動畫系統之上附加Root Motion功能,其實很簡單
。
何謂Root Motion
在不使用Root Motion的情況下,類似走、跑這樣的位移控制是這樣的:
- 請美術在導出動畫時把位移去掉;
- 在程序代碼里控制角色移動的速度,在播放動畫的同時,計算其位移。
這類做法其實挺不科學的,程序控制的角色,只能當作1個質點來處理,并且大多數時候都是勻速運動,而動畫中的角色的移動常常很難跟這個匹配。所以需要比較良好的計算和比較好的美術技能才能避免角色“滑步”的現象。在“跑”這類快速移動這,滑步還比較好處理,如果是慢速移動。。。。再利害的美術也心有余而力不足了。這類情況下,最好還是使用Root Motion:
- 美術在導出動畫的時候是附帶位移的;
- 程序把動畫的每幀的位移是從動畫中讀取出來,再利用到角色上的,這樣就可以到達動畫和位移的完善匹配了。
在Legacy中添加Root Motion功能
了解了Root Motion的概念以后,在Unity3D引擎中我們很簡單就能夠實現此功能了。Unity3D有1個統1的對象層次結構設計,這點非常贊,我們可以很簡單找到角色的根骨骼,然后把其中的Transform變換讀取出來,請見以下示例代碼:
//-- 計算當前幀的Root Motion
Vector3 rootPos = m_rootBone.localPosition;
m_rootMotion = rootPos - m_lastRootPos;
m_lastRootPos = rootPos;
rootPos.x = 0;
rootPos.z = 0;
m_rootMotion.y = 0;
m_rootBone.localPosition = rootPos;
請注意,我們在后續的代碼中要把m_rootMotion附加的角色對象上,所以m_rootBone的postion被reset了。
在讀取了此幀的Root Motion,在可以把它利用到當前對象之上了:
//-- Apply Root Motion
Vector3 nextPos = this.transform.position + m_rootMotion;
this.transform.position = nextPos;
另外,1個細節需要處理1下,在動畫循環的那1幀,需要特殊處理1下。好的,看1下完全的源代碼吧:
using UnityEngine;
using System.Collections;
public class ApplyRootMotion : MonoBehaviour
{
public Transform m_flagObject; // 用來測試位置的1個對象
//-- Root Motion 控制變量
Transform m_rootBone;
Vector3 m_lastRootPos;
Vector3 m_rootMotion;
int m_lastAnimTime;
void Start ()
{
//-- 從SkinnedMeshRenderer中讀取Root Bone
SkinnedMeshRenderer skinMesh = this.gameObject.GetComponentInChildren<SkinnedMeshRenderer>();
m_rootBone = skinMesh.rootBone;
//-- 變量初始化
m_rootMotion = Vector3.zero;
m_lastRootPos = m_rootBone.localPosition;
m_lastAnimTime = 0;
}
void Update ()
{
//-- Apply Root Motion
Vector3 nextPos = this.transform.position + m_rootMotion;
this.transform.position = nextPos;
//-- 測試代碼:更新測試物體的位置
Vector3 flagPos = m_flagObject.position;
flagPos.x = nextPos.x;
flagPos.z = nextPos.z;
m_flagObject.position = flagPos;
//-- 測試代碼:更新攝像機
Camera.main.transform.LookAt(this.transform);
}
void LateUpdate()
{
AnimationState animState = this.animation["walking"];
if ((int)animState.normalizedTime > m_lastAnimTime)
{
//-- 動畫循環處理
m_lastRootPos = m_rootBone.localPosition;
m_rootMotion = Vector3.zero;
}
else
{
//-- 計算當前幀的Root Motion
Vector3 rootPos = m_rootBone.localPosition;
m_rootMotion = rootPos - m_lastRootPos;
m_lastRootPos = rootPos;
rootPos.x = 0;
rootPos.z = 0;
m_rootMotion.y = 0;
m_rootBone.localPosition = rootPos;
}
m_lastAnimTime = (int)animState.normalizedTime;
}
}
最后是截圖。。。好吧,靜態圖片看不出效果,可以下載完全Demo(請使用Unity 4.6版本打開),角色移動非常平滑,毫無滑步。
請移步百度網盤:http://pan.baidu.com/s/1o6kJsIe 密碼:osoc
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈