前回① (#)で CharacterController と Animator を使い、ヒューマノイドを歩かせる土台を作りました。ただ、アニメだけではキャラが常に正面を向き、足は坂道で地面に刺さったり浮いたりします。人間らしさを一段引き上げるのが IK(インバースキネマティクス)です。この記事では Unity の OnAnimatorIK
を使って、視線・足・手をリアルタイムに制御する方法を、実プロジェクトのコード付きで解説します。
IK(インバースキネマティクス)とは
通常のアニメは「肩→肘→手首」と根元から先端へ回転を決めます(フォワードキネマティクス)。IKはその逆で、「手をここに置きたい」という最終位置を先に決め、肘や肩の角度を自動計算します。これにより、
- 視線:動く対象を目で追う
- 足:坂・段差の地面に足裏を合わせる
- 手:ドアノブ・武器・テニスラケットなど対象を掴む といった「環境に反応する動き」を、アニメを作り直さずに実現できます。 前提:2つの設定を忘れると一切効かない(最重要) IKを書く前に、ここでつまずく人が非常に多いです。
1.モデルが Humanoid リグであること(Avatar の Animation Type = Humanoid)。GenericやLegacyでは OnAnimatorIK
の足・手ゴールが使えません。
2.Animator Controller のレイヤーで「IK Pass」を ON にすること。
using UnityEngine;
[RequireComponent(typeof(Animator))]
public class HumanoidIKController : MonoBehaviour
{
Animator _animator;
[Header("視線")]
[SerializeField] Transform lookTarget;
[Header("足の接地")]
[SerializeField] LayerMask groundMask = ~0;
[SerializeField] float rayUp = 0.5f; // 足の少し上からレイを出す
[SerializeField] float rayDown = 1.2f; // 下方向の探索距離
[SerializeField] float footOffsetY = 0.1f; // 足裏とボーン原点のズレ補正
[Header("手")]
[SerializeField] Transform rightHandTarget;
void Awake() => _animator = GetComponent(); void OnAnimatorIK(int layerIndex)
{
if (_animator == null) return;
ApplyLookAt();
ApplyFootIK(AvatarIKGoal.LeftFoot, "IKLeftFootWeight");
ApplyFootIK(AvatarIKGoal.RightFoot, "IKRightFootWeight");
ApplyHandIK(AvatarIKGoal.RightHand, rightHandTarget);
}
}
3.視線IK:対象を目で追う
SetLookAtWeight で「どれだけ追うか」、SetLookAtPosition で「どこを見るか」を指定します。
void ApplyLookAt()
{
if (lookTarget == null) { _animator.SetLookAtWeight(0f); return; } // (全体, 体, 頭, 目, 首振りの制限)
_animator.SetLookAtWeight(1f, 0.3f, 0.7f, 1f, 0.6f);
_animator.SetLookAtPosition(lookTarget.position);
}
SetLookAtWeight の引数がコツです。
- 第2引数 bodyWeight=0.3:体を少しだけひねる(1にすると全身で向いて不自然)
- 第3引数 headWeight=0.7:主に頭で追う
- 第5引数 clampWeight=0.6:真後ろまで首が回らないよう制限(人間らしさの肝)
2.足IK:坂・段差に足裏を合わせる 各足の現在のIK位置から真下へレイを飛ばし、地面のヒット点に足を移動+傾きを地面の法線に合わせます。
void ApplyFootIK(AvatarIKGoal goal, string weightParam)
{
// アニメのカーブで歩行中の接地タイミングに合わせて重みを変える
float weight = _animator.GetFloat(weightParam);
if (weight <= 0f) { _animator.SetIKPositionWeight(goal, 0f); return; }
Vector3 footPos = _animator.GetIKPosition(goal);
Vector3 origin = footPos + Vector3.up * rayUp;
if (Physics.Raycast(origin, Vector3.down, out var hit, rayUp + rayDown, groundMask))
{
// 位置:地面 + 足裏オフセット
_animator.SetIKPositionWeight(goal, weight);
_animator.SetIKPosition(goal, hit.point + Vector3.up * footOffsetY); // 回転:地面の傾きに足裏を合わせる Quaternion align = Quaternion.FromToRotation(Vector3.up, hit.normal) * transform.rotation; _animator.SetIKRotationWeight(goal, weight); _animator.SetIKRotation(goal, align); }
}
ポイント
- 重み(weight)はアニメのカーブから取る:足が地面に着いている瞬間だけ1、振り上げ中は0にすると、空中で足が固定される不自然さを防げま
す。Animationクリップに IKLeftFootWeight 等のカーブを追加して制御します。 - footOffsetY はモデルごとに調整。足が地面に刺さるなら増やし、浮くなら減らします。
3.手IK:対象を掴む ドアノブやラケットなどのターゲットに右手を合わせます。位置と回転の両方を指定します。
void ApplyHandIK(AvatarIKGoal goal, Transform target)
{
if (target == null) { _animator.SetIKPositionWeight(goal, 0f); return; } _animator.SetIKPositionWeight(goal, 1f);
_animator.SetIKRotationWeight(goal, 1f);
_animator.SetIKPosition(goal, target.position);
_animator.SetIKRotation(goal, target.rotation);
}
腕が届かない位置にターゲットを置くと肘が不自然に伸び切るので、ターゲットは肩から腕の長さの範囲に置くのが基本です。
よくあるハマりどころ
| 症状 | 原因 |
| IKが一切効かない | レイヤーの IK Pass が OFF/リグが Humanoid でない |
| 足が地面に刺さる/浮く | footOffsetY の調整不足、レイのレイヤーマスク漏れ |
| 空中で足が固定される | 重みを常に1にしている(カーブで接地時のみ1に) |
| 首が真後ろまで回る | SetLookAtWeight の clampWeight が大きすぎる |
| 手や視線がカクつく | ターゲットの移動をLerpで滑らかにしていない |
まとめ
- IKは OnAnimatorIK の中だけで設定する
- 大前提は Humanoidリグ+レイヤーのIK Pass ON
- 視線=SetLookAtWeight/Position、足=レイで地面に接地+法線で傾き、手=ターゲットに位置・回転を合わせる
- 足の重みはアニメのカーブで接地時だけ効かせるのが自然さの鍵
次回③では、IKと組み合わせて使う 手続き的な歩行(ProceduralWalk)と重心バランス制御(BalanceController)
を取り上げ、アニメに頼らず足を運ばせる仕組みを解説します。

