[UE4] IK 구현하기 -2 (Two Bone IK(2본IK), 로컬을 컴포넌트로/컴포넌트에서 로컬로)
📐 발과 충돌된 지점의 간격 구하기
이전시간에 구현했던 LineTraceByChannel 추적값에 무언가 충돌되면 충돌된 객체와 플레이어의 발 사이 간격을 구해야 한다.
Line Trace에서 Impact Point는 충돌된 지점을 가리키고, Trace End는 충돌 검사가 끝나는 지점을 가리킨다.
따라서 발과 충돌된 지점의 간격을 구하고 싶다면 Impact Point에서 Trace End 벡터를 빼면 된다.
두 개의 벡터를 뺀 길이를 구하면 위 그림에서 초록색 구간에 해당하는 간격의 거리가 나온다.
먼저 Impact Point에서 Trace End 부분을 빼면 Impact Point와 End 지점간의 길이가 나온다.
해당길이에서 Offset Distance를 빼주는데, 이 값은 임의의 값(5)으로 애니메이션이 캐릭터와 살짝 떠있는 크기만큼 고정값을 그냥 준거기 때문에 Offset Distance는 신경쓰지 않아도 된다.
Impact Point와 End 사이의 거리에서 Trace Distance(발에서부터 End부분)를 빼면 발에서부터 Impact Point 사이의 길이가 나온다. Impact Point가 발 위에 있다고 해도 동일하게 발과 Impact Point간의 거리가 나와서, 발이 얼마만큼 움직일 것인지 간격을 구할 수 있다.
이렇게 구해진 이동 간격을 애니메이션 블루프린트에서 가져가서 Two Bone IK 함수를 호출하여 플레이어의 Feet IK를 구현해보았다.
🔎 로컬을 컴포넌트로 / 컴포넌트에서 로컬로
![]() |
![]() |
애니메이션에 IK를 적용시키기 위해선 가장먼저, 로컬 공간에 있는 애니메이션을 컴포넌트 공간으로 변경해주어야 한다.
(애니메이션의 공간 중, 로컬 공간은 변경이 불가능하고, 컴포넌트 공간은 변경이 가능하기 때문, 이전 게시글에 설명됨)
[UE4] IK란?(Inverse Kinematic), Bone 구조, Two Bone IK 솔버(IK+FK), 애니메이션 공간(Mesh/Local/Component)
📚 IK (Inverse Kinematic) IK란 고르지 않은 지형에서 지형에 맞추어 발을 딛는 반응형 애니메이션을 구현할 수 있는 기능이다. 울퉁불퉁한 땅이나 계단을 오를 때 캐릭터의 발이 Rotation을 알아서 처
cse-child.tistory.com
컴포넌트 공간에서 IK 적용이 다 끝나면 최종 애니메이션은 다시 로컬공간으로 변경한 본을 전달해준다.
이때 "로컬을 컴포넌트로" 바꾸는 과정은 Bone을 복사만 하기 때문에 몇번을 바꾸든 상관없이 여러 번 호출해도 된다. 그러나 "컴포넌트를 로컬로" 바꾸는 과정은 바뀐 Bone을 실제 Bone에 모두 적용시켜주고 변경해주기 때문에 연산으로 인해 속도가 느려질 수 있어 최소한으로 호출하는 것이 좋다.
💡 Two Bone IK (2본IK)
투 본 IK 컨트롤은 캐릭터의 특정 Bone을 Effector와 Joint로 설정하여 IK(역운동학) 기능을 적용할 수 있는 함수이다.
투 본 IK를 호출한 후 함수를 눌러 디테일 패널에서 값들을 지정 및 수정해주어야 원하는대로 사용할 수 있다.
Effector는 실제로 움직일 본 값을 의미한다. 발 IK를 구현하는 과정에서는 Foot_L 본이 될 수 있다.
Joint는 기준이 될 본을 의미한다. Two Bone IK는 IK 연산량을 줄이기 위해, IK 연산이 필요한 부분만 IK 연산을 적용하고, 나머지 부분은 FK로 계산하는 기능 구현할 수 있도록 도와주는 함수이다. 따라서 Joint로 정해둔 기준 본을 두고 Effector 본과 Joint 본 사이는 IK 연산을, 나머지 부분은 FK 연산을 수행하도록 구현한다.
여기서 또 중요한 점은, Effector와 Joint에 설정되는 본은 모두 실제 본이 아니고 가상 본으로 설정하게 된다. 두 요소를 Two Bone IK 함수 이전에 미리 초기화/설정 해놓은 후, Two Bone IK에서 이동될 본의 위치를 한번에 연산하고, 실제 Foot_L 같은 본에 적용시키는 과정으로 진행된다.
🚩 IK 사용 예시
우선, 왼발의 IK 적용 과정만 정리해보자.
1. Effector 설정
먼저, 로컬을 컴포넌트로 바꾼 뒤, Feet 컴포넌트에서 구해온 왼발의 트랜스폼과 회전값을 받아온다.
받아온 데이터를 Effector, 즉 왼발에 먼저 적용하여 왼발의 트랜스폼을 변경한다.
🔶 Bone to Modify
실제 적용은 바로 일어나는 것이 아니라 마지막 계산을 수행할 솔버(2 본 IK)에서 일어나기 때문에 왼발의 Bone to Modify를 가상 왼발 본(VB_ik_foot_l)으로 설정한다.
🔶 Translation Mode
- Ignore : 트랜스폼과 회전을 변경하지 않는다.
- Replace Existing : 설정한 트랜스폼과 회전값으로 변경시킨다. (바로 바뀜)
- Add to Existing : 트랜스폼과 회전값에 변경할 트랜스폼과 회전값을 더하면서 서서히 바뀌도록 변경시킨다.
나는 왼발의 위치가 서서히 바뀌도록 하기 위해 Add to Existing 로 설정하였다.
🔶 Translation Space
- World Space : 월드 공간
- Component Space : Mesh를 출력해주는 컴포넌트 공간
- Parent Bone Space : 컴포넌트 안에 존재하는 Bone의 부모 공간
- Bone Space : 컴포넌트 안에 존재하는 Bone들의 공간
나는 왼발의 Bone을 변경하고 싶기 때문에 Bone Space로 설정하였다. Component Space는 Mesh의 컴포넌트를 의미하는데, 이미 왼발의 Bone은 Mesh의 컴포넌트에 위치하는 값이기 때문에 의미가 없다!
🔶 Alpha
알파 값에 따라 IK가 완전 적용(1) 될지, 반만 적용(0.5) 될지, 아니면 적용되지 않을지(0)에 대한 설정을 할 수 있다.
위 코드에서의 Alpha 값은 애니메이션 마다 IK를 적용할지의 여부를 달리하기 때문에, 애니메이션에 커브로 0 또는 1의 값을 지정하여 전달하도록 하였다.
2. Joint 설정
나는 Joint를 무릎으로 설정하였다.
따라서 Joint를 설정할 본 트랜스폼에는 무릎 가상 본(VB_ik_foot_l_calf_l)로 Body를 설정하였다.
Joint에는 트랜스폼에 (20, 30, 0) 값의 절대값을 지정해주었는데 왼발의 트랜스폼은 X축이 위아래, Y축이 앞뒤였다.
발의 IK가 적용될 때 보통 무릎이 굽혀지기 때문에, 무릎을 앞으로 조금 빼서 굽힌 듯한 모양을 만들기 위한 트랜스폼 값을 지정해준것이다. (무릎의 굽힘 정도를 연산하는 방법도 있지만 너무 복잡해서 절대값을 지정해준 것)
무릎인 Joint의 설정값도 발과 마찬가지로, Add to Existing, Bone Space로 Tanslation 값을 지정해주었다.
3. Bone에 IK 연산 및 적용
- IKBone: 실제 움직일 본
- Allow Stretching : 본을 자연스럽게 늘리는 기능
- Max Stretch Scale : 최대 몇 배 까지 본을 늘릴 수 있게 할 것인지 설정
- Effector Target / Joint Target : IK 를 계산할 본
이제 가상 본이 아니라, 실제로 움직일 본인 Foot_L을 IKBone으로 지정해주고, Allow Streching 옵션을 체크해주었다.
이후, Effector와 Joint는 둘다 Bone 공간에 있기 때문에 둘다 Bone Space로 설정해주고, 계산할 본을 가상 본들로 설정해주었기 때문에 Effector Target 과, Joint Target에 가상 왼발, 가상 왼무릎을 넣어주었다.
4. 오른발도 왼발과 똑같이 적용
오른발도 왼발과 마찬가지로 오른발에 IK를 적용시키고, 오른 무릎에 FK, 이후 Two Bone IK에서 연산하여 IK를 실제로 적용하는 과정을 거쳤다.
! 오른발을 작업할 때 주의사항 !
오른발과 왼발의 트랜스폼은 반전되어 있기 때문에 왼발의 이동 값과 반대로, 즉 마이너스를 곱한 값으로 설정해야 한다.
따라서 절댓값을 지정한 오른쪽 무릎(VB ik_foot_r_calf_r)의 Translation 값은 (-20, -30, 0)으로 설정하였다.
5. 발과 무릎이 움직일 때 허리도 같이 움직이기
발과 무릎이 움직이면 자연스럽게 움직일 허리(Pelvis)까지 설정해줘야 IK 작업을 마무리 할 수 있다.
허리는 IK로 어딘가에 붙이거나 하지 않기 때문에 그냥 "본 트랜스폼(변경)"만 사용하여 변경해주고, Pelvis 본 자체가 World 공간에 있기 때문에 Translation Space를 World Space로 설정한다.
허리는 왼발과 오른발 중 더 낮은 위치에 있는 발쪽으로 맞춰 이동하면 되기 때문에 이동할 거리(Pelvis Distance)는 Left Distance와, Right Distance 중 더 작은 값(MIN)으로 설정하였다.
![]() |
![]() |
왼발 | 오른발 |
또한 허리가 숙여지는 만큼, 왼발과 오른발도 더 내려가야 하기 때문에 왼발의 거리 - 허리 거리, 오른발의 거리 - 허리 거리를 각각 빼주었고, 오른발은 왼발과 반대축이므로 (오른발 거리 - 허리 거리) * -1 값을 목표 지점으로 설정해주었다.
블루프린트를 보면 Pelvis Distance 변수를 Left와 Right보다 앞쪽에 Set 하는데, 이는 Left와 Right 변수를 초기화할 때 Pelvis Distance를 이용하기 때문에 먼저 설정해 준 것이다.
위와 같은 과정으로 구해진 Pelvis Distance를 애니메이션에서 가져가서 마지막으로 허리 트랜스폼을 변경하면 IK 구현이 완료되었다.
👣 발 회전 값 구하기
IK로 발 위치가 움직일 때 바닥의 경사에 맞는 Roll 회전과 Pitch 회전을 적용하여 발이 자연스럽게 꺾일 수 있도록 구현하기 위해, 발의 회전 값을 구해야 한다.
Atan을 이용하면 직각을 이루는 두 선분을 통해 cos 각도를 구할 수 있다.
이러한 Atan의 성질을 이용하여, Line Trace에서 충돌되는 지점의 Normal 벡터를 발의 회전값으로 변경하는 블루프린트를 작성하였다.
X(Roll)회전 값을 구하기 위해 Normal 벡터의 Y축과 X축의 Atan2 값을 구하였고, Y(Pitch)회전 값을 구하기 위해 X축과 Z축의 Atan2값을 구하면 된다.
이때 Pitch 회전에서 주의해야 할 점은, 회전하는 각도에 -1를 곱해주어야 한다.
오르막길을 발로 딛는다면 발이 윗쪽 방향으로 회전하게 되는데, 이는 Pitch의 반시계방향 회전이다. 시계방향으로 회전한다면 발이 발바닥 쪽으로 회전되어야 한다.
따라서 지면이 올라갈수록 발등이 몸쪽으로 회전해야 하므로, 반시계 방향 회전을 적용하기 위한 -1 값을 곱해주었다.
모든 연산 과정을 거쳐 구해진 Rotator 값을 IK에 적용할 때, RInterp To 함수를 이용해 보간하여 발이 부드럽게 회전되도록 구현하였다.
아래는 결과적으로 구현된 Feet IK의 적용 결과이다.