오늘은 룬 시스템에 프리셋 기능을 추가하기 위한 기초 작업을 완료했음
기존에는 직업별로 하나의 룬 배치만 저장할 수 있었는데, 사용자들이 여러 개의 빌드를 저장하고 전환하고 싶다는 요청이 많아서 프리셋 시스템을 도입하기로 결정
이번 단계에서는 데이터 구조 변경과 저장/로드 로직을 새로운 프리셋 구조에 맞게 전면 개편했음
기존 방식의 문제:
기존 데이터 구조
// 삭제된 구조체
USTRUCT(BlueprintType)
struct FRunePlacementData
{
GENERATED_BODY()
UPROPERTY()
TArray<FPlacedRuneInfo> PlacedRunes;
};
// SaveGame에서 사용
TMap<ECharacterClass, FRunePlacementData> SavedRunesByClass;
핵심 아이디어: "각 직업마다 3개의 프리셋 슬롯 제공"
새로운 구조체로 프리셋 시스템 구현:
USTRUCT(Atomic, BlueprintType)
struct FArcaneBoardPresets
{
GENERATED_BODY()
UPROPERTY()
TArray<FPlacedRuneInfo> Preset1;
UPROPERTY()
TArray<FPlacedRuneInfo> Preset2;
UPROPERTY()
TArray<FPlacedRuneInfo> Preset3;
UPROPERTY()
int32 LastUsedPresetIndex;
FArcaneBoardPresets()
{
LastUsedPresetIndex = 1; // 기본값은 프리셋 1
}
};
SaveGame 구조 변경
// GS_ArcaneBoardSaveGame.h에서
TMap<ECharacterClass, FArcaneBoardPresets> SavedRunesByClass; // 구조 변경
기존 함수 시그니처 변경
// 기존
void SaveBoardConfig();
void LoadBoardConfig();
// 새로운 - 프리셋 인덱스 매개변수 추가
void SaveBoardConfig(int32 PresetIndex = -1); // -1은 현재 프리셋에 저장
void LoadBoardConfig(int32 PresetIndex = -1); // -1은 최근 사용 프리셋 로드
새로운 프리셋 관련 함수들 추가
// UI에서 필요한 최소한의 함수들만 추가
bool IsPresetEmpty(int32 PresetIndex) const;
int32 GetCurrentPresetIndex() const;
// 현재 프리셋 추적용 변수
int32 CurrentPresetIndex;
GetPresetArray 함수의 중복
// 초기에는 두 개 함수가 필요한지 의문이었음
TArray<FPlacedRuneInfo>* GetPresetArray(FArcaneBoardPresets& Presets, int32 PresetIndex);
const TArray<FPlacedRuneInfo>* GetPresetArray(const FArcaneBoardPresets& Presets, int32 PresetIndex) const;
표준 C++ const 오버로딩 패턴으로 해결
// const 버전에 실제 로직 구현
const TArray<FPlacedRuneInfo>* UGS_ArcaneBoardLPS::GetPresetArray(
const FArcaneBoardPresets& Presets, int32 PresetIndex) const
{
switch (PresetIndex)
{
case 1: return &Presets.Preset1;
case 2: return &Presets.Preset2;
case 3: return &Presets.Preset3;
default: return nullptr;
}
}
// non-const 버전은 const 버전을 호출
TArray<FPlacedRuneInfo>* UGS_ArcaneBoardLPS::GetPresetArray(
FArcaneBoardPresets& Presets, int32 PresetIndex)
{
return const_cast<TArray<FPlacedRuneInfo>*>(
static_cast<const UGS_ArcaneBoardLPS*>(this)->GetPresetArray(
static_cast<const FArcaneBoardPresets&>(Presets), PresetIndex));
}
코드 중복을 방지하면서도 const 안전성을 보장하는 표준적인 방식을 적용했음
새로운 저장 로직
void UGS_ArcaneBoardLPS::SaveBoardConfig(int32 PresetIndex)
{
// PresetIndex가 -1이면 현재 프리셋에 저장
int32 TargetPresetIndex = (PresetIndex == -1) ? CurrentPresetIndex : PresetIndex;
// 세이브 파일 로드 또는 생성
UGS_ArcaneBoardSaveGame* SaveGameInstance = GetOrCreateSaveGame();
// 현재 클래스의 프리셋 구조체 가져오기
FArcaneBoardPresets& ClassPresets = SaveGameInstance->SavedRunesByClass[CurrentClass];
// 해당 프리셋에 현재 룬 배치 저장
TArray<FPlacedRuneInfo>* TargetPreset = GetPresetArray(ClassPresets, TargetPresetIndex);
*TargetPreset = BoardManager->PlacedRunes;
// 최근 사용 프리셋 업데이트
ClassPresets.LastUsedPresetIndex = TargetPresetIndex;
CurrentPresetIndex = TargetPresetIndex;
}
새로운 로드 로직
void UGS_ArcaneBoardLPS::LoadBoardConfig(int32 PresetIndex)
{
// 로드할 프리셋 인덱스 결정
int32 TargetPresetIndex;
if (PresetIndex == -1)
{
// 최근 사용 프리셋 로드 (기본값은 1)
TargetPresetIndex = ClassPresets.LastUsedPresetIndex;
}
else
{
TargetPresetIndex = PresetIndex;
}
CurrentPresetIndex = TargetPresetIndex;
// 해당 프리셋 로드
const TArray<FPlacedRuneInfo>* TargetPreset = GetPresetArray(ClassPresets, TargetPresetIndex);
BoardManager->LoadSavedData(CurrentClass, *TargetPreset);
}
빈 프리셋 체크 로직
bool UGS_ArcaneBoardLPS::IsPresetEmpty(int32 PresetIndex) const
{
// 세이브 파일이 없거나 해당 프리셋에 룬이 없으면 빈 프리셋
const TArray<FPlacedRuneInfo>* TargetPreset = GetPresetArray(ClassPresets, PresetIndex);
return !TargetPreset || TargetPreset->Num() == 0;
}
UI에서 빈 프리셋 슬롯을 다르게 표시하기 위한 함수
완료 항목
남은 항목