언리얼 C++ 프로그래밍

[언리얼 C++] Asset 레퍼런스

언린이 2021. 6. 16. 21:31

언리얼 엔진 4에서 Asset 레퍼런스는 두 가지 방식으로 생각해 볼 수 있습니다.

하나는 오브젝트 A가 오브젝트 B를 참조하여 오브젝트 A 로드시 오브젝트 B도 함께 로드되도록 만드는 강참조이고, 다른 하나는 오브젝트 경로가 같은 문자열 형태의 간접 메커니즘을 통해 오브젝트 A가 오브젝트 B를 참조하게 만드는 약참조입니다.

 

 

1. 직접 프로퍼티 참조(강참조)

Asset 레퍼런스의 가장 흔한 경우로 UPROPERTY 매크로를 통해 노출됩니다. C++ 클래스에서 UPROPERTY로 지정한 변수를 선언하고, 해당 클래스를 상속 받는 블루프린트를 생성하면 블루프린트에서 UPROPERTY로 지정한 변수를 참조할 수 있습니다.

 

UPROPERTY(EditAnywhere, Category=Mesh)
UStaticMesh* StaticMesh;

 

이런식으로 EditAnywhere로 지정한 프로퍼티는 어디에서나 참조 및 수정이 가능합니다.

그래서 이런식으로 변수를 선언하면 블루프린트에서 해당 StaticMesh를 설정할 수 있으며 블루프린트가 로드될 때마다 참조된 StaticMesh 또한 자동으로 로드됩니다.

 

 

2. 생성 시간 참조(강참조)

생성 시간 참조는 주어진 프로퍼티에 대해 로드해야 하는 Asset을 프로그래머가 정확히 알고 있어, 그 프로퍼티를 오브젝트의 생성시 일부분으로 설정하는 경우입니다.

여기에는 ConstructorHelpers라는 특수 클래스가 사용되는데, 생성 단계 도중 오브젝트와 오브젝트의 클래스를 찾는 것입니다.

 

UPROPERTY()
class UTexture2D* Texture;

ASampleTest::ASampleTest()
{
    static ConstructorHelpers::FObjectFinder<UTexture2D> TextureObj(TEXT("/Game/UI/texture_test"));
	
    if (TextureObj.Succeeded())
    	Texture = TextureObj.Object;
}

 

위 생성자에서 ConstructorHelpers 클래스는 메모리에서 Asset을 찾아본 다음 로드합니다. 로드할 것을 지정하는데 Asset에 대한 전체 경로가 쓰인 것이 보입니다. Asset이 존재하지 않거나 오류로 인해 로드할 수 없는 경우 프로퍼티는 nullptr로 설정됩니다. 그러므로 디펜시브 코딩을 해주는 것이 좋습니다.

 

 

3. 간접 프로퍼티 참조(약참조)

Asset 로드 시점을 쉽게 제어할 수 있는 방법은 TSoftObjectPtr을 사용하는 것입니다.

참고로 TSoftObjectPtr을 사용하려면 Asset을 수동으로 로드해야 합니다. 템플릿으로 된 LoadObject<>() 메서드나 StaticLoadObject()나 FStreamingManager를 사용하여 오브젝트를 로드할 수 있습니다. 처음 두 메서드는 Asset을 동기식으로 로드하여 프레임 속도에 영향을 줄 수 있으니 게임플레이에 영향을 끼치지 않을 것이 확실한 것에만 사용하는 것이 좋습니다.

 

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Mesh)
TSoftObjectPtr<UStaticMesh> Mesh;

UStaticMesh* GetLoadedMesh()
{
    if (Mesh.IsPending())
    {
        const FSoftObjectPath& AssetRef = Mesh.ToStringReference();
        Mesh = Cast<UStaticMesh>(Streamable.SynchronousLoad(AssetRef));
    }
    
    return Mesh.Get();
}

 

위 코드는 UStaticMesh의 TSoftObjectPtr을 사용하여 런타임에 메쉬를 로드하고 있습니다.

 

 

4. 오브젝트 검색/로드(약참조)

오브젝트 검색과 로드는 런타임에 스트링을 만들어 그 오브젝트로의 레퍼런스를 구하는 방법입니다.

이미 생성 또는 로드된 UObject만 사용하려는 경우, FindObject<>()를 사용하며, 로드되어 있지 않은 오브젝트를 로드하려면 LoadObject<>()를 사용합니다. 두 함수의 사용 예제는 다음과 같습니다.

 

AFunctionalTest* TestToRun = FindObject<AFunctionalTest>(TestsOuter, *TestName);

GridTexture = LoadObject<UTexture2D>(NULL, TEXT("/Engine/EngineMaterials/DefaultWhiteGrid.DefaultWhiteGrid"), NULL, LOAD_None, NULL);