Unity Animator에서 지원하는 IK 기능을 사용했습니다.
참고 자료
b3agz의 REALISTIC Foot Placement Using IK in Unity
1. IK Pass 활성화
- IK를 적용하려는 객체의 Animator Layer에 IK Pass를 활성화
2. 사용한 API 설명
- SetIKPositionWeight: AvatarIKGoal에 해당하는 부분의 포지션 변환 가중치 설정 (0 = IK 이전의 원본 애니메이션, 1 = 목표)
- SetIKRotationWeight: AvatarIKGoal에 해당하는 부분의 회전 변환 가중치 설정 (0 = IK 이전의 원본 애니메이션, 1 = 목표)
- GetIKPosition: AvatarIKGoal에 해당하는 곳의 Position 값 반환
(Ex. AvatarIKGoal.LeftFoot 이면, 아바타의 LeftFoot에 매핑되어 있는 곳의 Position 값)
GetIKPosition의 AvatarIKGoal 관련 추가 설명
3. 코드 설명
IK 적용 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
if (animator)
{
// Left Foot
// Position 과 Rotation weight 설정
animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, 1);
animator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, 1);
///<summary>
/// GetIKPosition
/// => IK를 하려는 객체의 위치 값 ( 아래에선 아바타에서 LeftFoot에 해당하는 객체의 위치 값 )
/// Vector3.up을 더한 이유
/// => origin의 위치를 위로 올려 바닥에 겹쳐 바닥을 인식 못하는 걸 방지하기 위해
/// (LeftFoot이 발목 정도에 있기 때문에 발바닥과 어느 정도 거리가 있고, Vector3.up을 더해주지 않으면 발목 기준으로 처리가 되어 발 일부가 바닥에 들어간다.)
///</summary>
Ray leftRay = new Ray(animator.GetIKPosition(AvatarIKGoal.LeftFoot) + Vector3.up, Vector3.down);
// distanceGround: LeftFoot에서 땅까지의 거리
// +1을 해준 이유: Vector3.up을 해주었기 때문
if (Physics.Raycast(leftRay, out RaycastHit leftHit, distanceGround + 1f, layerMask))
{
// 걸을 수 있는 땅이라면
if (leftHit.transform.tag == "WalkableGround")
{
Vector3 footPosition = leftHit.point;
footPosition.y += distanceGround;
animator.SetIKPosition(AvatarIKGoal.LeftFoot, footPosition);
animator.SetIKRotation(AvatarIKGoal.LeftFoot, Quaternion.LookRotation(transform.forward, leftHit.normal));
}
}
// Right Foot
animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, 1);
animator.SetIKRotationWeight(AvatarIKGoal.RightFoot, 1);
Ray rightRay = new Ray(animator.GetIKPosition(AvatarIKGoal.RightFoot) + Vector3.up, Vector3.down);
if (Physics.Raycast(rightRay, out RaycastHit rightHit, distanceGround + 1f, layerMask))
{
if (rightHit.transform.tag == "WalkableGround")
{
Vector3 footPosition = rightHit.point;
footPosition.y += distanceGround;
animator.SetIKPosition(AvatarIKGoal.RightFoot, footPosition);
animator.SetIKRotation(AvatarIKGoal.RightFoot, Quaternion.LookRotation(transform.forward, rightHit.normal));
}
}
}