콘텐츠로 이동

Unity Animation Rigging IK 체인 구성 완전 가이드

Unity Animation Rigging 패키지는 런타임 IK와 컨스트레인트를 구현합니다. 애니메이션 클립과 독립적으로 동작하여 표면 적응, 조준, 손 위치 보정 등을 실시간으로 처리할 수 있습니다.

설치: Package Manager → com.unity.animation.rigging


캐릭터 오브젝트 구조:
Character
├── Rig 1 (Rig 컴포넌트 + RigBuilder 컴포넌트를 Character에 추가)
│ ├── TwoBoneIK_RightHand
│ ├── TwoBoneIK_LeftHand
│ └── AimIK_Head
└── Armature
└── (스켈레톤 본)

RigBuilder 설정: Character 오브젝트에 Rig Builder 컴포넌트 추가 → Rigs 리스트에 Rig 오브젝트 추가


// 에디터 설정 후 런타임 제어
using UnityEngine;
using UnityEngine.Animations.Rigging;
public class HandIKController : MonoBehaviour
{
[Header("IK 컨스트레인트")]
[SerializeField] private TwoBoneIKConstraint rightHandIK;
[SerializeField] private TwoBoneIKConstraint leftHandIK;
[Header("무기 그립 위치")]
[SerializeField] private Transform rightGrip;
[SerializeField] private Transform leftGrip;
private float _ikWeight = 0f;
private bool _isHoldingWeapon = false;
void Update()
{
// IK 무게 스무스 전환
float targetWeight = _isHoldingWeapon ? 1f : 0f;
_ikWeight = Mathf.MoveTowards(_ikWeight, targetWeight, Time.deltaTime * 5f);
rightHandIK.weight = _ikWeight;
leftHandIK.weight = _ikWeight;
// 손 위치를 무기 그립으로 이동
if (rightGrip != null)
rightHandIK.data.target.position = rightGrip.position;
if (leftGrip != null)
leftHandIK.data.target.position = leftGrip.position;
}
public void PickUpWeapon(Transform weaponRightGrip, Transform weaponLeftGrip)
{
rightGrip = weaponRightGrip;
leftGrip = weaponLeftGrip;
_isHoldingWeapon = true;
}
}

public class AimSystem : MonoBehaviour
{
[SerializeField] private MultiAimConstraint spineAim; // 척추 조준
[SerializeField] private MultiAimConstraint headAim; // 머리 조준
[SerializeField] private Transform aimTarget; // 조준점
[SerializeField] private float aimSmoothSpeed = 10f;
private Vector3 _targetPosition;
private bool _isAiming;
void Update()
{
if (_isAiming)
{
// 조준 무게 증가
spineAim.weight = Mathf.Lerp(spineAim.weight, 1f, Time.deltaTime * aimSmoothSpeed);
headAim.weight = Mathf.Lerp(headAim.weight, 0.8f, Time.deltaTime * aimSmoothSpeed);
// 조준점 이동
aimTarget.position = Vector3.Lerp(
aimTarget.position, _targetPosition, Time.deltaTime * aimSmoothSpeed);
}
else
{
spineAim.weight = Mathf.Lerp(spineAim.weight, 0f, Time.deltaTime * 5f);
headAim.weight = Mathf.Lerp(headAim.weight, 0f, Time.deltaTime * 5f);
}
}
public void SetAimTarget(Vector3 worldPosition)
{
_targetPosition = worldPosition;
_isAiming = true;
}
public void StopAiming() => _isAiming = false;
}

public class FootIKSystem : MonoBehaviour
{
[SerializeField] private TwoBoneIKConstraint leftFootIK;
[SerializeField] private TwoBoneIKConstraint rightFootIK;
[SerializeField] private Transform leftFootTarget;
[SerializeField] private Transform rightFootTarget;
[SerializeField] private float footHeightOffset = 0.1f;
[SerializeField] private LayerMask groundMask;
void LateUpdate()
{
AdaptFootToGround(leftFootTarget, leftFootIK);
AdaptFootToGround(rightFootTarget, rightFootIK);
}
void AdaptFootToGround(Transform footTarget, TwoBoneIKConstraint constraint)
{
Ray ray = new Ray(footTarget.position + Vector3.up * 0.5f, Vector3.down);
if (Physics.Raycast(ray, out RaycastHit hit, 1.5f, groundMask))
{
// 발을 지면에 붙임
Vector3 targetPos = hit.point + Vector3.up * footHeightOffset;
footTarget.position = Vector3.Lerp(
footTarget.position, targetPos, Time.deltaTime * 15f);
// 법선 방향으로 발 회전
Quaternion targetRot = Quaternion.FromToRotation(
footTarget.up, hit.normal) * footTarget.rotation;
footTarget.rotation = Quaternion.Lerp(
footTarget.rotation, targetRot, Time.deltaTime * 15f);
constraint.weight = 1f;
}
else
{
constraint.weight = Mathf.Lerp(constraint.weight, 0f, Time.deltaTime * 5f);
}
}
}

5. OverrideTransform — 본 직접 제어

섹션 제목: “5. OverrideTransform — 본 직접 제어”
public class HeadLookAt : MonoBehaviour
{
[SerializeField] private OverrideTransform headConstraint;
[SerializeField] private Transform lookAtTarget;
void Update()
{
// 머리가 바라볼 방향 계산
Vector3 direction = lookAtTarget.position - headConstraint.transform.position;
headConstraint.data.target.rotation = Quaternion.LookRotation(direction);
headConstraint.weight = 0.7f; // 70% 영향 (애니메이션 30% 유지)
}
}

public class RigController : MonoBehaviour
{
[SerializeField] private Rig combatRig;
[SerializeField] private Rig idleRig;
private RigBuilder _rigBuilder;
void Awake()
{
_rigBuilder = GetComponent<RigBuilder>();
}
public void SwitchToCombat()
{
combatRig.weight = 1f;
idleRig.weight = 0f;
}
public void SwitchToIdle()
{
StartCoroutine(BlendRigs());
}
IEnumerator BlendRigs()
{
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime * 3f;
combatRig.weight = 1f - t;
idleRig.weight = t;
yield return null;
}
}
}

컨스트레인트용도
TwoBoneIKConstraint팔/다리 2본 IK
ChainIKConstraint꼬리/촉수 다본 IK
MultiAimConstraint본이 타겟을 바라봄
OverrideTransform본 위치/회전 직접 덮어쓰기
MultiParentConstraint여러 부모의 영향 혼합