인벤토리 시스템을 심화하여 구현하던 도중 만난 오류입니다. 객체를 관리하는 다른 경우에도 충분히 생길 수 있는 오류이기 때문에 정리해두려고 합니다.
우선 제가 하려고 했던 것은 플레이어가 특정 아이템을 습득할 때, 시스템이 해당 아이템 객체의 유효성을 확인하고 인벤토리에 추가하려고 했습니다. 그리고 위젯 컨트롤러에서 해당 데이터의 변화를 바인딩처리로 감지하고 있었습니다. 이때, 아이템 객체의 상태가 가비지 컬렉션 시스템에 의해 소멸된 경우나 아이템 객체에 접근할 수 없는 상태라면 문제가 발생할 수 있었습니다. 아이템 객체가 유효하지 않은데도 접근을 시도하면 런타임 오류나 크래시가 발생할 수 있습니다. 가비지 컬렉션 관련 에러는 특정 코드에 대해 문제가 생겼다고 알려주지않고 몇번 쓰레드에서 오류가 났다거나, 아니면 다음처럼 인덱스 오류가 났다고 나옵니다.
Assertion failed: IsValidIndex(Index) [File:D:\build\++UE5\Sync\Engine\Source\Runtime\CoreUObject\Public\UObject\UObjectArray.h] [Line: 511] IsValidIndex(25887068)
게임이 복잡해지고 많은 의존성이 생기게되면 어떤 코드에서 문제가 생겼는지 알기 어렵기 때문에 Garbage Collection error에 대해서 생각을 하고 코딩을 하는 습관을 들여야합니다.
/**
* Return the annotation associated with a uobject
*
* @param Object Object to return the annotation for
*/
FORCEINLINE TAnnotation GetAnnotation(const UObjectBase *Object)
{
check(Object);
return GetAnnotation(GUObjectArray.ObjectToIndex(Object));
}
다음의 부분에서 오류가 생겼습니다. TAnnotation이나 GetAnnotation은 구글링을 해보아도 정보가 잘 나오지 않습니다. 다음의 공식 사이트를 참고해보겠습니다.
참고자료
https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/CoreUObject/UObject/FUObjectAnnotationSparse
https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/CoreUObject/UObject/FUObjectAnnotationSparse/GetAnnotation
https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/CoreUObject/UObject/FUObjectAnnotationDense/GetAnnotation/1
TAnnotation은 특정 객체 타입에 추가 메타데이터를 연결하는 템플릿 클래스입니다. 이를 통해 객체에 부가 정보를 주석처럼 저장하고, Unreal Engine의 메모리 관리 시스템과 연동하여 자동으로 관리됩니다.
주요 특징
템플릿 클래스: 특정 타입에 대한 추가 정보를 저장할 수 있습니다.
객체에 부가 정보 저장: 기본 메타데이터 외에 추가 정보를 관리합니다.
자동 메모리 관리: 엔진의 메모리 관리 시스템과 연동되어 불필요한 메타데이터는 자동으로 정리됩니다.
GetAnnotation은 TAnnotation 객체에서 특정 객체에 부착된 주석이나 메타데이터를 가져오는 함수입니다. 이를 통해 특정 객체에 대해 추가로 저장된 데이터를 손쉽게 참조할 수 있습니다.
주요 특징
객체의 부가 정보 반환: TAnnotation에 저장된 주석 또는 메타데이터를 특정 객체에 대해 조회하고 가져올 수 있습니다.
유효성 검사: 해당 객체에 주석이 없는 경우, 기본값이나 nullptr을 반환하도록 구현할 수 있습니다.
간단한 예제로 이러한 상황을 만들어볼 수 있습니다.
void AFPS_TESTCharacter::BeginPlay()
{
Super::BeginPlay();
UObjectBase* InvalidObject = reinterpret_cast<UObjectBase*>(0x12345678);
int32 ObjectIndex = GUObjectArray.ObjectToIndex(InvalidObject);
UE_LOG(LogTemp, Log, TEXT("Object Index: %d"), ObjectIndex);
}
이 코드에서는 Unreal Engine이 GUObjectArray를 초기화하기 전에 ObjectToIndex
함수에 접근하면 초기화 문제로 인해 오류가 발생할 수 있습니다.
하나하나 따라가봅시다.
LOG_Dynamic과 LOG_Static에서 오류 메시지가 떠있는 걸 확인할 수 있습니다.
Dispatchbeginplay에서 문제가 났음을 보여줍니다.
NotifyBeginplay로 GameStateBase에서 오류가 났음을 알 수 있습니다.
UWorld에서는 StartPlay시에 문제가 생기고 있습니다. 즉 엔진은 정상적으로 시작되었으나 게임 플레이를 눌렀을때 문제가 생겼음을 알 수 있습니다.
익숙한 GameInstance도 보입니다.