개요
GameFeature 를 사용하는 중에 AssetManager 와 스캔할 프라이머리 에셋 타입이라는 게 있어서, 공 부해보았다. 에셋 매니저를 사용하지 않아도, 게임은 만들 수 있다. 그럼 왜 필요한지 알아보자.
에셋 매니저를 사용하는 이유
1. 에셋 로드를 위해 경로를 직접 작성, 에디터에서 클래스 레퍼런스 연결하는 걸 막을 수 있다.
- 에셋 경로를 직접 작성하고 있는 경우
- 에디터에서 레퍼런스를 연결하는 경우
LoadObject("/Game/Characters/Hero/BP_Hero.BP_Hero");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ABSGameState") TMap<FGameplayTag, TSoftObjectPtr<const UBSCharacterDefinition>> AvailableCharacterDefinitionMap;
2. 원하는 에셋을 원하는 때에 로드, 언로드할 수 있다.
위로 얻는 효과는 프로그래머의 실수를 방지하고, 메모리를 효율적으로 사용할 수 있다.
생각보다 사용 방법은 쉬웠다. 왜 진작 사용하지 않았을까?에셋 매니저 사용 방법
- 에셋 매니저가 로드할 수 있는 에셋은 Primary Asset 뿐이다.
- 기본적으로 UWorld(레벨) 에셋이 유일한 PrimaryAsset 이며, 상속 후에 GetPrimaryAssetId 함수를 오버라이드 하면 PrimaryAsset 이 된다.
- PrimaryDataAsset 을 상속받은 BP 또는 DA 가 해당 된다.
- PrimaryAsset 은 이름 그대로 주요한 에셋으로, 직접 관리하고 싶은 최상위 에셋이다.
- PrimaryAsset 안에는 Secondary Asset 으로 구분된다.
- Primary 이 SecondaryAsset 참조하거나 사용하려는 경우 언리얼 엔진이 자동으로 세컨더리 에셋을 로드한다.
- 예제를 보면 UBSCharacterDefinition 은 PrimaryAsset, 멤버 변수는 SecondaryAsset 이다.
- 이제 PrimaryAssetID 로 경로나, 레퍼런스 필요 없이 검색하여 에셋을 가져올 수 있다.
class BISHOUJO_DOOM_API UBSCharacterDefinition : public UPrimaryDataAsset { GENERATED_BODY() public: UBSCharacterDefinition(); public: virtual FPrimaryAssetId GetPrimaryAssetId() const override { return FPrimaryAssetId("Character", CharacterTag.GetTagLeafName()); } UPROPERTY(EditDefaultsOnly, Category = "UBSCharacterDefinition") FGameplayTag CharacterTag; UPROPERTY(EditDefaultsOnly, Category = "UBSCharacterDefinition") TArray<FString> GameFeaturesToEnable; UPROPERTY(EditDefaultsOnly, Instanced, Category="UBSCharacterDefinition") TArray<TObjectPtr<UGameFeatureAction>> DefaultGameFeatureActions; UPROPERTY(EditDefaultsOnly, Category="UBSCharacterDefinition") TObjectPtr<const UBSPawnData> DefaultPawnData; UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UBSCharacterDefinition") TArray<TObjectPtr<UBSAbilitySet>> AbilitySets; UPROPERTY(EditdefaultsOnly, BlueprintReadOnly, Category = "UBSCharacterDefinition") TSoftClassPtr<USkeletalMesh> TestSkeletalMeshClass; };
- 프로젝트 세팅의 AssetManager 탭에서 스캔할 프라이머리 에셋 타입을 추가한다.
- 스캔이라는 건 프로그래머가 로드할 에셋을 찾기 위해, 프라이머리 에셋의 타입과 에셋의 위치와 용도를 가지고 목록을 만드는 것이다.
- 주의할 점은, 규칙의 쿠킹 관련된 옵션을 선택해주어야, 프로덕션과 개발 단계 둘 다 로드가 가능하다.
- AssetManager 를 사용해서 PrimaryAssetID 로 에셋을 검색해 로드할 수 있다.
- 주의할 점: 커스텀 에셋매니저를 사용하려면 프로젝트 세팅에서 설정해주어야 한다.
const FPrimaryAssetId CharacterDefID("Character", InTag.GetTagLeafName()); // 1. CharacterDefinition 비동기 로드 UBSAssetManager::Get().LoadCharacterDefinition(CharacterDefID, FStreamableDelegate::CreateLambda([this, CharacterDefID, InPlayerState, InTag]() { // 2. CharacterDefinition 비동기 로드 완료 const auto LoadedCharacterDef = UBSAssetManager::Get().GetPrimaryAssetObject(CharacterDefID); if (!LoadedCharacterDef) { UE_LOG(LogBS, Warning, TEXT("Invalid LoadedCharacterDef")); return; } }
void UBSAssetManager::LoadCharacterDefinition(const FPrimaryAssetId& InCharacterDefinitionId, const FStreamableDelegate& InLoadCompleteDelegate) { // 기본 에셋 경로 가져오기 const FSoftObjectPath AssetPath = GetPrimaryAssetPath(InCharacterDefinitionId); if (!AssetPath.IsValid()) { UE_LOG(LogTemp, Error, TEXT("Invalid asset path for: %s"), *InCharacterDefinitionId.ToString()); InLoadCompleteDelegate.ExecuteIfBound(); return; } // 로드할 에셋 목록 구성 TArray<FSoftObjectPath> AssetsToLoad; AssetsToLoad.Add(AssetPath); // 비동기 로딩 시작 LoadAssetList(AssetsToLoad, InLoadCompleteDelegate, FStreamableManager::AsyncLoadHighPriority); }
프라이머리 에셋 로드 시
- 세컨더리 에셋인 TSoftClassPtr 의 TestSkeletalMeshClass 는 자동으로 로드가 된다.
- 독립형 게임 모드에서도 확인 완료
- 자동으로 로드되지 않게 하려면, StreamableMananger 로 직접 로드해야 한다.
FStreamableManager& StreamableManager = UAssetManager::GetStreamableManager(); auto bLoadComplete = StreamableManager.IsAsyncLoadComplete(NewCharacterDef->TestSkeletalMeshClass.ToSoftObjectPath()); UE_LOG(LogBS, Log, TEXT("UBSCharacterDefManagerComponent::TestSkeletalMeshClass bLoadComplete = %d"), bLoadComplete);
- 세컨더리 에셋 안의 자식들도 자동으로 로드가 된다.
auto bLoadComplete = StreamableManager.IsAsyncLoadComplete(NewCharacterDef->TestSkeletalMeshClass.ToSoftObjectPath()); UE_LOG(LogBS, Log, TEXT("UBSCharacterDefManagerComponent::TestSkeletalMeshClass bLoadComplete = %d"), bLoadComplete); bLoadComplete = StreamableManager.IsAsyncLoadComplete(NewCharacterDef->DefaultPawnData->TestPawnMeshClass.ToSoftObjectPath()); UE_LOG(LogBS, Log, TEXT("UBSCharacterDefManagerComponent::TestPawnMeshClass bLoadComplete = %d"), bLoadComplete);
- 결론 : 프라이머리 에셋을 로드하면, 세컨더리 에셋들은 모두 자동으로 로드된다.
결과
- 프라이머리 에셋인 BSCharacterDefinition 을 AssetManager로 로드하는데 성공했으며, CharacterDefinition 의 CharacterTag 를 출력해서 확인하고 있다.