IK
更新: 4/8/2026 字数: 0 字 时长: 0 分钟
Puppet(木偶)
本示例演示了如何使用 Unity 的 Inverse Kinematics(IK) 系统,让角色实时看向一个移动的目标(例如鼠标位置)。
- 使用
Animator.SetLookAtPosition()和Animator.SetLookAtWeight()来控制角色的头部和眼睛注视方向。 - Animancer 可以无缝地与 Unity 的内置 IK 系统结合使用。
- 本示例还展示了如何在播放普通动画的同时应用 IK。
开启 IK:
[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 让角色看向一下方向很简单:
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类来访问,我们在启动时进行初始化:
private AnimatedFloat _FootWeights;
protected virtual void Awake()
{
_FootWeights = new AnimatedFloat(_Animancer, "LeftFootIK", "RightFootIK");随后在OnAnimatorIK()中访问它们。
启动时还需要做其他几件事:
启用 IK,让 Unity 调用OnAnimatorIK()并应用到角色上。从 Animator 获取脚部骨骼并保存下来,避免每帧重复获取:
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]):
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);
// ..
}