본문 바로가기
언리얼 C++ 게임 개발의 정석/14. 게임의 완성

14.1 게임 데이터의 저장과 로딩

by ftftgop3 2024. 5. 24.

플레이어의 데이터를 저장 하고 불러오는 기능 구현 방식

언리얼 오브젝트를 상속 받은 SaveGame 클래스 설계 

세이브 시스템에 SaveGame 데이터를 넘겨주면 저장 과 로딩을 쉽게 구현

에디터에서 저장 할 경우 Save 폴더에 있는 SaveGames 폴더에 게임 데이터가 저장

 

SaveGame 상속 받은 ABSaveGame 클래스 생성

각 저장 파일에 접근 할려면 고유 이름은 슬롯 이름 필요

기본 세이브 데이터를 생성 하는 로직으로 플레이어 스테이트 구현

UCLASS()
class ARENABATTLE_API UABSaveGame : public USaveGame
{
	GENERATED_BODY()
public:
	UABSaveGame();
	
	//레벨 저장 변수
	UPROPERTY()
		int32 Level;
	//경험치 저장 변수
	UPROPERTY()
		int32 Exp;
	//플레이어 이름 저장 변수
	UPROPERTY()
		FString PlayerName;
	//스코어 저장 변수
	UPROPERTY()
		int32 HighScore;
};
#include "ABSaveGame.h"

UABSaveGame::UABSaveGame()
{
	//각 변수 데이터 초기화
	Level = 1;
	Exp = 0;
	PlayerName = TEXT("Guest");
	HighScore = 0;
}

 

ABPlayState

ABSaveGame 사용 할 수 있게 slot name 지정

Slotname 으로 데이터 로드 후 저장된 값이 없다면 기본 데이터로 슬롯 초기화

UCLASS()
class ARENABATTLE_API AABPlayerState : public APlayerState
{
	GENERATED_BODY()
    public:
    
	//가장 높은 점수 리턴
	int32 GetGameHighScore() const;
	//저장할 슬롯 네임
	FString SaveSlotName;
    
    protected:
    //가장 높은 스코어
	UPROPERTY(Transient)
		int32 GameHighScore;
}

#include "ABSaveGame.h"
AABPlayerState::AABPlayerState()
{
	CharacterLevel = 1;
	GameScore = 0;
	Exp = 0;
	GameHighScore = 0;
	SaveSlotName = TEXT("Player1");
}


int32 AABPlayerState::GetGameHighScore() const
{
	return GameHighScore;
}

void AABPlayerState::InitPlayerData()
{
	//세이브 슬롯 네임에 해당하는 SaveGame 데이터 로드
	auto ABSaveGame = Cast<UABSaveGame>(UGameplayStatics::LoadGameFromSlot(SaveSlotName, 0));
	if (nullptr == ABSaveGame)
	{
		//만약 해당 슬롯이 없다면 기본 데이터로 초기화
		ABSaveGame = GetMutableDefault<UABSaveGame>();
	}

	SetPlayerName(ABSaveGame->PlayerName);
	SetCharacterLevel(ABSaveGame->Level);
	GameScore = 0;
	GameHighScore = ABSaveGame->HighScore;
	Exp = ABSaveGame->Exp;
}
void AABPlayerState::AddGameScore()
{
	//스코어 증가
	GameScore++;
	//최대 스코어 판단 후 갱신
	if (GameScore >= GameHighScore)
	{
		GameHighScore = GameScore;
	}

	//Playstate 업데이트
	OnPlayerStateChanged.Broadcast();
}

  

플레이어 데이터 변경 시 자동 저장 기능 구현

PlayerState

경험치 변동 시 데이터 저장 하는 로직

최초에 플레이어 데이터 로드 후 바로 저장 

UCLASS()
class ARENABATTLE_API AABPlayerState : public APlayerState
{
	GENERATED_BODY()
	
public:
	//player state 저장
	void SavePlayerData();
}

