11.21 - TIL

김혁·2025년 11월 21일

TIL

목록 보기
62/84

팀 프로젝트 진행 내용

1. 메타휴먼 캐릭터 깨지는 현상 수정

(1) 오류

  • 기존에 플레이어 캐릭터는 UEFN_Mannequin을 스켈레탈 메시로 설정이 되어있고, 해당 메시에 뮤터블을 통해 메타휴먼 데이터를 붙이는 구조로 되어있었다.
  • 그러다보니 UEFN_Mannequin 본 구조를 그대로 따라가는데, 메타휴먼 데이터를 붙이니 머리가 크고 팔다리가 두꺼워지는 현상이 있었다.
  • 메타휴먼용 스켈레탈 메시를 사용하게 되면, 기존의 애니메이션 데이터는 전부 UEFN_Mannequin을 통해 맞춰져 있기 때문에 모든 애니메이션을 리타겟팅해야 하는 번거로운 작업이 필요했다.

(2) 해결 과정

  • 게임 애니메이션 샘플 프로젝트(GASP)를 보면 되게 다양한 캐릭터를 리타겟팅을 통해 자연스러운 애니메이션을 보여준다. 해당 프로젝트를 참고해서 UEFN_Mannequin을 메인 스켈레탈 메시로 가지는 캐릭터를 만들고, 해당 캐릭터를 리타겟팅만 해서 메타휴먼 메시를 사용해서 원하는 스켈레탈 메시 사용과 기존의 애니메이션 동작을 부드럽게 진행되게끔 하고자 했다.
  • 먼저 메타휴먼을 위한 스켈레탈 메시를 통해 IK Rig 파일을 먼저 생성해줬다.
  • 후에 Auto Create IK를 통해 IK를 생성해주고, Auto Create Retarget Chains를 통해 Chain을 생성해줬다.
  • UEFN_Mannequin에 존재하는 IK Retargeting Chains와 이름이 동일하게 설정을 해주었고, 발톱이나 손의 경우에는 이름이 다른 경우도 있어서 해당 체인을 추가해주거나 수정해줬다.
  • 또한, 파쿠르 동작을 위해 스켈레탈 메시 자체에 attach라는 본을 추가해서 해당 부분도 Chain을 생성해줬다.
  • 다음으로 IK Retargeter 파일을 만들고, IK Rig을 할당해줬다.
  • Op Stack 창에서 Retarget Op를 추가해줬다. 해당 동작은 정확히 어떤 동작을 수행하는지는 모르지만, 각 Chain 정보에 따라 리타겟팅을 실행시켜주고, IK Rig도 일치시켜주고, Curve 데이터도 다시 연결해주는 것으로 보인다.
  • 다음으로는 BP_PlayerCharacter를 상속받는 블루프린트 클래스를 만들고, 기존의 메시는 Visible을 꺼줘서 UEFN_Mannequin은 안 보이게 해줬다. 다음으로는 해당 스켈레탈 메시 밑에 메타휴먼용 스켈레탈 메시를 할당해서 애니메이션을 따라가게끔 지정해줬다. 그 밑에는 기존의 메타휴먼용 데이터를 붙이는 로직을 추가했다.
  • 다음 로직들은 ABP_GenericRetarget에 있는 것이다. 빨간 주석을 보면 알다시피 해당 과정을 하기 위해서는 스켈레탈 메시에 Component Tag로 리타겟팅할 에셋의 Name을 가져와서 Map에서 찾고, 해당 리타겟팅 에셋을 통해 애니메이션 리타겟팅하는 것을 알 수 있다.
  • 해당 ABP에서 Map으로 IK Retargeter 정보를 가지고 있고, 이를 통해 리타겟팅이 실행된다는 것을 알 수 있다.
  • 위에서 복잡한 노드들이 있는데, 해당 과정은 Retarget Profile을 설정하는 과정으로 보인다.
  • 자세하게는 동작을 모르겠지만, 리타겟팅 정보를 받아오는 것으로 보인다.
  • 마지막으로 리타겟팅 에셋과 해당 프로필을 통해 출력해주는 애님 그래프 과정이다.
  • 위의 과정을 통해 BP_PlayerCharacter에는 정확히 기능만 구현된 캐릭터가 있게 되었고, 스켈레탈 메시의 변경이 필요하면 리타겟팅을 통해 부드러운 애니메이션을 실행시켜줄 수 있게 되었다.

참고 : 공식 홈페이지 - 게임 애니메이션 샘플 프로젝트
https://dev.epicgames.com/documentation/ko-kr/unreal-engine/game-animation-sample-project-in-unreal-engine


2. 파쿠르 애니메이션 벽 관통하는 버그 수정

(1) 오류

  • 위와 같이 벽을 향해 일정 시간동안 이동 후에 파쿠르를 실행하는 애니메이션의 경우에 벽 근처에서 파쿠르를 실행하면, 벽을 통과해서 이동을 일정 시간하고 파쿠르를 실행하기 위해 뒤로 텔포를 해서 파쿠르 동작을 실행하는 버그가 발생했다.

