조준점 UI가 생겼으니 해당 위치로 캐릭터가 조준하도록 하는 모션을 추가하겠습니다
어떻게 만들어야 할까
1. 각도를 계산해서 특정 본을 수동으로 변경해준다
2. 애니메이션 몽타주를 사용하고 각도값을 애니메이션 재생과 연결한다
3. 에임오프셋을 사용한다
1번의 경우 정석적이긴 하지만 엔진에서 기능 지원을 할 경우 이런 아트/기획 친화적이지 않은 방식의 기능은 자제해야 할 것 같습니다
2번의 경우 몽타주로 만들어 본 결과 다른 몽타주와 섞어서 복합적인 모션을 만들 때 레이어 관리 등 신경 써야 할 부분이 늘어납니다 블루 프린트로 제어하는 상황이라면 노드의 갯수가 늘어나서 가시성을 해치게 됩니다
그러므로 3번 에임 오프셋을 사용한 기능을 만들었습니다
에임 오프셋(Aim Offset)
에임오프셋은 일종의 블렌드 스페이스 기능입니다

에셋만들기
Pitch, Yaw axis에 대한 리소스가 다 있는 경우 Animation-AimOffset
Yaw axis의 리소스가 없는 걍우 Animation-regacy-AimOffset1D를 사용하면 됩니다
인자로 넣어줄 데이터의 축이 1개인지의 차이입니다

저는 Pitch에 대한 리소스밖에 없기 때문에 1D스페이스를 만들었습니다.
생성하고나면 어디서 많이 보던 애니메이션 블렌드 스페이스가 있습니다
AxisSetting에서 축 이름과 최소 최대값을 정하고 SmootingTime을 어느정도 주어서 각도가 갑자기 변해도 애니메이션이 튀지 않게 세팅해줍니다
애니메이션은 Additive세팅이 되어있는 포즈로 넣어주면 됩니다
하나의 애니메이션인 경우 복사해두고 특정 프레임마다 잘라서 사용하면 편하게 세팅이 가능합니다

적용하기

애니메이션그래프에서 만들어놓은 에셋을 캐시된 포즈를 베이스로 연결하고 각도값을 넣어주면 됩니다
각도값은 C++에서 연결했습니다
//엑터 Forward 벡터에서 Pitch값만 추출
const FRotator rotActor = GetActorForwardVector().Rotation();
const FRotator rotActor_Pitch(rotActor.Pitch, 0, 0);
const FVector vActorPitch = rotActor_Pitch.Vector();
//컨트롤 Pitch 벡터는 플레이어 컨트롤러의 로테이션값에서 추출했습니다
fAimAngle = FMath::RadiansToDegrees(FMath::Acos(vActorPitch.Dot(vControlPitch)));
if (vActorPitch.Cross(vControlPitch).Y < 0.f)
fAimAngle *= -1.f;
완성

(부록) TPS 조준에 대한 고찰
TPS 장르를 해보면서 조준에 대해서 불편했던 점을 마주할 수 있는데
내가 조준한 위치를 캐릭터가 공격하지 않는 현상입니다
3인칭 사격에 대한 문제점 분석
1. 조준의 주체(카메라)와 공격의 주체(캐릭터)의 위치가 다르다
2. 공격의 주체가 어느 각도로 사격을 해야 하는지 모호하다
카메라의 방향으로 사격을 하면 카메라 위치에 대한 고려가 안되어서 조준점과 다른 부분을 공격하는 경우가 발생합니다 대부분의 TPS 장르의 빗맞힘 문제가 이 경우에서 발생한다는 생각이 들어서 사격점을 보정해주는 기능을 추가했습니다

어떻게 만들어야 할까
1. 기본 사격 방향을 카메라 방향 * 공격 최대거리로 지정
2. 해당 목표로 카메라 기준 라인트레이싱
3. 라인트레이싱에 히트 되는 엑터가 있을 경우 목표점 보정
//카메라 컴포넌트포지션
FVector vecWorldLocation = FollowCamera->GetComponentLocation();
//카메라 로테이션
FVector vecCameraLook = Controller->GetControlRotation().Vector();
//타겟=포지션+방향*최대거리 (무기 별 최대거리 데이터 세팅해두고 활용하게 변경)
FVector vecTarget = vecWorldLocation + vecCameraLook * 3000.f;
FHitResult FHresult;
//콜리전파라미터에 자기자신 충돌무시
FCollisionQueryParams collisionParams;
collisionParams.AddIgnoredActor(this);
//라인 트레이싱
if (GetWorld()->LineTraceSingleByChannel(FHresult, vecWorldLocation, vecTarget, ECollisionChannel::ECC_OverlapAll_Deprecated, collisionParams))
vAimPosition = FHresult.Location;
else
vAimPosition = vecTarget;
목표물에 대한 포지션을 얻었으니 이를 기반으로 엑터 로테이션을 보정해주면 플레이어(카메라) 가 조준했던 조준점을 공격하는 능동 조준 기능을 만들 수 있습니다.

조준점 보정과 블렌드 스페이스, 애니메이션 몽타주를 다 섞으면 이런 다이브샷도 만들 수 있습니다
