초보 코린이의 성장 일지

UE4 엄청 어려운 FK, IK 계산 본문

언리얼

UE4 엄청 어려운 FK, IK 계산

코오린이 2023. 2. 3. 16:40

이론은 이해할 수 있으나 과정 자체가 너무 복잡하고 지금까지 했던 과정들을 넘어서는 어려운 난이도를 자랑한다.

하지만 개발에 있어 꼭 알아야 하는 부분이라 최대한 정리를 해보겠다.

 

스켈레톤 본들을 보면 위에서 아래로 흐르는 구조로 각 관절이 정리되어있다.

또 한 본들은 각 부위마다 위치, 회전, 크기를 가지고 있고, 각 트랜스폼을 가지고 있다.

 

FK

1.위에서 천천히 내려오면서 각 부위에 도착할때마다 값을 계산하고 아래로 내려오면서 위에 값들을 누적하면서 계속 내려온다

2. 위에서 연산하는만큼 엄청 빠르다.

3. 문제점은 애니메이션 수정이 불가능하다.

4. 가만히 서있다고 생각했을때, 발이 어딘가 올라가 있다고 가정하면 모션에 변화가 생기게된다. 하지만 계산이 이뤄진 상태로 내려오다보니 나중에 차질이 생겨서 결국 애니메이션 변경이 불가능하다.

 

IK

1. FK와 반대로 아래에서 위로 올라가는 구조이다.

2. 애니메이션 수정이 가능하다.

 

몸이 상하체로 나눠져 있으므로, 위에서 FK가 내려오고 아래에서 IK가 올라와 결국 중간 지점 스켈레톤 골반지점에서 만나

계산되고 동작이 이뤄지는걸 "Two Bone IK" 라고 부른다

 

위에 설명을 토대로 먼저 발만 움직여 볼 것이다.

- 로컬 공간(FK로 계산된 공간, 애니메이션 공간)

- Mesh공간(FK로 계산되지만 수정이 가능, 하지만 언리얼에서 옵션기능을 줘야 가능 자체적으로 불가능)

- Component 공간(FK + IK 수정이 가능하다, 별도로 임의로 계산되는 공간) 

- 로컬공간을 Component공간으로 바꾸는건 가능하나(이때는 문제가 없다), Component공간을 로컬공간으로 바꾸는 방법은 IK를 FK로 재계산을 하기 때문에 연산량이 너무 많아져서 사용하지 않는게 좋다.

 

 

IK이전에 우선은 줌기능을 구현해서 선형보간을 할 것이다.

Speed = 줌 속도

MinRange = 줌을 최소로 땡길 범위

MaxRange = 줌을 최대로 땡길 범위

InterpSpeed = 보간 속도

 

WeaponComponent로 가서 함수로 타입하나를 지정해준다. 왜 이름을  BowMode라고 지정한 이유는 활을 든 상태일때와 아닐때가 앞으로 구현할 상태들에있어 제일 많이 사용되는 조건이 될 거기 때문이다.

1. Spring Arm 안에 있는 Target Arm Length를 줌안에 우선 세팅해놓고 시작해보겠다.

위에 설명한대로 BowMode 상태로 조건을 처리할게 많이 사용할 일이 생기기 때문에 편리하게 사용하기 위해 함수로 미리 빼놨다. 또 한 사용할 변수도 선언했다.

 

1. 오른쪽 마우스를 눌러서 줌을 하고있는 BowMode가 아니라면 ture로 연결

2. 이미 값을 세팅해놓은 ZoomData를 핀분활해서 사용, Axis를 Spped와 곱해준다

3. 곱한 값을 Zooming에서 또 더해준다. 그럼 줌이 속도에 따라 올라갔다 내려갔다 할 것이다.

4. 값 제한을 clamp로 해준다. Min, Max를 연결

5. 제한한 값을 Zooming에 넣어준다.

 

바로 아래 또 커스텀이벤트 Apply Zoom 생성

1. BowMode가 아닌지부터 체크해준다.

2. Zooming이 가려는 값을 나타낸다. Equal로 가려는 값이 갈 값과 동일한지 확인해준다.

3. 그래서 Zooming와 Target Arm Length에 오차가 0.1이 보다 작다면 같다고 간주 할 것이다.

4. 같으면 계산하면 안되므로 Not를 넣어줘서 조건문까지 이어준다.

