게임데이터 관리
Unreal 이론2024. 11. 6. 13:10
[이득우의 언리얼 프로그래밍 Part2 수업의 정리]
사전 준비
CSV 파일로 게임의 설정 데이터를 저장해 언리얼 엔진에서 Import 한다.
프로젝트 폴더에 GameData 폴더를 만들고, 엑셀 파일을 UTF-8의 csv 파일로 저장
위의 CSV 파일을 사용하기 위해 인덱스 이름과 동일한 이름을 가진 구조체를 생성
-> 소스파일 폴더에서 구조체 헤더파일 작성 -> Generated project file 실행해 헤더파일을 프로젝트에 추가
엑셀 데이터의 임포트
- DataAsset과 유사하게 FTableRowBase를 상속받은 구조체를 선언
- 엑셀의 Name 컬럼 (얘가 key값이 된다)을 제외한 컬럼과 동일하게 UPROPERTY 속성 선언
- 엑셀 데이터를 csv로 익스포트 한 후 언리얼 엔진에 임포트
데이터를 관리할 싱글톤 클래스의 설정
- 언리얼 엔진에서 제공하는 싱글톤 클래스
- 게임 인스턴스
- 에셋 매니저
- 게임 플레이 관련 액터 (GameMode, GameState)
- 프로젝트에 싱글톤으로 등록한 언리얼 오브젝트
- 언리얼 오브젝트 생성자에서 사용하지 않도록 주의
- 위의 사진에 싱글톤 클래스를 설정하면 엔진이 초기화 될 때 자동으로 GEngine이라는 전역변수에 싱글톤을 만들어줌
// ABGameSingleton.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "ABCharacterStat.h"
#include "ABGameSingleton.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogABGameSingleton, Error, All);
/**
*
*/
UCLASS()
class ARENABATTLE_API UABGameSingleton : public UObject
{
GENERATED_BODY()
public:
UABGameSingleton();
static UABGameSingleton& Get();
// Character Stat Data Section
public:
FORCEINLINE FABCharacterStat GetCharacterStat(int32 InLevel) const { return CharacterStatTable.IsValidIndex(InLevel) ? CharacterStatTable[InLevel] : FABCharacterStat(); }
UPROPERTY()
int32 CharacterMaxLevel;
private:
// Stat Data 내부 보관용 배열 - 추후 필요한 객체들에게 이 배열로 뿌림
TArray<FABCharacterStat> CharacterStatTable;
};
// ABGameSingleton.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "GameData/ABGameSingleton.h"
DEFINE_LOG_CATEGORY(LogABGameSingleton);
UABGameSingleton::UABGameSingleton()
{
// UDataTable -> (Key, Value)의 한쌍으로 이루어짐
static ConstructorHelpers::FObjectFinder<UDataTable> DataTableRef(TEXT("/Script/Engine.DataTable'/Game/ArenaBattle/GameData/ABCharacterStatTable.ABCharacterStatTable'"));
if (nullptr != DataTableRef.Object)
{
const UDataTable* DataTable = DataTableRef.Object;
check(DataTable->GetRowMap().Num() > 0);
TArray<uint8*> ValueArray;
DataTable->GetRowMap().GenerateValueArray(ValueArray);
// Algo의 Transform 알고리즘으로 value값만 가져오도록 설계
Algo::Transform(ValueArray, CharacterStatTable,
[](uint8* Value)
{
return *reinterpret_cast<FABCharacterStat*>(Value);
}
);
}
CharacterMaxLevel = CharacterStatTable.Num();
ensure(CharacterMaxLevel > 0);
}
UABGameSingleton& UABGameSingleton::Get()
{
// 엔진 초기화시 바로 활성화 되기 때문에 Get 함수로 받을 수 없다면 잘못된것이므로 CastCheck로 형변환 됬는지 강력히 검사
UABGameSingleton* Singleton = CastChecked<UABGameSingleton>(GEngine->GameSingleton);
if (Singleton) {
return *Singleton;
}
UE_LOG(LogABGameSingleton, Error, TEXT("Invalid Game Singleton"));
// 코드 흐름을 위해 작성한것일 뿐 실제 여기까지 가는 일이 일어나지 않도록 앞에서 철저히 검사
return *NewObject<UABGameSingleton>();
}
- 싱글톤 클래스의 로직. 싱글톤 클래스가 엔진 초기화시 제대로 생성되었는지 검사하며, 요청한 객체가 잘 쓸수 있도록 생성자에서 DataTable의 value들을 가져온다.
프로젝트의 주요 레이어
- 게임 레이어: 기믹과 NPC
- 미들웨어 레이어: 캐릭터의 스탯 컴포넌트, 아이템 박스
- 데이터 레이어: 스탯 데이터, 데이터 관리를 위한 싱글톤 클래스
캐릭터 스탯 시스템
- 위의 구조대로 Stat Component가 Base Stat과 무기의 능력치에 따른 modifier Stat을 합산하여 반영하도록 ABCharacterStatComponent의 구조를 변경, 아래의 함수와 변수를 추가
void SetLevelStat(int32 InNewLevel);
FORCEINLINE float GetCurrentLevel() const { return CurrentLevel; }
FORCEINLINE void SetModifierStat(const FABCharacterStat& InModifierStat) { ModifierStat = InModifierStat; }
FORCEINLINE FABCharacterStat GetTotalStat() const { return BaseStat + ModifierStat; }
UPROPERTY(Transient, VisibleInstanceOnly, Category = Stat)
float CurrentLevel;
UPROPERTY(Transient, VisibleInstanceOnly, Category = Stat, Meta = (AllowPrivateAccess = "true"))
FABCharacterStat BaseStat;
UPROPERTY(Transient, VisibleInstanceOnly, Category = Stat, Meta = (AllowPrivateAccess = "true"))
FABCharacterStat ModifierStat;
- 위의 구조 변경에 따라 CharacterBase 파일도 Stat의 값을 참조하도록 적절하게 변경
- 기믹 클래스에서 스테이지가 상승할때 마다 NPC 레벨이 증가해 더 강해진 NPC를 스폰하도록 설정
- 이를 위해 스테이지 번호를 지정하고, 이 번호의 증가에 NPC의 레벨을 연동. 이를 위해 CharacterBase에 SetLevel 함수 선언
- 근데 이렇게 해서 테스트 하다보면 2스테이지부터 MaxHp와 CurrentHp가 안맞는 현상 발생
액터의 생성과 지연 생성의 프로세스
- 액터의 BeginPlay에서 CurrentHp값과 MaxHp값이 세팅이 되지만, 기믹 함수에서는 이미 SpawnActor를 실행하고 SetLevel 함수가 실행되기 때문에 CurrentHp값이 반영안되는 문제 발생
- SpawnActor 대신 SpawnActorDeferred 함수를 사용하면 FinishSpawning 함수를 마지막에 호출해 액터의 BeginPlay가 실행된 후 최종적으로 스폰처리가 완료된다
- 스테이지 스폰과 NPC 스폰, 상자 스폰을 모두 Deferred로 변경해준다.
- 캐릭터가 무기를 습득했을 때 무기스탯에 따라 최종 스탯을 얻는 구조 추가
- Weapon 클래스에 ModifierStat 변수를 추가해서, CharacterBase의 equip weapon 함수에서 Stat이 WeaponItemData의 ModifierStat을 얻도록 로직 추가
- NPC 캐릭터가 스폰될 때 각기 다른 캐릭터로 스폰되도록 설정 추가
- 프로젝트 폴더의 Config->DefaultArenaBattle.ini 파일 추가.
- AABCharacterNonPlayer에 Config파일을 가져와 메쉬를 로딩하도록 로직 추가
// ABCharacterNonPlayer.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Character/ABCharacterBase.h"
#include "Engine/StreamableManager.h"
#include "ABCharacterNonPlayer.generated.h"
/**
*
*/
UCLASS(config = ArenaBattle)
class ARENABATTLE_API AABCharacterNonPlayer : public AABCharacterBase
{
GENERATED_BODY()
public:
AABCharacterNonPlayer();
protected:
virtual void PostInitializeComponents() override;
protected:
void SetDead() override;
void NPCMeshLoadCompleted();
// 비동기 방식으로 로드
UPROPERTY(config)
TArray<FSoftObjectPath> NPCMeshes;
TSharedPtr<FStreamableHandle> NPCMeshHandle;
};
// ABCharacterNonPlayer.cpp
#include "Character/ABCharacterNonPlayer.h"
#include "Engine/AssetManager.h"
AABCharacterNonPlayer::AABCharacterNonPlayer()
{
GetMesh()->SetHiddenInGame(true);
}
void AABCharacterNonPlayer::PostInitializeComponents()
{
Super::PostInitializeComponents();
ensure(NPCMeshes.Num() > 0);
int32 RandIndex = FMath::RandRange(0, NPCMeshes.Num() - 1);
NPCMeshHandle = UAssetManager::Get().GetStreamableManager().RequestAsyncLoad(NPCMeshes[RandIndex], FStreamableDelegate::CreateUObject(this, &AABCharacterNonPlayer::NPCMeshLoadCompleted));
}
void AABCharacterNonPlayer::NPCMeshLoadCompleted()
{
if (NPCMeshHandle.IsValid())
{
USkeletalMesh* NPCMesh = Cast<USkeletalMesh>(NPCMeshHandle->GetLoadedAsset());
if (NPCMesh)
{
GetMesh()->SetSkeletalMesh(NPCMesh);
GetMesh()->SetHiddenInGame(false);
}
}
NPCMeshHandle->ReleaseHandle();
}
- 이후 실행해보면 메쉬 로딩까지 NPC가 숨겨져있다가 랜덤한 캐릭터 메쉬로 잘 로딩되는것 확인 가능
'Unreal 이론' 카테고리의 다른 글
인공지능 - 행동트리 모델의 구현 (0) | 2024.11.11 |
---|---|
인공지능 - 행동트리 모델의 이해 (2) | 2024.11.10 |
무한 맵의 제작 (0) | 2024.10.31 |
아이템 시스템 (1) | 2024.10.30 |
캐릭터 스탯과 위젯 (1) | 2024.10.27 |
댓글()