Post List

2017/12/07

Unreal4__10_Head_Tracking

게임을 보면 대기상태에서 카메라가 케릭터 앞에있을때 케릭터는 카메라를 바라보는 경우가 있지요. 오늘은 기능을 소개해보겠습니다.

이렇게 보니 약간 무섭네요 T^T... 아무튼 기술 구현을 목적으로... 

카메라의 Forward Vector 플레이어의 Forward Vector 각도를 계산해 일정범위내에 있을때에만 카메라를 바라보게 제한을 두었습니다.

스켈레톤에 목에 해당하는 본의 회전값을 제어하기 위해 저는 블루프린트로 접근해서 만들어보았습니다.

카메라와 플레이어간의 각도계산은 C++에서 이루어지며, 바라보게되는 범위내에 있으면 블루프린트로 전달할 있는 bool 변수 값을 추가하여 블루프린트에서 해당 값이 true 되면 회전값을 변경해주도록 하였습니다.

우선 플레이어의 블루프린트를 먼저 보겠습니다


노드들이 많습니다…
우선 'Follow Camera', 'Mesh', 'Capsule Component' 필요합니다.
Follow Camera  캐릭터를 바라보는 벡터를 구하기 위함입니다.
Mesh 회전할 본의 월드 좌표를 얻어 위에서 말했듯이 카메라를 바라보는 벡터를 구하기 위함입니다.
Capsule Component 실제 플레이어가 향하고 있는 Forward Vector 얻어오기 위함입니다.

여기서 Mesh Get Socket Location 노드는 스켈레톤에서 해당하는 본의 월드 공간 좌표를 반환하기때문에 Follow Camera 월드좌표와 연산을해도 문제가 없답니다.

이제 카메라를 바라보는 벡터, 플레이어의 Forward Vector 카메라를 바라보는 벡터로 이동하기 위한 회전 값을 구하는 노드를 연결해보겠습니다.

먼저 Follow Camera로부터 GetWorldLocation노드로 월드 위치 좌표를 얻어옵니다.
그리고 저는 Mesh 'neck' 해당하는 본을 회전시킬 생각이기때문에 'neck' 해당하는 본의 월드 위치 좌표를 얻어옵니다.

그리고 벡터를 빼주는데요, 순서는 [ 카메라 위치 좌표 - 'neck' 좌표 ] 입니다그래야 플레이어가 카메라를 바라보는 벡터가 만들어지지요.
만약 반대로 뺏을경우 카메라가 플레이어를 바라보는 벡터의 값이 나오게 되기때문에고려해서 연산해주시면 되겠습니다.

플레이어가 카메라를 바라보는 벡터를 Tracking Vector 라고 하겠습니다.

Tracking Vector Normalize 통해 단위벡터로 만들어줍니다.

이제  Forward Vector Tracking Vector간의 사이각도를 구해보겠습니다.

벡터사이의 각도를 구하는 방법은 벡터의 내적 값을 이용하는 방법입니다.
벡터의 내적값을 acos으로 변환하면 라디안 단위의 세타값이 나옵니다.

벡터의 내적을 구하는 공식은 두가지 입니다.
  1. A · B = Ax * Bx + Ay * By + Az * Bz
  2. A · B = ( A.length * B.length ) * cosθ

Ax * Bx + Ay * By + Az * Bz = ( A.length * B.length ) * cosθ;
이렇게 표현할 있습니다.

( Ax * Bx + Ay * By + Az * Bz  ) / ( A.length * B.length )  = cosθ;
사이각을 얻기위해 cosθ만 남기고 왼쪽으로 모두 넘겨줍니다. 그럼 위와 같은 식이 나오고,

( A · B ) / ( A.length * B.length ) = cosθ;
위와 같은 식으로도 되겠지요.  Cosθ를 각도로 바꾸게되면 acos 사용하면 됩니다.

Acos이란 cos함수의 역함수이죠, 반환되는 값은 라디안값이 됩니다.

그러면 식은,
Acos( ( A · B ) / ( A.length * B.length ) ) = radian 됩니다.

이게 여기서 식을 간단하게 계산하기위해
Tracking Vector Normalize 통해 단위벡터로 만들어줍니다Normalize 호출함으로써 백터는 단위백터로 바뀌게되고 길이가 1 백터로 되지요.

그렇게 되면
Acos( A · B ) = radian

이렇게 간단해지게 된답니다.

식을 이용해 Capsule Component Forward Vector Tracking Vector 내적하고  결과값을 Acos으로 변환하여 벡터간 사이값을 구하게 됩니다.

애님 그래프로 와서 'Neck Rotation'값을 실제 'neck_01' 해당하는 본에 회전값을 넣어주겠습니다.
'본 트랜스폼(변경)'(Transform (Modify) Bone)노드는 지정된 본의 트랜스폼을 변경하는데 사용됩니다.  저는 회전값만 변경해줄 예정이지만, 회전값외에도 이동,스케일값도 변경이 가능하답니다.

그럼 'Neck Rotation' 변수에 담긴 목의 회전값을 Rotation에 연결해줍니다.

여기서 한가지 더 짚고넘어갈 부분이 있습니다. 'Neck Rotation'은 월드공간에서의 데이터로 만들어진 회전값이였죠,
일반적으로 애니메이션 블루프린트에서 포즈 작업을 할 때, 포즈는 로컬 스페이스에 있게 됩니다. 그러나 특정 블렌드노드와 모든 SkeletalContral은 컴포넌트 스페이스에서 작동합니다. 즉, 이러한 유형의 노드에 전달하기 전 인풋 포즈를 변환해줘야 한다는 뜻입니다. 인풋 포즈가 로컬 스페이스 포즈를 출력하는 노드에서 오는 경우, 그 포즈를 올바른 스페이스로 변환해 줘야 스켈레탈 컨트롤이 그에 맞는 작업을 할 수가 있습니다. 그 후 스켈레탈 컨트롤 작업을 하고 난 결과 포즈를 다시 로컬 스페이스로 변환해 줘야 추가적인 블렌드나 Result핀에 쓸 수 있습니다.



마지막으로 Rotation에 Rotation Space를 World Space로 변경해주시고, 플레이를 하면 처음 gif와 같이 아까 지정한 범위에 카메라가 들어올 시 캐릭터의 시선은 카메라를 바라보게 됩니다. 
저는 translation과 scale은 사용하지 않기때문에 check를 해제해 비활성화 해주었답니다.