액션 슈터 캐릭터를 만들다 보면 시점 전환, 조준, 손 IK, 카메라 반동이 서로 얽힌다. TPS에서는 전신 캐릭터가 자연스럽게 보여야 하고, FPS에서는 팔과 무기가 카메라 기준으로 안정적으로 보여야 한다. ADS 상태에서는 조준점, 무기 스코프, 손 위치가 같은 방향을 바라봐야 하며, 발사 시에는 무기와 카메라가 함께 반응해야 한다.
이번 구현에서는 TPS와 FPS를 하나의 메쉬로 억지로 처리하지 않고, 용도별 카메라와 스켈레탈 메쉬를 분리했다. 이렇게 나누면 렌더링 조건과 애니메이션 보정이 조금 늘어나지만, 각 시점에서 보여야 하는 것과 숨겨야 하는 것을 명확하게 제어할 수 있다.
캐릭터에는 TPSCamera와 FPSCamera를 두고, 현재 시점에 따라 한쪽 카메라만 활성화한다.
TPS에서는 캐릭터 전신과 무기가 월드 안에서 자연스럽게 보이는 것이 중요하고, FPS에서는 카메라 앞의 팔과 무기가 흔들림 없이 보이는 것이 중요하다.
그래서 카메라 전환은 단순한 위치 변경이 아니라, 어떤 메쉬를 렌더링할지까지 함께 바꾸는 상태 전환으로 다뤘다.
시점 전환 시 처리하는 값은 크게 세 가지다.
카메라만 전환하면 TPS와 FPS가 모두 같은 본 포즈를 공유하게 된다. 이 경우 FPS에서 머리나 상체가 카메라를 가리거나, TPS에서 1인칭 팔 메쉬가 전신 메쉬와 겹쳐 보이는 문제가 생긴다. 그래서 렌더링 대상이 다른 세 개의 메쉬를 사용했다.
캐릭터의 스켈레탈 메쉬는 다음처럼 나눴다.
FPSCamera 하위에 붙는 1인칭 팔 전용 메쉬기본 전신 메쉬는 월드에서 캐릭터의 실제 외형을 담당한다. 그림자도 이 메쉬에서만 활성화했다. FPS로 전환했을 때 로컬 플레이어에게는 보이지 않게 처리하지만, 다른 플레이어에게는 계속 보이도록 유지한다.
뷰 메쉬는 로컬 플레이어 전용이다.
TPS에서는 숨겨두고, FPS 또는 특정 조준 상태에서 로컬 화면 보정을 위해 사용한다.
이 메쉬에서는 upperarm_l, upperarm_r, head 본을 숨겨서 FPS 팔 전용 메쉬와 겹치지 않도록 했다.
FPS 팔 전용 메쉬는 FPSCamera 하위에 붙인다.
카메라 기준으로 팔과 무기가 보이도록 하기 위한 메쉬이므로, 전신 메쉬의 월드 포즈와는 다른 기준으로 움직일 수 있다.
이 분리는 FPS 화면 안정성을 얻기 위한 핵심 구조다.
같은 캐릭터라도 TPS와 FPS에서 필요한 시각 정보가 다르다. 전신, 로컬 뷰, 1인칭 팔을 분리하면 렌더링 조건과 IK 보정을 시점별로 다르게 가져갈 수 있다.
TPS ADS는 무기 스코프가 실제 타겟 방향을 바라보도록 만든 뒤, 그 결과에 맞춰 양손 IK를 맞추는 방식으로 처리했다. 핵심은 무기 기준의 보정값을 미리 알고 있어야 한다.
먼저 ADS 기준 포즈를 가진 BP_ADSPose 캐릭터를 별도로 두고, 무기별로 필요한 값을 사전에 계산했다.
여기서 구한 InverseScopeRelativeToHandR와 InverseScopeRelativeToHandL 값을 무기 데이터 에셋에 저장한다.
무기가 변경되면 캐릭터는 현재 무기 데이터 에셋에서 이 값을 가져온다.
실제 조준 중에는 라인 트레이스로 타겟 방향을 구한다.
그 방향을 기준으로 총을 들고 있는 본의 트랜스폼을 회전시킬 수 있도록 ADSWeaponScopeTransform을 계산한다.
이후 InverseScopeRelativeToHandR와 ADSWeaponScopeTransform을 조합해 ADSHandTransform을 만들고,
컨트롤 릭에서 hand_l, hand_r IK에 적용한다.
정리하면 TPS ADS는 다음 순서로 움직인다.
로딩 중...
FPS에서도 기본 흐름은 TPS와 비슷하다.
다만 라인 트레이스를 기준으로 월드 타겟을 맞추는 TPS와 달리, FPS에서는 카메라의 Forward Vector를 기준으로 조준 방향을 잡는다.
뷰 메쉬를 사용하는 FPS ADS에서는 TPS와 같은 방식으로 ADS 보정값과 무기 스코프 트랜스폼을 조합한다. TPS는 라인 트레이스로 타겟을 잡고, FPS는 카메라 전방 방향을 기준으로 계산한다.
로딩 중...
1인칭 팔 전용 메쉬를 사용하는 경우에는 조금 다르게 처리했다.
미리 조준 상태인 캐릭터에서 ADSVMOffset을 사전 계산하고, ADS 진입 시 FPSArms의 Relative Transform을 이 오프셋으로 보간한다.
Timeline을 사용해 일반 상태에서 조준 상태까지 자연스럽게 Lerp하면, 카메라 기준 팔 위치를 안정적으로 맞출 수 있다.
로딩 중...
전신 메쉬 기준으로 손 위치를 맞추면 캐릭터 애니메이션에는 자연스러울 수 있지만, 화면 중앙 조준에는 흔들림이 생길 수 있다. 그래서 FPS 팔 전용 메쉬는 카메라 하위에서 별도로 보정하고, 조준 상태로 들어갈 때 사전 계산된 오프셋을 사용했다.
ADS 상태가 너무 고정되어 있으면 조준은 정확해 보이지만 움직임이 딱딱하게 느껴진다. 그래서 카메라 입력과 캐릭터 이동에 따라 약간의 sway와 lag를 추가했다.
Translation lag는 Base Aim Rotation의 Pitch, Yaw 델타에서 구한다.
이 델타 값을 Spring Interpolation으로 부드럽게 만든 뒤, Visual Lag Root 씬 컴포넌트의 Relative Transform에 적용한다.
플레이어가 시점을 빠르게 움직이면 무기와 팔이 바로 따라붙지 않고 살짝 늦게 반응한다.
Rotation sway는 캐릭터의 현재 가속도와 전방 벡터를 사용했다.
CurrentAcceleration과 Forward Vector를 외적하고, 그 결과를 Up Vector와 내적해서 좌우 이동에 따른 Roll 값을 구한다.
이 값을 회전에 반영하면 캐릭터가 이동할 때 무기가 미세하게 기울어진다.
이 처리는 실제 조준 방향을 바꾸기 위한 로직이라기보다, 화면에서 느껴지는 무게감을 만들기 위한 시각 보정이다. 따라서 최종 발사 방향이나 판정 로직과 강하게 결합하지 않고, 별도의 시각 루트 컴포넌트에 적용하는 편이 다루기 쉽다.
Recoil은 무기 반동과 카메라 반동을 나눠 처리했다. 무기는 손 IK와 컨트롤 릭을 통해 반응하고, 카메라는 컨트롤 입력을 통해 플레이어 시야에 반동을 준다.
무기 반동은 무기 데이터 에셋에서 Roll, Pitch, Yaw의 최소/최대 값을 읽어 시작한다.
발사 시 각 축의 랜덤 값을 구하고, RInterpTo로 Target Recoil Rotate에 빠르게 수렴시킨다.
이 값은 recoil snapiness에 따라 얼마나 날카롭게 튀는지가 달라진다.
계산된 반동 회전은 컨트롤 릭에서 hand_l, hand_r의 컨트롤 트랜스폼에 반영한다.
이후 손 위치 IK가 적용되면서 무기와 손이 함께 움직인다.
카메라 recoil은 별도로 무기 데이터 에셋에 정의된 벡터 커브를 읽고, 해당 값을 Add Controller Input으로 적용했다.