Skip to content

IK

更新: 4/8/2026 字数: 0 字 时长: 0 分钟

Puppet(木偶)

本示例演示了如何使用 Unity 的 Inverse Kinematics(IK) 系统,让角色实时看向一个移动的目标(例如鼠标位置)。

  • 使用Animator.SetLookAtPosition()Animator.SetLookAtWeight()来控制角色的头部和眼睛注视方向。
  • Animancer 可以无缝地与 Unity 的内置 IK 系统结合使用。
  • 本示例还展示了如何在播放普通动画的同时应用 IK。

开启 IK:

cs
[SerializeField] private AnimancerComponent _Animancer;

protected virtual void Awake()
{
    _Animancer.Layers[0].ApplyAnimatorIK = true;
}

Unity 实际上是将此设置应用于每个状态(在AnimationClipPlayable中),而非每个层(在AnimationLayerMixerPlayable中)。因此 Animancer 会将AnimancerLayer.ApplyAnimatorIK的值传播到该层连接的所有AnimancerState.ApplyAnimatorIK,同时也会设置AnimancerLayer.DefaultApplyAnimatorIK用于新创建的状态。这实际上是本样本中唯一与 Animancer 相关的特殊处理。其他所有内容都与使用 Animator Controller 时完全相同。

关于利用 IK 让角色看向一下方向很简单:

cs
private void OnAnimatorIK(int layerIndex)
{
    //..
    _Animancer.Animator.SetLookAtPosition(_LookAtTarget.position);
    _Animancer.Animator.SetLookAtWeight(1);
}

WARNING

由于 Playables API 的限制,即使你使用了多个层,Unity 也始终只会传入layerIndex = 0

不平地面

在本样本中,你将学习:

  • 根据地形调整角色脚部的高度。
  • 从动画中读取自定义 AnimationCurve(动画曲线) 的值。

本样本使用了一份普通的 Walk 动画的副本,并额外添加了两个动画曲线 LeftFootIK 和 RightFootIK,用于决定在动画的任意时刻希望 IK 具有多大的权重。

自定义的 AnimationCurve 可以通过 Animancer 的AnimatedFloat类来访问,我们在启动时进行初始化:

cs
private AnimatedFloat _FootWeights;

protected virtual void Awake()
{
    _FootWeights = new AnimatedFloat(_Animancer, "LeftFootIK", "RightFootIK");

随后在OnAnimatorIK()中访问它们。

启动时还需要做其他几件事:

启用 IK,让 Unity 调用OnAnimatorIK()并应用到角色上。从 Animator 获取脚部骨骼并保存下来,避免每帧重复获取:

cs
private Transform _LeftFoot;
private Transform _RightFoot;

public bool ApplyAnimatorIK
{
    get => _Animancer.Layers[0].ApplyAnimatorIK;
    set => _Animancer.Layers[0].ApplyAnimatorIK = value;
}


protected virtual void Awake()
{
    ApplyAnimatorIK = true;
    
    _LeftFoot = _Animancer.Animator.GetBoneTransform(HumanBodyBones.LeftFoot);
    _RightFoot = _Animancer.Animator.GetBoneTransform(HumanBodyBones.RightFoot);
}

OnAnimatorIK()中,使用AnimatedFloat根据当前动画时间读取目标曲线的值(通过_FootWeights[x]):

cs
protected virtual void OnAnimatorIK(int layerIndex)
{
    UpdateFootIK(
        _LeftFoot,
        AvatarIKGoal.LeftFoot,
        _FootWeights[0],
        _Animancer.Animator.leftFeetBottomHeight);
    UpdateFootIK(
        _RightFoot,
        AvatarIKGoal.RightFoot,
        _FootWeights[1],
        _Animancer.Animator.rightFeetBottomHeight);
}

// footTransform 用于向下射线检测
private void UpdateFootIK(
    Transform footTransform,
    AvatarIKGoal goal,
    float weight,
    float footBottomHeight)
{
    Animator animator = _Animancer.Animator;
    animator.SetIKPositionWeight(goal, weight);
    animator.SetIKRotationWeight(goal, weight);

    // ..
}