程序员人生 网站导航

在Unity3D的Legacy动画系统中应用Root Motion

栏目:综合技术时间:2014-12-17 08:20:43

最近仔细比较了Unity3D目前版本中的两套动画系统:Legacy和Mecanim。Mecanim系统功能较之Legacy要强大很多,但是使用AnimatorController着实不方便(虽然使用AnimatorOverrideController可以免重复编辑状态机),是由于游戏逻辑层面常常要用1个状态机或类似的机制来控制角色的状态,而角色层面的状态逻辑和动画层面是没法逐一对应的,两套复杂的状态机要配合起来。。。想一想就觉得蛋疼啊!难怪很多朋友现在还在使用Legacy动画系统。Legacy动画系统其实功能也很全面了,包括Layer、过渡混合、上下身混合之类的功能完全能够胜任,而且控制起来就直接的多了。惟独Root Motion这个我很需要特性没有支持,本文就探讨1下如何在Legacy动画系统之上附加Root Motion功能,其实很简单大笑

何谓Root Motion

在不使用Root Motion的情况下,类似走、跑这样的位移控制是这样的:
  1. 请美术在导出动画时把位移去掉;
  2. 在程序代码里控制角色移动的速度,在播放动画的同时,计算其位移。
这类做法其实挺不科学的,程序控制的角色,只能当作1个质点来处理,并且大多数时候都是匀速运动,而动画中的角色的移动常常很难跟这个匹配。所以需要比较良好的计算和比较好的美术技能才能避免角色“滑步”的现象。在“跑”这类快速移动这,滑步还比较好处理,如果是慢速移动。。。。再利害的美术也心有余而力不足了。这类情况下,最好还是使用Root Motion:
  1. 美术在导出动画的时候是附带位移的;
  2. 程序把动画的每帧的位移是从动画中读取出来,再利用到角色上的,这样就可以到达动画和位移的完善匹配了。

在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



------分隔线----------------------------
------分隔线----------------------------

最新技术推荐