모든 데이터에 대한 요청을 처리하는 녀석
Initialize시 모든 데이터 테이블을 불러와서 캐시해 놓는다. (비동기)
GameInstanceSubsystem을 상속받았기에 GameInstance와 유사한 수명을 갖게 된다.
동기 로드 (Synchronous Loading)
동기 로드는 리소스를 즉시 메모리에 로드하는 방식입니다. 로드가 완료될 때까지 프로그램의 실행이 일시 중지되며, 로드가 끝나면 다음 작업을 수행합니다.
특징:
- 즉시 실행: 리소스 로드가 완료될 때까지 기다리며, 로드가 완료되면 바로 다음 코드를 실행합니다.
- 간단한 코드: 구현이 간단하고, 로드 순서나 타이밍을 제어하기 쉽습니다.
장점:
- 간단하고 직관적: 복잡한 비동기 처리 없이, 리소스가 필요한 시점에 바로 로드할 수 있습니다.
- 디버깅 용이: 동기적인 실행 흐름으로 인해 디버깅이 상대적으로 쉬운 편입니다.
단점:
- 프레임 정지(Stall): 대용량 리소스를 로드할 때 프로그램이 일시적으로 멈출 수 있습니다. 이는 게임 플레이 도중 심각한 끊김 현상을 유발할 수 있습니다.
- 사용자 경험 저하: 긴 로드 시간은 사용자 경험을 저하시킬 수 있습니다.
비동기 로드 (Asynchronous Loading)
비동기 로드는 리소스를 백그라운드에서 로드하는 방식입니다. 로드가 완료될 때까지 메인 실행 흐름을 중단하지 않으며, 로드가 완료되면 알림을 받거나 콜백 함수를 통해 처리합니다.
특징:
- 백그라운드 로드: 메인 스레드가 아닌 별도의 스레드에서 리소스를 로드하여 프로그램 실행을 방해하지 않습니다.
- 콜백 또는 이벤트 사용: 로드 완료 시점을 알리기 위해 콜백 함수나 이벤트를 사용합니다.
장점:
- 부드러운 사용자 경험: 백그라운드에서 로드가 진행되기 때문에, 게임 플레이 도중에도 끊김 없이 부드러운 경험을 제공합니다.
- 효율적인 리소스 사용: 필요할 때만 리소스를 로드하여 메모리 사용을 최적화할 수 있습니다.
단점:
- 복잡한 구현: 동기 로드에 비해 구현이 복잡하며, 로드 완료 시점에 대한 추가적인 관리가 필요합니다.
- 동기화 문제: 비동기 로드의 특성상, 리소스가 필요할 때 아직 로드가 완료되지 않았을 수 있는 문제를 처리해야 합니다.
Q . 동기, 비동기 로딩중 왜 비동기를 활용했는지?
A . RPG게임 특성상 많은 양의 데이터가 존재합니다. 많은 데이터를 한 번에 로딩을 하면 게임이 멈추는 느낌을 주고 이는 부정적인 유저 경험을 제공하기 때문에 동기 방식이 아닌 비동기 방식을 사용했습니다. 물론 지금은 데이터가 적기 때문에 동기 로딩도 문제가 되지는 않습니다.
Q . 그럼 데이터를 필요할 때 로드하지 않고, 처음에 로드하려고 하는 이유는?
A . 플레이 중에 발생하는 히칭 현상을 부정적인 유저 경험을 제공합니다. 또한 RPG게임은 특성상 데이터가 많아서 로딩이 많이 발생합니다. 유저마다 다른 코스튬, 무기, 아이템 등등. 런타임 중에 로딩해야 할 데이터가 많기 때문에 자주 사용하는 데이터 테이블을 처음에 로드해서 캐시하는 방식을 사용했습니다.
Q . 그럼 모든 데이터를 처음에 로딩해서 메모리에 올려놓으면 되는거 아닌지?
A . 모든 데이터를 처음에 로딩을 하게 되면 너무 긴 로딩시간을 요구할 수 있습니다. 이는 유저들에게 부정적인 유저 경험을 제공합니다. 또한 유저마다 PC사양이 다르기 때문에 최소 사양에 맞춰서 메모리를 사용해야 합니다.
Q . 64bit 운영체제에서 가상 메모리를 쓰면 무한의 가까운 메모리를 사용할 수 있는거 아닌지?
A . 무한의 가까운 메모리를 사용할 수 있지만, 가상 메모리는 디스크를 활용하기 때문에 I/O가 매우 느립니다. 이는 퍼포먼스 저하를 발생시킬 수 있습니다.
/////////////////////////
SHDataManagerSubsystem.h
/////////////////////////
UCLASS()
class SHPROJECT_API USHDataManagerSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection);
public:
UPROPERTY()
TMap<FName, TObjectPtr<UDataTable>> LoadedDataTables;
};
/////////////////////////
SHDataManagerSubsystem.cpp
/////////////////////////
void USHDataManagerSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
FString Path = TEXT("/Game/DataTable");
UObjectLibrary* ObjectLibrary = UObjectLibrary::CreateLibrary(UDataTable::StaticClass(), false, true);
if (ObjectLibrary == nullptr)
return;
ObjectLibrary->LoadAssetDataFromPath(Path);
TArray<FAssetData> AssetDataList;
ObjectLibrary->GetAssetDataList(AssetDataList);
for (const FAssetData& AssetData : AssetDataList)
{
TWeakObjectPtr<USHDataManagerSubsystem> WeakThis = MakeWeakObjectPtr(this);
USHAssetManager::GetStreamableManager().RequestAsyncLoad(AssetData.GetSoftObjectPath(), [WeakThis, AssetData]()
{
UDataTable* DataTable = Cast<UDataTable>(AssetData.GetSoftObjectPath().TryLoad());
if (DataTable == nullptr)
return;
if (WeakThis.IsValid() == false)
return;
FString AssetName = DataTable->GetName();
WeakThis->LoadedDataTables.Add(*AssetName, DataTable);
});
}
}
'Unreal Project' 카테고리의 다른 글
Control Rig을 활용한 캐릭터 다리 조절 (1) | 2024.09.23 |
---|---|
Material을 활용한 캐릭터 Outline 그리기 (1) | 2024.08.13 |
SHSkillEffectBase (0) | 2024.06.11 |
SHDamageModifierBase (0) | 2024.06.10 |
SHCombatSystemComponent (0) | 2024.06.06 |