(2) 해결 과정

  • 위의 사진은 해당 애님 몽타주의 프레임 단위별로 실행하게 되는 동작들이다.
  • 처음에는 모션 워핑 과정에서 벽을 인식하고 손을 짚게 되는데, 해당 과정이 너무 늦으니까 벽을 인식하지 않고, 즉 워핑을 하지 않고 벽을 통과하는 게 되고, 워핑 단계가 되면 텔포가 되는 게 아닐까라는 생각을 했다.
  • 그래서 아래와 같이 수정을 해서 테스트를 해보았는데, 동일하게 작동을 했다.
  • 그렇다면 이 몽타주가 선택되는 시점이 너무 빨라서 모든 애니메이션을 실행시키려고 하는게 아닐까라는 생각을 했다.
  • 몽타주를 선택하는 시점은 PoseSearchBranchIn 노티파이 스테이트가 실행되는 시점이다. 먼저 장애물의 정보에 따라서 1차적으로 선택할 수 있는 파쿠르 애니메이션을 거르고, 여기서의 Trajectory를 확인하고 모션 매치 함수를 통해 골라서 실행이 되게 된다.
  • 그렇다면 PoseSearchBranchIn 노티파이 스테이트가 너무 초반부터 있어서 걸어가는 동작에서 확인을 하고 이 시점이 가장 맞다고 판단을 해서 앞에서부터 실행되는데, 해당 부분에는 모션 워핑이 없으니까 벽을 통과해서 애니메이션대로 진행되고 모션 워핑을 실행하면 뒤로 텔포를 해서 파쿠르 액션을 실행시키지 않을까라는 생각을 했다.
  • 따라서 위와 같이 PoseSearchBranchIn 노티파이 스테이트 시점을 가장 뒤에 3프레임만 할당을 했다. 이정도의 정보로도 충분히 선택할 수 있고, 해당 시점에서 바로 실행되게끔 하고자 했다.
  • 해당 동작을 통해 애님 몽타주가 실행되는 시점을 늦춰서 어색하게 이동하는 부분을 해결했다.

3. 절벽에서 발 구르는 버그 수정

(1) 오류

  • 위와 같이 절벽에만 가면 캐릭터가 움직이는 버그가 있었다.
  • 모션 매칭의 조건이 잘못되었는지 절벽에 있을 때는 공중에 있다고 생각을 하게 되나라고 의심을 했다.

(2) 해결 과정

  • 먼저 Rewind Debugger를 통해서 해당 동작이 있을 경우에는 어떤 PSD를 선택하게 되는지 확인해보았다.

  • 확인해보니, 저러한 현상이 나타날 때, PSD_Run에서 애니메이션을 찾는다는 사실을 알게 되었다. 예측하기로는 저 상황에서는 움직이지 않고 있으니 PSD_Idle 중에서 선택을 해야 된다.
  • 다음으로 해당 상황에서 왜 저런 선택을 하는지 Chooser Table을 확인해보았다.

  • 확인해보니 해당 상황에서 Movement State를 Idle이 아닌, Moving으로 인식을 해서 Run에 관련된 PSD가 선택된다는 것을 알게 되었다.
  • 따라서 Movement State를 업데이트하는 곳을 찾아보았다.
// AO_PlayerCharacter_AnimInstance.cpp

void UAO_PlayerCharacter_AnimInstance::UpdateStates()
{
	...

	// Update MovementState (Moving, Idle)
	MovementStateLastFrame = MovementState;
	if (IsMoving())
	{
		MovementState = EMovementState::Moving;
	}
	else
	{
		MovementState = EMovementState::Idle;
	}
    
    ...
}
  • 해당 내용을 확인해보니 매 Tick마다 IsMoving()에 따라서 상태를 업데이트하고 있었다.
  • 그럼 IsMoving()에 오류가 있지 않을까라고 기대를 했다.
// AO_PlayerCharacter_AnimInstance.cpp

bool UAO_PlayerCharacter_AnimInstance::IsMoving() 
{
	if (TrjFutureVelocity.IsNearlyZero(10.f) && Acceleration.IsNearlyZero())
	{
		return false;
	}
	
	return true;
}
  • 해당 로직을 보니, 가속도가 0이거나, 미래 궤적 속력이 0이면 false가 나오게끔 되어있어야 하는데, AND 로직을 통해 되어있었다.
  • 해당 로직을 올바르고 조금 더 직관적이게, 아래와 같이 수정했다.
bool UAO_PlayerCharacter_AnimInstance::IsMoving() 
{
	if (!TrjFutureVelocity.IsNearlyZero(10.f) && !Acceleration.IsNearlyZero())
	{
		return true;
	}
	
	return false;
}
  • 해당 내용을 수정하니 공중에서 발을 구르는 버그는 해결됐다.
profile
게임 개발자를 향해..

0개의 댓글