在Unity3D的Legacy动画系统中应用Root Motion
来源:程序员人生 发布时间:2014-12-17 08:20:43 阅读次数:3794次
最近仔细比较了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
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