@Satellite-109
2020-04-02T03:14:21.000000Z
字数 4448
阅读 1457
Animator
IK
MatchTarget
StateMachine
BlendTree
Animator 主要通过状态机控制人物动画状态改变
Animator 编辑器各个属性如图:
![]()
![]()
属性 说明 融合方式 Override为覆盖,Additive为附加。附加将通过内部插值运算自动融合不同层级动画 权重 权重为1时 且 融合方式为Override时,该层将覆盖底层的动画 人物遮罩 人物遮罩应用于层级部分动画显示。如图,当该层(含遮罩)动画播放时,将播放双手、头部、body的动画,而处于红色的双腿动画将不会播放出来,显示别的层级的双腿动画 IK 勾选IK即可通过局部带动整体的骨骼显示。IK通过Unity自带的函数进行代码控制 private void OnAnimatorIK(int layIndex)
以及private void OnAnimatorIK()
layIndex为层级下标。Base Layer为0,逐级递增
混合树就是将多个相似的动作混合(混合造型与时间相似的动作)
混合类型:
类型 | 属性 |
---|---|
1D | 使用一个参数进行混合,最好混合同个方向的动画,例如:向前走,向前跑 |
2D Simple Direction | 混合不同方向的动画,需要有一个位于(0,0)的动画,例如:Idle |
2D Freeform Direction | 类似上一种,但能混合同个方向的动画,同样需要原点动画 |
2D Freeform Cartesian | 最好混合动作没有明显朝向区别的动画,可以在x轴和y轴使用不同的定义 |
MatchTarget 通常用于设置人物身体的位置在规定时间内到达某个某个位置。例如,用手支撑身体时的动画,把手的位置通过MatchTarget设置到物体表面,达到更好的表现效果。与IK不同的是,存在一个变化的效果,IK直接把手放置到物体表面,不存在变化的效果。
if (anim.GetCurrentAnimatorStateInfo(0).IsName("JumpHight"))
{
anim.MatchTarget(targetLeft, Quaternion.identity, AvatarTarget.LeftHand, new MatchTargetWeightMask(Vector3.one, 0), 0.13f, 0.26f);
anim.MatchTarget(targetRight, Quaternion.identity, AvatarTarget.RightHand, new MatchTargetWeightMask(Vector3.one, 0), 0.13f, 0.26f);
}
说明:当动画位于第0层的 JumpHight 动画时,设置双手在 0.13s 到 0.26s 之间通过插值的方式移动到 targetLeft 和 targetRight 这两个目标点
IK通过把身体位置设置到某个位置,达到更好的表现效果。例如:持枪设计时,通过把双手设置到正前方,通过双手带动整个身体动画。
private void OnAnimatorIK(int layIndex)
{
if (layIndex == 1 && ik && anim.GetCurrentAnimatorStateInfo(1).IsName("IdleGrab_FrontHigh"))
{
anim.SetIKPosition(AvatarIKGoal.RightHand, ikPos.position);
anim.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
ik = false;
}
}
说明:ik 为bool值变量,增加对IK的控制,当为第一层,名为“IdleGrab_FrontHigh”动画时,把右手设置到特定位置 ikPos.position 。
详情可以看官网例子,Inverse Kinematics
动画的其他控制
Curves
Curves可以为动画添加一条曲线,并在Animator编辑器中的参数面板添加相同的参数,则在动画播放过程中,该参数由动画自动控制,我们可以通过获取该参数的值得到相应的播放进度
Event
Events则可以添加在关键帧,并在动画播放到该位置时自动调用 Object 上的 Function ,不指定 Object 则默认挂载 Animator 上的脚本
public class AnimController : MonoBehaviour
{
private int jumpUpParameter = Animator.StringToHash("JumpUp");
private int jumpDownParameter = Animator.StringToHash("JumpDown");
private int walkParameter = Animator.StringToHash("Walk");
private int turnParameter = Animator.StringToHash("Turn");
private int colliderParameter = Animator.StringToHash("Collider");
private int hookParameter = Animator.StringToHash("Hook");
private int pickParameter = Animator.StringToHash("Pick");
private Animator anim;
private bool ik =false;
[HideInInspector]
public Vector3 targetLeft, targetRight;
public Transform ikPos;
public Transform t;
// Use this for initialization
void Start ()
{
anim = this.GetComponent<Animator>();
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.W))
{
anim.SetBool(walkParameter, true);
}
if (Input.GetKeyUp(KeyCode.W))
{
anim.SetBool(walkParameter, false);
}
float turn = Input.GetAxis("Horizontal");
anim.SetFloat(turnParameter, turn * 54f);
if (Input.GetKeyDown(KeyCode.K))
{
//当前处于Idle时(Turn 等于 0)
if (anim.GetFloat(turnParameter) == 0)
{
//跳起
RaycastHit hitInfo;
if (Physics.Raycast(transform.position + GetComponentInChildren<SkinnedMeshRenderer>().bounds.size.y * Vector3.up,
transform.forward, out hitInfo, 1))
{
if (hitInfo.collider.tag == "Wall")
{
targetLeft = hitInfo.point + (hitInfo.collider.gameObject.GetComponent<Renderer>().bounds.size.y - GetComponentInChildren<SkinnedMeshRenderer>().bounds.size.y) * Vector3.up + (0.2f * Vector3.left);
targetRight = hitInfo.point + (hitInfo.collider.gameObject.GetComponent<Renderer>().bounds.size.y - GetComponentInChildren<SkinnedMeshRenderer>().bounds.size.y) * Vector3.up + (0.2f * Vector3.right);
t.position = targetLeft;
anim.SetTrigger(jumpUpParameter);
}
}
}
}
CharacterControllerEnable();
if (anim.GetCurrentAnimatorStateInfo(0).IsName("JumpHight"))
{
anim.MatchTarget(targetLeft, Quaternion.identity, AvatarTarget.LeftHand, new MatchTargetWeightMask(Vector3.one, 0), 0.13f, 0.26f);
anim.MatchTarget(targetRight, Quaternion.identity, AvatarTarget.RightHand, new MatchTargetWeightMask(Vector3.one, 0), 0.13f, 0.26f);
}
Attack();
}
private void Attack()
{
if (Input.GetKeyDown(KeyCode.J))
{
anim.SetTrigger(hookParameter);
}
if (Input.GetKeyDown(KeyCode.I))
{
anim.SetTrigger(pickParameter);
}
}
private void OnAnimatorIK(int layIndex)
{
if (layIndex == 1 && ik && anim.GetCurrentAnimatorStateInfo(1).IsName("IdleGrab_FrontHigh"))
{
anim.SetIKPosition(AvatarIKGoal.RightHand, ikPos.position);
anim.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
ik = false;
}
}
public void SetRightHandPos()
{
ik = true;
}
private void CharacterControllerEnable()
{
if (anim.GetFloat(colliderParameter) > 0.5)
{
anim.GetComponent<CharacterController>().enabled = false;
}
if (anim.GetFloat(colliderParameter) < 0.5)
{
anim.GetComponent<CharacterController>().enabled = true;
}
}
}