UEFN_Mannequin을 스켈레탈 메시로 설정이 되어있고, 해당 메시에 뮤터블을 통해 메타휴먼 데이터를 붙이는 구조로 되어있었다.
UEFN_Mannequin 본 구조를 그대로 따라가는데, 메타휴먼 데이터를 붙이니 머리가 크고 팔다리가 두꺼워지는 현상이 있었다.UEFN_Mannequin을 통해 맞춰져 있기 때문에 모든 애니메이션을 리타겟팅해야 하는 번거로운 작업이 필요했다.UEFN_Mannequin을 메인 스켈레탈 메시로 가지는 캐릭터를 만들고, 해당 캐릭터를 리타겟팅만 해서 메타휴먼 메시를 사용해서 원하는 스켈레탈 메시 사용과 기존의 애니메이션 동작을 부드럽게 진행되게끔 하고자 했다.
UEFN_Mannequin에 존재하는 IK Retargeting Chains와 이름이 동일하게 설정을 해주었고, 발톱이나 손의 경우에는 이름이 다른 경우도 있어서 해당 체인을 추가해주거나 수정해줬다.attach라는 본을 추가해서 해당 부분도 Chain을 생성해줬다.

UEFN_Mannequin은 안 보이게 해줬다. 다음으로는 해당 스켈레탈 메시 밑에 메타휴먼용 스켈레탈 메시를 할당해서 애니메이션을 따라가게끔 지정해줬다. 그 밑에는 기존의 메타휴먼용 데이터를 붙이는 로직을 추가했다.

ABP_GenericRetarget에 있는 것이다. 빨간 주석을 보면 알다시피 해당 과정을 하기 위해서는 스켈레탈 메시에 Component Tag로 리타겟팅할 에셋의 Name을 가져와서 Map에서 찾고, 해당 리타겟팅 에셋을 통해 애니메이션 리타겟팅하는 것을 알 수 있다.


BP_PlayerCharacter에는 정확히 기능만 구현된 캐릭터가 있게 되었고, 스켈레탈 메시의 변경이 필요하면 리타겟팅을 통해 부드러운 애니메이션을 실행시켜줄 수 있게 되었다.참고 : 공식 홈페이지 - 게임 애니메이션 샘플 프로젝트
https://dev.epicgames.com/documentation/ko-kr/unreal-engine/game-animation-sample-project-in-unreal-engine



PoseSearchBranchIn 노티파이 스테이트가 실행되는 시점이다. 먼저 장애물의 정보에 따라서 1차적으로 선택할 수 있는 파쿠르 애니메이션을 거르고, 여기서의 Trajectory를 확인하고 모션 매치 함수를 통해 골라서 실행이 되게 된다.PoseSearchBranchIn 노티파이 스테이트가 너무 초반부터 있어서 걸어가는 동작에서 확인을 하고 이 시점이 가장 맞다고 판단을 해서 앞에서부터 실행되는데, 해당 부분에는 모션 워핑이 없으니까 벽을 통과해서 애니메이션대로 진행되고 모션 워핑을 실행하면 뒤로 텔포를 해서 파쿠르 액션을 실행시키지 않을까라는 생각을 했다.
PoseSearchBranchIn 노티파이 스테이트 시점을 가장 뒤에 3프레임만 할당을 했다. 이정도의 정보로도 충분히 선택할 수 있고, 해당 시점에서 바로 실행되게끔 하고자 했다.


// AO_PlayerCharacter_AnimInstance.cpp
void UAO_PlayerCharacter_AnimInstance::UpdateStates()
{
...
// Update MovementState (Moving, Idle)
MovementStateLastFrame = MovementState;
if (IsMoving())
{
MovementState = EMovementState::Moving;
}
else
{
MovementState = EMovementState::Idle;
}
...
}
IsMoving()에 따라서 상태를 업데이트하고 있었다.IsMoving()에 오류가 있지 않을까라고 기대를 했다.// AO_PlayerCharacter_AnimInstance.cpp
bool UAO_PlayerCharacter_AnimInstance::IsMoving()
{
if (TrjFutureVelocity.IsNearlyZero(10.f) && Acceleration.IsNearlyZero())
{
return false;
}
return true;
}
bool UAO_PlayerCharacter_AnimInstance::IsMoving()
{
if (!TrjFutureVelocity.IsNearlyZero(10.f) && !Acceleration.IsNearlyZero())
{
return true;
}
return false;
}