[UE-5] TObjectPtr vs TSoftObjectPtr vs TWeakObjectPtr

‍박성령·2026년 2월 8일

언리얼엔진

목록 보기
4/4
post-thumbnail

TObjectPtr, TSoftObjectPtr, TWeakObjectPtr 모두 UObject를 참조하기 위한 스마트 포인터이다.

개발 중 위 스마트 포인터들의 차이점을 모르고 사용하다 몇 시간 동안 에러로 고생해서 글로 남깁니다. 😱😱


TObjectPtr

  • 에디터/런타임에서 이 포인터가 가리키는 에셋은 Reference Graph에 걸린다.

  • 그래서 액터가 로드될 때, 액터의 멤버변수로 TObjectPtr이 있다면 같이 로드된다.

  • 액터에 반드시 존재해야하는 필수적인 에셋을 관리한다.

TSoftObjectPtr

  • 내부적으로 에셋 경로(FSoftObjectPath)를 들고 있다. 때문에 세이브 파일/블루프린트/에디터 에셋 필드에 적합하다.

  • 액터가 로드되어도 직접 로드하기 전까지는 메모리에 올리지 않는다.

  • 메모리를 아끼고 필요할 때만 로드하여 메모리 절약 및 초기 로딩 최적화에 유리하다. 대신 로드 코드를 직접 작성해야한다.

  • 에셋이 아직 로드되어있지 않으면 .Get()을 사용해도 nullptr이 나온다.

  • 실제 객체가 필요할 때 LoadSynchronous()로 로드 후 Get()으로 가져와야 한다.

UPROPERTY(EditDefaultsOnly, Category="Input")
TSoftObjectPtr<UInputMappingContext> DefaultIMC;

UInputMappingContext* IMC = DefaultIMC.LoadSynchronous();

TWeakObjectPtr

  • 순환 참조 문제를 해결하기 위한 포인터

  • GC가 객체를 수거하면 자동으로 nullptr로 처리할 수 있다.


사례

개발하던 중 알 수 없는 오류가 발생했다.
캐릭터에 입력 기능을 만들고 이동을 테스트하는데 어떤 때는 움직이고 어떤 때는 움직이지 않았다.

이것 저것 살펴보던 중 에디터를 실행한 후 IMC 에셋을 로드한 상태에선 IMC가 캐릭터에 들어가서 입력이 되었으나 IMC 에셋을 로드하지 않은 상태에선 IMC가 들어가지 않는 것을 확인했다.

USTRUCT()
struct FInputMappingContextAndPriority
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, Category = "Input", meta = (AssetBundles = "Client,Server"))
	TObjectPtr<UInputMappingContext> InputMapping;

	// Higher priority input mappings will be prioritized over mappings with a lower priority.
	UPROPERTY(EditAnywhere, Category = "Input")
	int32 Priority = 0;
};

IMC와 Priority를 담은 구조체를 만들었고 여기서 IMC는 TSoftObject 스마트 포인터로 보관했다.

나중에 IMC를 가져올 때 그냥 Get으로 가져왔고 여기서 문제가 발생했다.

for (const FInputMappingContextAndPriority& Mapping : DefaultInputMappings)
{
	if (UInputMappingContext* IMC = Mapping.InputMapping.Get()) // 이 부분
	{
		if (UEnhancedInputUserSettings* Settings = Subsystem->GetUserSettings())
		{
			Settings->RegisterInputMappingContext(IMC);
		}

		Subsystem->AddMappingContext(IMC, Mapping.Priority);
	}
}

IMC를 TSoftObjectPtr로 관리하면서 IMC가 메모리에 없고 nullptr이 나오기 때문에 입력이 먹히지 않았다.

IMC 에셋을 연 이후에는 에셋을 열면서 강제로 로드되고 그제서야 동작이 되었던 것이다.

위 코드는 Lyra 예제에서 가져온건데 IMC를 TSoftObjectPtr로 관리한 이유는 여러 IMC를 전부 로드해서 메모리에 올리지 않고 필요할 때만 로드해서 적용하려는 목적인 것 같다.

아무튼 이를 해결하기 위해 LoadSynchronous() 함수를 추가하거나 그냥 TObjectPtr을 쓸 수도 있다.

profile
게임 개발을 좋아하는 개발자입니다.

0개의 댓글