KoreanFoodie's Study

언리얼 애셋 레퍼런스(Hard Reference, Soft Reference) 본문

Game Dev/Unreal C++ : Study

언리얼 애셋 레퍼런스(Hard Reference, Soft Reference)

GoldGiver 2022. 4. 4. 14:19

Hard Reference vs Soft Reference

A 가 B 를 하드 참조(Hard Reference) 하고 있다고 하면, A 가 로딩될때 B 도 로딩된다. 반면 A 가 B 를 약하게 참조(Soft Reference) 한다면, B 애셋의 경로 등을 string 의 형태로 갖고 있다는 뜻이다.

먼저 하드 참조부터 살펴보도록 하자.

 

 

Direct Property Reference (Hard Reference)

UPROPERTY 를 붙여 변수를 선언하면, 해당 변수(혹은 블루프린트)가 로드될 때 이에 대응하는 애셋 또한 로딩된다. 예시 코드를 보자.

/** construction start sound stinger */
UPROPERTY(EditDefaultsOnly, Category=Building)
USoundCue* ConstructionStartStinger;

 

 

Construction Time Reference (Hard Reference)

다음 예시는 생성자에서 ConstructorHelpers 클래스를 이용해 애셋을 로딩하는 예제이다. 추후 애셋을 바꾸지 않을 경우, static 키워드를 붙여준다.

/** gray health bar texture */
UPROPERTY()
class UTexture2D* BarFillTexture;

AStrategyHUD::AStrategyHUD(const FObjectInitializer& ObjectInitializer) :
    Super(ObjectInitializer)
{
    static ConstructorHelpers::FObjectFinder<UTexture2D> BarFillObj(TEXT("/Game/UI/HUD/BarFill"));
    assert(nullptr != BarFillObj);
    BarFillTexture = BarFillObj.Object;
}

만약 애셋을 찾지 못했을 경우 nullptr 를 리턴한다. assert 로 애셋이 제대로 로드되었는지 체크해주는 습관을 들이는 것이 좋다.

이제 약한 참조의 예시를 보자.

 

 

Indirect Property Reference

비동기 애셋 로딩에서 다루었던 것처럼, FSoftObjectPath 를 이용해 경로를 보관한다. 그 후 TSoftObjectPtr 를 사용해 템플릿화된 코드를 저장한다. 실제로 애셋을 로딩할 때는 LoadObject<>( ) 메서드를 호출해야 한다. 예제 코드를 보자.

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category=Building)
TSoftObjectPtr<UStaticMesh> BaseMesh;

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

IsPending 메소드를 사용하면 해당 애셋이 로딩되었는지 아닌지를 알 수 있다.

 

 

Find / Load Object

UPROPERTY 를 이용하지 않고, 애셋과 경로 정보를 가지고 런타임에서 직접 애셋을 로드할 수도 있다. 만약 이미 로드된 애셋(혹은 UObject) 을 찾는다면 FindObejct<>( ) 를 사용하면 된다. 만약 로드되지 않은 애셋을 로드하고 싶으면 LoadObject<>( ) 를 사용한다. (내부적으로 LoadObject 는 FindObject 와 같은 기능을 먼저 수행한다. 로드하려면 일단 찾아야 하니까!) 예제 코드를 보자.

AFunctionalTest* TestToRun = FindObject<AFunctionalTest>(TestsOuter, *TestName);
GridTexture = LoadObject<UTexture2D>(NULL, TEXT("/Engine/EngineMaterials/DefaultWhiteGrid.DefaultWhiteGrid"), NULL, LOAD_None, NULL);

 

타입에 맞게 로드를 해 보자.

DefaultPreviewPawnClass = LoadClass<APawn>(NULL, *PreviewPawnName, NULL, LOAD_None, NULL);


/* Above code is same as below codes */
DefaultPreviewPawnClass = LoadObject<UClass>(NULL, *PreviewPawnName, NULL, LOAD_None, NULL);

if (!DefaultPreviewPawnClass->IsChildOf(APawn::StaticClass()))
{
    DefaultPreviewPawnClass = nullptr;
}
Comments