멈추지 않고 끈질기게
[Unreal] 최종 프로젝트 19일차 이후 작업 본문
※ 해당 포스팅은 개인의 공부 정리용 글입니다. 틀린 내용이 있다면 추후 수정될 수 있습니다.
※ 해당 포스팅은 청년취업사관학교 교육 과정의 최종 프로젝트에 관한 내용을 포함하고 있습니다.
※ 해당 포스팅은 Unreal 5.4.1 버전을 기준으로 작성되었습니다.
0. 서론
18일차 이후로 에셋 변경 작업에 많을 시간을 소요했고, 이후로는 최종 발표 및 엑스포 전시가 코앞이라 작업에 집중하느라 포스팅을 까먹고 말았습니다. 해서 19일차 이후로의 작업을 간단하게 정리하여 포스팅합니다. 로직쪽으로 크게 추가된 내용은 많지 않고 에셋 적용 및 QA가 대부분이라 추가 작업분만 올리고, QA 과정에서 나온 이슈 중 크리티컬해 보이는 내용은 별도의 포스팅으로 등록하겠습니다. 원래 회고까지 한번에 올릴까 했는데, 엑스포 전시까지 포함하여 올릴 내용이 좀 있어보여서 별도의 포스팅으로 등록하고자 합습니다.
1. 스켈레탈 메시 교체 및 애니메이션
우선 NPC들의 스켈레탈 메시는 마켓플레이스에서 구매한 Scanned 3D People 에셋을 사용하였습니다.
또한 기존의 언리얼 5의 기본 스켈레탈에 맞춰 import 한 애니메이션들을 해당 NPC들의 스켈레탈에 맞게 리타게팅 하였습니다. 해당 에셋의 스켈레탈 메시들이 기본적으로 언리얼 4의 기본 스켈레탈 기반으로 되어있어서, 5.4.1의 편리해진 리타게팅 기능으로 쉽게 바꿀 수 있었습니다.
2. 시작 화면 및 튜토리얼 합치기
다른 팀원분께서 시작 화면 및 튜토리얼을 별도의 레벨로 제작하여 합치려 했으나, 레벨 이동 후에 크래시가 발생하는 이슈가 있었습니다. 해당 부분이 발표때까지 해결되지 않아, 아예 시작화면 UI 및 튜토리얼을 본 게임이 시작되는 레벨에 합치는 작업을 진행하였습니다.
우선 기존에는 NPCController의 BeginPlay()에서 바로 BehaviorTree(이하 BT) 관련 데이터를 초기화하는 로직을 실행하였으나, 해당 부분을 public 함수로 외부에서 호출하도록 변경하였습니다. 그리고 게임모드에 본 게임을 시작할 때 실행해야 할 내용들을 별도의 함수로 선언하였습니다.
// NPCBase
void ANPCBase::RunAI()
{
if (NPCController)
{
NPCController->RunAI();
NPCController->SetHomeLoc(HomeLoc);
}
}
그리고 튜토리얼 맵에 존재하던 모든 액터들을 본 게임 맵에 복사해두고, 튜토리얼 종료 지점 혹은 튜토리얼 스킵 시 바로 제거하도록 하였습니다. 해당 액터들은 튜토리얼 작업하신 분께서 미리 Tutorial 이라는 이름으로 Tag를 추가해두셔서, GetAllActorsWithTag를 통해 탐색하여 제거하였습니다.
// GameMode
void AFarmLifeGameMode::StartFarmLife()
{
if(StartUI->IsInViewport())
{
StartScreenOff();
}
if(TutorialUI)
{
TutorialUI->RemoveFromParent();
}
if(GuideUI)
{
GuideUI->RemoveFromParent();
}
// Change Input Mode
FTimerHandle TempHandle;
GetWorldTimerManager().SetTimer(TempHandle, this, &AFarmLifeGameMode::ChangeInputMode_Game, 0.1f, false);
// Start Time Flow
GetWorldTimerManager().SetTimer(TimerHandle, this, &AFarmLifeGameMode::UpdateMinutes, TimeUpdateInterval, true, TimeUpdateInterval);
TimerUI->SetVisibility(ESlateVisibility::Visible);
// NPC AI Start
for (ANPCBase* NPC : NPCArray)
{
NPC->RunAI();
}
// Delete Tutorial Objects
TArray<AActor*> TutorialActors;
UGameplayStatics::GetAllActorsWithTag(GetWorld(), TEXT("Tutorial"), TutorialActors);
for (AActor* TA : TutorialActors)
{
TA->Destroy();
}
// Activate GroundActors
TArray<AActor*> GroundActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AGroundActor::StaticClass(), GroundActors);
for (AActor* GA : GroundActors)
{
GA->SetActorEnableCollision(true);
GA->SetActorHiddenInGame(false);
}
// Delegate
if (OnFarmLifeStart.IsBound())
{
OnFarmLifeStart.Broadcast();
}
}
다만 해당 과정에서 ConstructorHelpers::FClassFinder 함수와 관련하여 언리얼 엔진이 켜지지 않는 치명적인 이슈를 겪었는데, 해당 이슈에 관한 내용은 별도의 포스팅으로 등록하였습니다.
https://sam0308.tistory.com/118
3. 레벨 최적화
맵에 본격적으로 시각적인 품질 개선에 들어가기 시작하면서, 프레임이 체감이 될 정도로 떨어지는 문제가 발생하였습니다. 특히나 나무가 많은 구역을 지나갈 때에는 프레임이 20대까지 떨어지는 모습을 확인했습니다. 레벨 담당자 분이 따로 텍스쳐 크기 조정등의 작업을 하고 계시긴 했으나, 프로젝트 전체의 퀄리티에 관한 문제라 저도 해결하기 위해 원인을 찾아보고 있었습니다.처음에는 폴리지 액터들이 머티리얼 인스턴스가 아닌 원본을 사용하는게 아닌가 의심했으나, 거의 모두 머티리얼 인스턴스를 사용하고 있었습니다.
좀 더 근본적인 원인을 찾기 위해 언리얼 엔진에서의 프로파일링 방법을 찾다가 언리얼 인사이트에 대해 알게 되었습니다. 가장 프레임 드랍이 심한 부분에서 녹화해서 확인해 본 결과, 그림자가 큰 비중을 차지하고 있음을 확인할 수 있었습니다.
사실 아직 인사이트의 상세한 내용들을 전부 알지는 못하나, 일단 ShadowDepths 라는 항목이 큰 비중을 차지하고 있다는 점이 확인되었습니다. 바로 레벨의 폴리지 액터들의 그림자를 전부 꺼 본 결과 60 프레임이 유지되었습니다. 다만 시각적인 품질이 티가 날 정도로 많이 떨어지기는 해서, 해당 내용을 레벨 담당자 분에게 전달드리면서 시각적 품질에 영향을 가장 크게 주는 그림자만 살리는 방향으로 요청드렸습니다. 추가로 발표 직전에는 Build Lighting 및 Build Paths를 통해 사전에 연산 해놓을 수 있는 요소들을 Baking 하여 최대화면 기준으로 50프레임 대를 유지할 수 있도록 했습니다.
'포트폴리오' 카테고리의 다른 글
[포트폴리오] 언리얼 팀 프로젝트 회고(3차) (0) | 2024.07.04 |
---|---|
[포트폴리오] 언리얼 팀 프로젝트 회고(2차) (0) | 2024.07.04 |
[Unreal] 최종 프로젝트 18일차 - 감정표현 SFX 추가 / NPC별 애니메이션 세부화 (0) | 2024.06.10 |
[Unreal] 최종 프로젝트 17일차 - 감정표현 UI 수정 / 채팅 개선 / BT 이슈 수정 (1) | 2024.06.07 |
[Unreal] 최종 프로젝트 16일차 - NPC 감정표현 추가 / 선물 주기 로직 추가 (0) | 2024.06.04 |