void AABPlayerState::InitPlayerData()
{
	//세이브 슬롯 네임에 해당하는 SaveGame 데이터 로드
	auto ABSaveGame = Cast<UABSaveGame>(UGameplayStatics::LoadGameFromSlot(SaveSlotName, 0));
	if (nullptr == ABSaveGame)
	{
		//만약 해당 슬롯이 없다면 기본 데이터로 초기화
		ABSaveGame = GetMutableDefault<UABSaveGame>();
	}

	SetPlayerName(ABSaveGame->PlayerName);
	SetCharacterLevel(ABSaveGame->Level);
	GameScore = 0;
	GameHighScore = ABSaveGame->HighScore;
	Exp = ABSaveGame->Exp;
	//데이터 로드 후 저장
	SavePlayerData();
}
bool AABPlayerState::AddExp(int32 IncomeExp)
{
	if (CurrentStatData->NextExp == -1)
		return false;
	//레벨업 판단
	bool DidLevelUp = false;
	//현재 경험치에 변수 값을 더하고
	Exp = Exp + IncomeExp;
	if (Exp >= CurrentStatData->NextExp)
	{
		//레벨 업 경험치를 넘어갈 경우 레벨 업 동작
		Exp -= CurrentStatData->NextExp;
		SetCharacterLevel(CharacterLevel + 1);
		DidLevelUp = true;
	}
	//플레이어 스테이트 변경 델리게이트 실행
	OnPlayerStateChanged.Broadcast();
	//경험치 변경으로 데이터 저장
	SavePlayerData();
	return DidLevelUp;
}
void AABPlayerState::AddGameScore()
{
	//스코어 증가
	GameScore++;
	//최대 스코어 판단 후 갱신
	if (GameScore >= GameHighScore)
	{
		GameHighScore = GameScore;
	}

	//Playstate 업데이트
	OnPlayerStateChanged.Broadcast();
	//스코어 변경으로 데이터 저장
	SavePlayerData();
}
void AABPlayerState::SavePlayerData()
{
	//게임 세이브 클래스 생성
	UABSaveGame* NewPlayerData = NewObject<UABSaveGame>();
	NewPlayerData->PlayerName = GetPlayerName();
	NewPlayerData->Level = CharacterLevel;
	NewPlayerData->Exp = Exp;
	NewPlayerData->HighScore = GameHighScore;
	
	//해당 SaveGame 을 slotname에 저장
	if (!UGameplayStatics::SaveGameToSlot(NewPlayerData, SaveSlotName, 0))
	{
		ABLOG(Error, TEXT("SaveGame Error!"));
	}
}

 

NewObject 

언리얼 오브젝트 생성 시 NewObject 명령을 사용 시

언리얼 실행 환경에서 가비지 컬렉션 자동으로 소멸

수동으로 delete 키워드를 사용 하지 않아도 된다

 

하이 스코어 값을 HUD UI 연동

void UABHUDWidget::UpdatePlayerState()
{
	//약 포인트 연결 확인
	ABCHECK(CurrentPlayerState.IsValid());
	//경험치 바 갱신
	ExpBar->SetPercent(CurrentPlayerState->GetExpRatio());

	//캐릭터 이름, 레벨, 스코어 갱신
	PlayerName->SetText(FText::FromString(CurrentPlayerState->GetPlayerName()));
	PlayerLevel->SetText(FText::FromString(FString::FromInt(CurrentPlayerState->GetCharacterLevel())));
	CurrentScore->SetText(FText::FromString(FString::FromInt(CurrentPlayerState->GetGameScore())));
	HighScore->SetText(FText::FromString(FString::FromInt(CurrentPlayerState->GetGameHighScore())));
}

 

저장 데이터 확인 

프로젝트의 Saved 폴더 내 SaveGames 폴더 에서 확인

파일명은 슬롯 이름과 동일