Unity之IK绑定及动画曲线解析
发表于2018-02-28
(一)IK控制器简介
只有勾选IK pass选项,unity才会去调用OnAnimatorIK函数。也只有调用了这个函数,unity才会进行相应的IK绑定。

当动作需要跟外界对象发生交互的时候,我们就需要对人物进行IK绑定。
1、IK(Inverse Kinematics)即反向动力学,即可以使用场景中的各种物体来控制和影响角色身体部位的运动,一般来说骨骼动画都是传统的从父节点到子节点的带动方式(即正向动力学),而IK则倒过来,由骨骼子节点带动骨骼父节点,具体情况比如人物走路踩到了石头就需要由脚的子节点来带动全身骨骼做出踩到石头的响应。IK可以使人物和场景更加贴合,从而达到更加真实的游戏效果,如果大家玩过《波斯王子》或《刺客信条》系列,应该对主角的攀爬和飞檐走壁的能力印象深刻,这些都是应用了IK,使动画贴合到具体的场景中进行的表现。--来自互联网

a.在Layers面板下右上角的齿轮中,选择IK。
b.编写IK控制代码。
我们要引用IK,unity提供一个void OnAnimatorIK(int index)回调函数,和Start,Update是同级关系。既然我们的人物被设置了IK属性,那么跟谁产生IK交互呢?且void OnAnimatorIK(int index)方法中需要获得position,rotation等参数,则必须声明一个Transform类型的变量,来存储交互对象的transform属性值。可以声明一个public的transform,然后将其他物体拖拽到上面去,也可以是私有的,然后用Find查找。
using UnityEngine; using System.Collections; public class PeopleMove : MonoBehaviour { private Transform IKTarget; public Animator m_walk; void Start () { m_walk = gameObject.GetComponent<Animator>(); IKTarget = GameObject.Find("IKTarget").transform; } void OnAnimatorIK(int index) { m_walk.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1);//枚举设置权重,AvatarIKGoal.LeftHand是IK手柄 m_walk.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1); m_walk.SetIKPosition(AvatarIKGoal.LeftHand, IKTarget.position);//绑定位置IK,就是跟谁绑定在一起,第二个参数是要绑定的那个对象的position m_walk.SetIKRotation(AvatarIKGoal.LeftHand,IKTarget.rotation);//设置位置的权重,和旋转的权重 }
效果:手一直指向另一个对象,人旋转,手夜跟着旋转,但还是指向另一个对象
2、IK控制器实例:跳箱子,目的是在jump动画播放结尾的时候,我们让人物的脚步位置停留在箱子顶端,来模拟跳上台阶的动作。则需要用到IK绑定。

a.首先是设置动画控制器组件:

从Idle动画过渡到jump动画,我们需要在jump动画中设置curve值,来标识jump动画的各个状态。
b.在状态机中设定相应参数来控制动画。

MatchStart和end是用来存储动画片段开始时间和结束时间。jump当然是控制jump动画播放与否,bool类型。
c.脚本控制:
using UnityEngine; using System.Collections; public class MTarget : MonoBehaviour { private Animator ani; public Transform RightHand;//右手的位置,球体赋值 bool hasJump=false; void Start () { ani = GetComponent<Animator>(); } void Update() { if (ani)//判断动画组件是否存在 { AnimatorStateInfo state = ani.GetCurrentAnimatorStateInfo(0);//获取当前状态信息 if(Input.GetKeyDown(KeyCode.H)) ani.SetBool("Jump",true); if(state.IsName("Base Layer.Jump"))//处于base层中的jump动画中,才能调用此MatchTarget函数,在jump动画状态时才能指定手部在某个时间点处于某个位置 { ani.SetBool("Jump", false); ani.MatchTarget(RightHand.position, RightHand.rotation, AvatarTarget.Root, new MatchTargetWeightMask(new Vector3(0, 1, 1), 0), ani.GetFloat("MatchStart"), ani.GetFloat("MatchEnd")); //第三个参数是哪个身体部位完成指定的IK绑定,最后两个参数是动画片段中指定的动画曲线来设定匹配开始的时间与结束的时间,设置动画参数来获取动画曲线的数值。 //vector是位置匹配的权重,x,y,z三个方向上都进行匹配,转转匹配不进行。 hasJump = true; } } } }
(二)动画曲线
Curves动画曲线: 通过动画事件,我们可以在动画过程中调用某个函数实现某种特定功能,如果我们需要在动画过程中调用多个函数,则需要在动画过程多个时间点插入动画事件。如果我们需要在动画中一个连续时间段中调用一个函数,则动画事件无法实现。可以说动画事件是在动画事件线进行离散式的事件调用。如果需要进行连续式的函数调用,则需要借助动画曲线,比如需要一段动画时间播放一段声音。动画曲线是动画剪辑(Animation Clip)的一个属性, 假如,需要在如下Run动画剪辑的中间某段中播放一个声音,则可以将这一段的曲线区别于其他时间段,比如这一段的曲线值均大于0,其他时间段均小于0.
例子类似于马钻火圈:

人物带有角色控制器,那么在播放动画的时候,角色控制器与cube的碰撞器接触,则使得人物不能穿过正方形的洞,因为虽然人物是横着准备往前跃,但是角色控制器并没有改变形状,那么我们就需要使用动画曲线,来控制动画播放的某个时刻(身体横着的时候),来修改角色控制器形状(变小,从而不与cube碰撞,人物也就跃过孔)。
a.动画控制器设置:

jump参数用来控制jump动画播放与否。ColliderHeight用来获取动画某个时刻的值,传递给相关函数使用。
b.给人物添加角色控制器。
c.对jump动画文件设置动画曲线参数:

第一个参数命名得和状态机中参数一致,来达到获取的效果。
d.编写相应脚本:
using UnityEngine; using System.Collections; public class Dive : MonoBehaviour { private Animator ani; private CharacterController chara; private float characterheight; public bool applyCurve = false; public float timeScale = .5f; bool preventJump = false; bool hasJumped = false;//记录跳跃状态 void Start() { ani = GetComponent<Animator>(); chara = GetComponent<CharacterController>(); characterheight = chara.height; } void OnGUI() { bool preValue = applyCurve; applyCurve = GUILayout.Toggle(applyCurve, "applyCurve"); if(preValue!=applyCurve) { preventJump = true; } else { preventJump = false; } } void Update () { Time.timeScale = timeScale;//比平常速度播放慢,来产生慢动作的效果 if(ani) { AnimatorStateInfo state = ani.GetCurrentAnimatorStateInfo(0); if (!preventJump && Input.GetKeyDown(KeyCode.Q)) { ani.SetBool("Jump", true); } if(state.IsName("Base Layer.Jump")) { hasJumped = true; ani.SetBool("Jump", false); } if(hasJumped&&state.IsName("Base Layer.idle"))//处于idle状态,就让场景复原 { hasJumped = false; Application.LoadLevel(0); } //使用动画片段的曲线,来调节角色控制器的大小 if(applyCurve) { chara.height = characterheight * (1 + 2 * ani.GetFloat("ColliderHeight")); } } } }
测试:观察到,当按下Q键进行jump播放的时候,人物在身体横着的时候,动画控制器会收缩,使得人物跃过孔洞。
来自:http://blog.csdn.net/qq_34078945/article/details/62045365