5. Finterp To로 현재값 Current에 연결, 갈 값이 Zooming으로 Target에 연결

6. World Delta Seconds로 Time에 연결해준다. Tick 이벤트에서 받는 값과 동일하다

7. Zoom Data을 핀분활하고, 그 안에 들어있는 Speed값을 연결

8. 모든 총 값을 Target Arm Length에 넣어준다.

 

줌기능이 만들어졌다. 만든 이유는 IK를 활용했을때 정확하게 붙어있는지를 상세히 보기위한 준비과정이다.

 

구조체 하나를 생성해준다. 위 순서대로 설명을 적어놓겠다.

 

1. 왼쪽 발이 움직일 간격 Vector지만 X축만 가져다 사용할 것이다.

2. 오른쪽 발이 움직일 간격 Vector지만 X축만 가져다 사용할 것이다.

3. 허리가 움직일 간격

4. 왼발이 회전 할 값

5. 오른발이 회전 할 값

 

블루프린트 클래스 Actor Component로 생성해준다.

사용 할 함수 및 변수를 생성

 

1. 시작할때 주인이 캐릭터인지 확인만 해준다. 

2. 중요한 행동들은 전부 Tick에서 다룰 것이다.

 

함수 Apply Inverse Kinemetics 들어와서 노드 작성

안에서만 사용 할 로컨 변수 생성, 변수 명대로 위치와 회전을 구해서 저장시키고 사용할 것이다.

Apply Inverse Kinemetics

매 프레임마다 이전 값을 저장하는 구조로 갈 것이다.

스켈레톤 본에 발을 눌러서 보면 윗방향이 X축이고 허리부분을 보면 윗방향이 Z축이다

 

시퀀스 0번

1. 변수 Data를 가져다가 Vector 부분만 핀분활을 해준다. 이 Data는 이전값을 나타 낼 것이다.

2. Left Distance와 Right Distance는 X값만 저장 위로 올라 갈 것이기 때문

3. 허리부분은 설명대로 Z축을 저장

4. 나머지 회전은 각 맞는 회전에 연결

 

시퀀스 1번

이제 자연스럽게 보이도 보간을 해 줄 것이다.

1. Socket Nmae를 왼발, 오른발 2개를 설정해준다.

2. 시퀀스 0번 Apply Inverse Kinemetics에 있는 이전 프레임에 값에서 저장해 놓은 값을 왼쪽 발 부분인 Left Distance Save에 저장한다

3. 왼쪽과 동일하게 오른쪽 발도 똑같이 저장해놓는다. 다른점은 아래 그림에 설명과 같이 대칭이므로 -1을 곱해줘서 축을 뒤집어 줘야한다. 이부분 정말 중요하다

4. 이제 나온 값 전부를 Data를 핀분활 한 후에 전부 X축에 저장해 줄 것이다.

 

- 디자이너들이 스켈레톤을 지정해 놓을때 절반만 구현하고 나머지 반을 뒤집어서 붙여서 만든다 그래야 축이 그림과 같이 나오는 것이다. 그래서 좌우 대칭이 일어난다.

 

이제 실제적인 거리를 구할 것이다.

만들어 놓은 함수 Trace로 와서 진행 할 것이다.

Trace

Trace에서 사용 할 로컬 변수

그리고 Trace를 눌러서 입력, 출력 파라미터 추가해준다.

 

기본적인 위치는 계산했으나 발 밑에 뜨는 부분까지 계산을 할 것이다.

Trace 앞 부분
Trace 뒷 부분

계산식이 상당히 길고 복잡하다.

 

1. 소켓은 Character안에 Mesh에 있으므로 불러서 위치인 Socket Location에 연결해준다.

2. Socket Location을 핀분활해서 캐릭터의 발 위치 X, Y만 사용한다. 그리고 중심의 높이를 사용할땐 Z축이 필요하다

3. 중심 높이를 구하는 방법은 단순하다 Character 자체 높이가 중심 높이가된다.

4. 그 나온 값을 로컬 변수 Start에 저장한다.

5. LineTraceByChannel에 연결해준다. 설정에 보면 Visibility는 보이는건 전부 수행하라는 뜻이다. 그리고 기본적으로 Trace 반응이 일으키게 되어있다 또 한 블록으로 설정이 처리되어있다.

6. End 지점이 발을 지나서 예외처리를 위해 아래까지 계산을 해야하므로 Make Vector 을 연결해준다. 중요한건

