※ 개인적인 공부를 위해 포스팅 하는 글입니다.
# 에셋매니저 만들기
ㆍ 커스텀이 가능하다.
에셋매니저의 상속을 받는 클래스 생성
코드를 이렇게 구성해주자. 싱글톤으로 해준다
내가 만든 R1AssetManager를 프로젝트 세팅에서 바꿔준다.
※ 단순히 리소스를 불러오는것 뿐 아니라 필요한 부분만 로드해오기,, 바뀐 리소스만 가져오기 등,, 다양한 기능을 정의할 수 있는 클래스다.
#AssetData에서 데이터 캐싱 추가하기
ㆍ 데이터 캐싱이란 간단하게 동일한 데이터에 반복해서 접근해야 하거나 많은 연산이 필요한 일일때, 결과를 빠르게 이용하고자 성능이 좋은 혹은 가까운 곳에 저장하는 것
ㆍ 파일 패스와 라벨로 찾는다... 캐싱데이터를 기반으로 찾는다
void UR1AssetData::PreSave(FObjectPreSaveContext objectSaveContext)
{
Super::PreSave(objectSaveContext);
AssetNameToPath.Empty();
AssetLabelToSet.Empty();
//이거 클리어다
AssetGroupNameToSet.KeySort([](const FName& A, const FName& B)
{
return (A.Compare(B) < 0);
});
for (const auto& Pair : AssetGroupNameToSet)
{
const FAssetSet& AssetSet = Pair.Value;
for (FAssetEntry AssetEntry : AssetSet.AssetEntries)
{
FSoftObjectPath& AssetPath = AssetEntry.AssetPath;
/*const FString& AssetName = AssetPath.GetAssetName();
if (AssetName.StartsWith(TEXT("BP_")) || AssetName.StartsWith(TEXT("B_")) || AssetName.StartsWith(TEXT("WBP_")) ||
AssetName.StartsWith(TEXT("GE_")) || AssetName.StartsWith(TEXT("GA_")) || AssetName.StartsWith(TEXT("ABP_")))
{
FString AssetPathString = AssetPath.GetAssetPathString();
AssetPathString.Append(TEXT("_C"));
AssetPath = FSoftObjectPath(AssetPathString);
}*/
AssetNameToPath.Emplace(AssetEntry.AssetName, AssetEntry.AssetPath);
for (const FName& Label : AssetEntry.AssetLabels)
{
AssetLabelToSet.FindOrAdd(Label).AssetEntries.Emplace(AssetEntry);
}
}
}
}
//캐싱하는 작업. 디버그 돌려주면 AssetData를 저장할 때 이곳으로 들어온다
ㆍ 이렇게 작성해줬다.
저장버튼을 누를때 presave가 중단점에 걸린다.
# Asset Manager
코드가 참 길다... 사실 이걸 외우는건 말도안되는짓
필요할 때 찾아서 쓸 수 있는 능력을 길러야 한다. 해당 포스팅에선 코드만 첨부
.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "Data/R1AssetData.h"
#include "Engine/AssetManager.h"
#include "R1AssetManager.generated.h"
DECLARE_DELEGATE_TwoParams(FAsyncLoadCompletedDelegate, const FName&/*AssetName or Label*/, UObject*/*LoadedAsset*/);
class UR1AssetData;
/**
*
*/
UCLASS()
class MYPROJECT_API UR1AssetManager : public UAssetManager
{
GENERATED_BODY()
public:
UR1AssetManager();
static UR1AssetManager& Get();
public:
static void Initialize();
template<typename AssetType>
static AssetType* GetAssetByName(const FName& AssetName);
//동기로딩, 비동기로딩 (동기Sync와 비동기ASync)
static void LoadSyncByPath(const FSoftObjectPath& AssetPath);
static void LoadSyncByName(const FName& AssetName);
static void LoadSyncByLabel(const FName& Label);
static void LoadAsyncByPath(const FSoftObjectPath& AssetPath, FAsyncLoadCompletedDelegate CompletedDelegate = FAsyncLoadCompletedDelegate());
static void LoadAsyncByName(const FName& AssetName, FAsyncLoadCompletedDelegate CompletedDelegate = FAsyncLoadCompletedDelegate());
static void ReleaseByPath(const FSoftObjectPath& AssetPath);
static void ReleaseByName(const FName& AssetName);
static void ReleaseByLabel(const FName& Label);
static void ReleaseAll();
/*
동기와 비동기 차이
함수를 호출했을때 함수가 끝날때까지 기다릴건지 안기다릴건지
*/
//원하는 형태로 로딩하고 불러오고 하는 그런역할을 하는놈이 에셋메니저군
/*
어떻게든
*/
private:
void LoadPreloadAssets();
void AddLoadedAsset(const FName& AssetName, const UObject* Asset);
private:
UPROPERTY()
TObjectPtr<UR1AssetData> LoadedAssetData;
UPROPERTY()
TMap<FName, TObjectPtr<const UObject>> NameToLoadedAsset;
//FCriticalSection LoadedAssetsCritical;
};
template<typename AssetType>
AssetType* UR1AssetManager::GetAssetByName(const FName& AssetName)
{
UR1AssetData* AssetData = Get().LoadedAssetData;
check(AssetData);
AssetType* LoadedAsset = nullptr;
const FSoftObjectPath& AssetPath = AssetData->GetAssetPathByName(AssetName);
if (AssetPath.IsValid())
{
LoadedAsset = Cast<AssetType>(AssetPath.ResolveObject());
if (LoadedAsset == nullptr)
{
UE_LOG(LogTemp, Warning, TEXT("Attempted sync loading because asset hadn't loaded yet [%s]."), *AssetPath.ToString());
LoadedAsset = Cast<AssetType>(AssetPath.TryLoad());
}
}
return LoadedAsset;
}
//이름으로 에셋 타입을 가져와주세요
.cpp
#include "R1AssetManager.h"
UR1AssetManager::UR1AssetManager() : Super()
{
}
UR1AssetManager& UR1AssetManager::Get()
{
if (UR1AssetManager* Singleton = Cast<UR1AssetManager>(GEngine->AssetManager))
{
return *Singleton;
}
//일로 올리는없지만 혹시라도 올수도있으니 로그찍어주자
UE_LOG(LogTemp, Fatal, TEXT("Can't find UR1AssetManager"));
//형식을 맞추기위해 대충 리턴
return *NewObject<UR1AssetManager>();
}
void UR1AssetManager::Initialize()
{
//Todo Asset Load
//게임 인스턴스가 생성될때 해주는게 좋을듯? 글로가서 구성해보자
Get().LoadPreloadAssets();
}
void UR1AssetManager::LoadSyncByPath(const FSoftObjectPath& AssetPath)
{
if (AssetPath.IsValid())
{
UObject* LoadedAsset = AssetPath.ResolveObject();
if (LoadedAsset == nullptr)
{
if (UAssetManager::IsValid())
{
LoadedAsset = UAssetManager::GetStreamableManager().LoadSynchronous(AssetPath, false);
}
else
{
LoadedAsset = AssetPath.TryLoad();
}
}
if (LoadedAsset)
{
Get().AddLoadedAsset(AssetPath.GetAssetFName(), LoadedAsset);
}
else
{
UE_LOG(LogTemp, Fatal, TEXT("Failed to load asset [%s]"), *AssetPath.ToString());
}
}
}
void UR1AssetManager::LoadSyncByName(const FName& AssetName)
{
UR1AssetData* AssetData = Get().LoadedAssetData;
check(AssetData);
const FSoftObjectPath& AssetPath = AssetData->GetAssetPathByName(AssetName);
LoadSyncByPath(AssetPath);
}
void UR1AssetManager::AddLoadedAsset(const FName& AssetName, const UObject* Asset)
{
if (AssetName.IsValid() && Asset)
{
//FScopeLock LoadedAssetsLock(&LoadedAssetsCritical);
if (NameToLoadedAsset.Contains(AssetName) == false)
{
NameToLoadedAsset.Add(AssetName, Asset);
}
}
}
void UR1AssetManager::LoadSyncByLabel(const FName& Label)
{
if (UAssetManager::IsValid() == false)
{
UE_LOG(LogTemp, Error, TEXT("AssetManager must be initialized"));
return;
}
UR1AssetData* AssetData = Get().LoadedAssetData;
check(AssetData);
TArray<FSoftObjectPath> AssetPaths;
const FAssetSet& AssetSet = AssetData->GetAssetSetByLabel(Label);
for (const FAssetEntry& AssetEntry : AssetSet.AssetEntries)
{
const FSoftObjectPath& AssetPath = AssetEntry.AssetPath;
LoadSyncByPath(AssetPath);
if (AssetPath.IsValid())
{
AssetPaths.Emplace(AssetPath);
}
}
GetStreamableManager().RequestSyncLoad(AssetPaths);
for (const FAssetEntry& AssetEntry : AssetSet.AssetEntries)
{
const FSoftObjectPath& AssetPath = AssetEntry.AssetPath;
if (AssetPath.IsValid())
{
if (UObject* LoadedAsset = AssetPath.ResolveObject())
{
Get().AddLoadedAsset(AssetEntry.AssetName, LoadedAsset);
}
else
{
UE_LOG(LogTemp, Fatal, TEXT("Failed to load asset [%s]"), *AssetPath.ToString());
}
}
}
}
void UR1AssetManager::LoadAsyncByPath(const FSoftObjectPath& AssetPath, FAsyncLoadCompletedDelegate CompletedDelegate)
{
if (UAssetManager::IsValid() == false)
{
UE_LOG(LogTemp, Error, TEXT("AssetManager must be initialized"));
return;
}
if (AssetPath.IsValid())
{
if (UObject* LoadedAsset = AssetPath.ResolveObject())
{
Get().AddLoadedAsset(AssetPath.GetAssetFName(), LoadedAsset);
}
else
{
TArray<FSoftObjectPath> AssetPaths;
AssetPaths.Add(AssetPath);
TSharedPtr<FStreamableHandle> Handle = GetStreamableManager().RequestAsyncLoad(AssetPaths);
Handle->BindCompleteDelegate(FStreamableDelegate::CreateLambda([AssetName = AssetPath.GetAssetFName(), AssetPath, CompleteDelegate = MoveTemp(CompletedDelegate)]()
{
UObject* LoadedAsset = AssetPath.ResolveObject();
Get().AddLoadedAsset(AssetName, LoadedAsset);
if (CompleteDelegate.IsBound())
CompleteDelegate.Execute(AssetName, LoadedAsset);
}));
}
}
}
void UR1AssetManager::LoadAsyncByName(const FName& AssetName, FAsyncLoadCompletedDelegate CompletedDelegate)
{
if (UAssetManager::IsValid() == false)
{
UE_LOG(LogTemp, Error, TEXT("AssetManager must be initialized"));
return;
}
UR1AssetData* AssetData = Get().LoadedAssetData;
check(AssetData);
const FSoftObjectPath& AssetPath = AssetData->GetAssetPathByName(AssetName);
LoadAsyncByPath(AssetPath, CompletedDelegate);
}
void UR1AssetManager::ReleaseByPath(const FSoftObjectPath& AssetPath)
{
FName AssetName = AssetPath.GetAssetFName();
ReleaseByName(AssetName);
}
void UR1AssetManager::ReleaseByName(const FName& AssetName)
{
UR1AssetManager& AssetManager = Get();
if (AssetManager.NameToLoadedAsset.Contains(AssetName))
{
AssetManager.NameToLoadedAsset.Remove(AssetName);
}
else
{
UE_LOG(LogTemp, Log, TEXT("Can't find loaded asset by assetName [%s]."), *AssetName.ToString());
}
}
void UR1AssetManager::ReleaseByLabel(const FName& Label)
{
UR1AssetManager& AssetManager = Get();
UR1AssetData* LoadedAssetData = AssetManager.LoadedAssetData;
const FAssetSet& AssetSet = LoadedAssetData->GetAssetSetByLabel(Label);
for (const FAssetEntry& AssetEntry : AssetSet.AssetEntries)
{
const FName& AssetName = AssetEntry.AssetName;
if (AssetManager.NameToLoadedAsset.Contains(AssetName))
{
AssetManager.NameToLoadedAsset.Remove(AssetName);
}
else
{
UE_LOG(LogTemp, Log, TEXT("Can't find loaded asset by assetName [%s]."), *AssetName.ToString());
}
}
}
void UR1AssetManager::ReleaseAll()
{
UR1AssetManager& AssetManager = Get();
AssetManager.NameToLoadedAsset.Reset();
}
void UR1AssetManager::LoadPreloadAssets()
{
if (LoadedAssetData)
return;
UR1AssetData* AssetData = nullptr;
FPrimaryAssetType PrimaryAssetType(UR1AssetData::StaticClass()->GetFName());
TSharedPtr<FStreamableHandle> Handle = LoadPrimaryAssetsWithType(PrimaryAssetType);
if (Handle.IsValid())
{
Handle->WaitUntilComplete(0.f, false);
AssetData = Cast<UR1AssetData>(Handle->GetLoadedAsset());
}
if (AssetData)
{
LoadedAssetData = AssetData;
LoadSyncByLabel("Preload");
}
else
{
UE_LOG(LogTemp, Fatal, TEXT("Failed to load AssetData asset type [%s]."), *PrimaryAssetType.ToString());
}
}
찾아서 사용한 코드인데 언리얼 버전이 달라서 타고 들어가서 같은 기능을 한거 찾아줌,,, 버전마다 이름도 조금씩 다르니 주의할것
# 에셋 메니저를 GameInstance에 등록해줘 에디터가 실행될 때 같이 실행되도록 구조를 짜줬다.
에셋관리... 블루프린트를 만드는 방법도 있지만 이렇게 하는 방법도 있다.
플레이어 컨트롤러를 예시로 보자
ㆍ 주석 부분이 원래 하고있던 부분이였다. 하지만 이제 에셋메니저가 있으니 필요가 없어졌다.
ㆍ 이렇게 수정해줬다. 원래 되던 이동도 잘 되고, 데이터 관리 에셋관리도 더 깔끔해졌다.
ㆍ 게임플레이 태크( 이전포스팅에서 만든거) 를 이용해서 무브와 턴을 불러오고 액션을 바인드해주고있다.
'언리얼엔진 > 언리얼 C++' 카테고리의 다른 글
Player 키 추가 해보기 (0) | 2024.10.18 |
---|---|
비동기 로딩 AssetManager (1) | 2024.10.17 |
GamePlayTag와 Data Asset (1) | 2024.10.16 |
Character와 카메라가지고 장난쳐보기 (0) | 2024.10.14 |
Control Rotation (0) | 2024.10.14 |