X, Y는 쓰지않으니 그대로 연결해주고, Z축만 구해주면 된다.

7. Capsule Component가 캐릭터 전체 크기를 나타내고, 변환될수도 있는 값을 사용할 것이므로 그냥 Scaled 사용

8. Half Height가 현재 캡슐 크기에 반을 나타내고, Set Start에 들어있는 Z축 위치와 - 해주면 절반이 나온다.

9. 하지만 발 밑에 기준으로 조금 더 내려가야 정확히 떨어질 수 있는 예외처리가 필요해서 TraceDistance 변수를 사용해서 - 해준 만큼 더 내려간다 2번의 -를 해주는 것이다.

10. LineTraceByChannel에 Trace Complex는 체크를 해주면 계산이 느려지지만 정확한 계산수행은 할 수 있다. 발 위치가 정확히 떨어지는지 우리는 확인해야 하므로 체크를 해 줄 것이다. (꼭 필요할때만 사용 할 것)

11. Character를 배열로 만들어서 제거해준다. 캐릭터는 추적하면 안되기 때문

12. 직선으로 뻗은 선에 걸려서 Return Value가 하나라도 충돌을 했다면 True가 나오지만 충돌이 안됐으면 할 필요가 없으므로 False로 Return 시켜버린다.

13. 이제 Hit를 확인하고 확인이 됐다면 닿은 지점인 Impact Point와 끝 지점인 Trace End를 빼 준다.

14. 곧 빼준 거리가 Vector에 크기가 되므로 Length로 연결해준다. 그리고 변수 OffsetDistance를 사용하여 5만큼 빼준다.

5로 정한 이유는 캐릭터가 잘 보면 발 지면에 닿아도 조금 떠있다 그 수치가 약 5정도된다. 그래야 자연스럽다

15. - 값이 나오면 발이 내려가고, + 값이 나오면 발이 올라가는걸 나타낸다.

16. 결국 값을 구해서 오른쪽 노드 끝에 반환로드에 있는 Out Distance에 들어가는 값이 그 거리만큼 발이 움직일거라는 수치를 나타낸다.

 

Get Scaled를 검색하면 그냥 Scaled와 UnScaled가 있다

Scaled 붙은 함수 = 원래 캡슐 크기에다가 스케일 지정한 값을 곱해준 값, 스케일 위아래를 늘렸다고 가정한걸 감안해서 Return해주는 것

UnScaled 붙은 함수 = 원래 스케일 늘린걸 곱하지 않고 원본값 Return해주는 것

 

 

Tick에서 FeetComponent 안에서 만들어 놓은 함수 Apply_InverseKinemetics를 콜해준다.

 

 

플레이어로 돌아가서 만든 컴포넌트 Feet 또 한 추가해준다.

 

ABP에서 사용 할 변수
애니메이션 이벤트그래프

애니메이션 이벤트그래프로 와서 이제 동작에 대한 애니메이션이 자연스럽게 나오도록 동작을 정리해준다.

 

1. 시퀀스로 나눠주고 Character에 있는 Component를 만들어준 Feet으로 설정해준다.

2. Feet 연산을 할 수도 있고 하지 않을 수도 있기 때문에 False와 True에 bool 변수로 체크해준다.

 

 

이제 궁긍적으로 만든 지형에 따라 움직이는 발 동작을 IK는 애니메이션 구간 어디에서 들어가야 좋을까? 

모든 애니메이션이 끝난 후 지형에 따라 움직이는게 맞다 그래서 모든 애니메이션이 끝난 뒤에 추가를 해준다.

 

 

애니메이션 레이어에 FeetLayer를 하나 추가해준다.

이 곳에서 연산이 앞으로 이뤄질 것이다.

 

위 그림대로 링크된 애님 레이어를 만들고, 설정에서 방금 생성 한 애니메이션 레이어를 설정해준다.

AnimGraph 최종 연결 상태

1. 위에 계산한 모든 값의 동작이 링크된 애님 레이어에서 결국 동작하는 것이다.

 

https://www.youtube.com/watch?v=23hunfsjJXs 

 

- 이제 IK에 일부분을 알게 된 것이다.

- 너무 어려운 부분이라 정리를 하지만 이해하지 못하는게 너무 많다.

- 다시 계속 보면서 이해를 해봐야겠다.

Comments