Unreal - 세이브 게임

2025. 12. 2. 21:21·Unreal 프로젝트 다이어리/두번째 프로젝트

구현내용

게임중 진행되는 세이브되어야하는 데이터들의 값 저장

 

구현목적

언리얼에서는 게임을 실행할 때마다 이전 인스턴스가 모두 초기화되기 때문에, 별도의 조치를 취하지 않으면 게임 데이터가 저장되지 않습니다 인벤토리, 스탯 등등 여러가지를 구현하다보니 값의 저장이 필요하게 되어 구현하였습니다

 

사용클래스

USaveFile 실제 저장되는 데이터를 담는 컨테이너 (USaveGame)
UProjectGameInstance 게임이 실행되는동안 유지되는 전역 데이터 저장기능 인스턴스
(GameInstance)
SaveManager 세이브 / 로드 로직을 관리하는 매니저
ProjectGameInstance가 소유하며 저장 / 로그 함수 구현

 

구현

 

SaveGame 클래스를 추가해줍니다

 

저장해야하는값들을 저장합니다

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/SaveGame.h"
#include "SaveFile.generated.h"

USTRUCT(BlueprintType)
struct FSaveItemData
{
	GENERATED_BODY()

	UPROPERTY()
	FString itemID;

	UPROPERTY()
	int32 itemCount;
};

UCLASS()
class PORTFOLIOMS_API USaveFile : public USaveGame
{
	GENERATED_BODY()

public:

	//캐릭 위치, 방향
	UPROPERTY()
	FVector playerLoc;

	UPROPERTY()
	FRotator playerRot;

	//캐릭 스탯
	UPROPERTY()
	float currentHp;

	UPROPERTY()
	float maxHp;

	UPROPERTY()
	float currentPosture;

	UPROPERTY()
	float currentExp;

	UPROPERTY()
	int32 playerLevel;

	//크리티컬 & 공격속도
	UPROPERTY()
	float currentCriticalRate;

	UPROPERTY()
	float currentAttackSpeed;

	//스탯어빌리티
	UPROPERTY()
	int32 currentStatPoint;
    .... 이하생략

 

 

세이브 매니저의 구현

UCLASS()
class PORTFOLIOMS_API USaveManager : public UObject
{
	GENERATED_BODY()

public:
	UPROPERTY()
	USaveFile* currentSave;

	UFUNCTION()
	bool SaveGame();

	UFUNCTION()
	bool LoadGame();

	UFUNCTION()
	void NewGame();

	UFUNCTION()
	void Initalize();

	UPROPERTY()
	UDataTable* characterBaseTable;

public:
	USaveFile* GetCurrentSaveFile() const { return currentSave; }
private:
	FString saveSlotName = TEXT("SaveFile");
	uint32 userIdx = 0;
	
};

 

bool USaveManager::SaveGame()
{
	if (!currentSave)
		return false;

	return UGameplayStatics::SaveGameToSlot(currentSave, saveSlotName, userIdx);
}

bool USaveManager::LoadGame()
{
	if (UGameplayStatics::DoesSaveGameExist(saveSlotName, userIdx))
	{
		currentSave = Cast<USaveFile>(UGameplayStatics::LoadGameFromSlot(saveSlotName, userIdx));
		return currentSave != nullptr;
	}
	return false;
}

void USaveManager::NewGame()
{
	currentSave = Cast<USaveFile>(UGameplayStatics::CreateSaveGameObject(USaveFile::StaticClass()));

	if (!characterBaseTable)
		return;

	const FString ContextString(TEXT("Character Base Stat"));
	FCharacterBaseStat* baseStat = characterBaseTable->FindRow<FCharacterBaseStat>(FName("BaseStat"), ContextString);
	if (!baseStat)
		return;

	//기본값
	currentSave->gold = 0;
	currentSave->silver = 0;
	currentSave->currentHp = baseStat->baseHp;
	currentSave->currentPosture = 0;
	currentSave->playerLevel = 1;
	currentSave->inventoryItems.Empty();
}

void USaveManager::Initalize()
{
	if (LoadGame())
		return;

	NewGame();
}

 

해당코드로 외부에 세이브파일을 만들게됩니다

 UGameplayStatics::SaveGameToSlot(currentSave, saveSlotName, userIdx);

 

 

해당 스탯컴포넌트에서 불러오기 를 구현합니다

//불러오기
void UStatComponent::InitStats(USaveFile* saveData)
{
	if (!characterBaseTable)
		return;

	const FString ContextString(TEXT("Character Base Stats"));
	FCharacterBaseStat* baseStat = characterBaseTable->FindRow<FCharacterBaseStat>(FName("BaseStat"), ContextString);

	cachedBaseStat = *baseStat;
	if (saveData)
	{
		level = saveData->playerLevel;
		currentExp = saveData->currentExp;
		CalcExpNextLev();
		currentCriticalRate = saveData->currentCriticalRate;
		currentAttackSpeed = saveData->currentAttackSpeed;
		currentStatPoint = saveData->currentStatPoint;
		totalStatPoint = saveData->totalStatPoint;
		currentAbilityPoint = saveData->currentAbilityPoint;
		totalAbilityPoint = saveData->totalAbilityPoint;
		statUpgradeStrength = saveData->statUpgradeStrength;
		statUpgradeSpeed = saveData->statUpgradeSpeed;
		statUpgradePower = saveData->statUpgradePower;
		statUpgradeCritical = saveData->statUpgradeCritical;
		tempStatUpgradeStrength = saveData->tempStatUpgradeStrength;
		tempStatUpgradeSpeed = saveData->tempStatUpgradeSpeed;
		tempStatUpgradePower = saveData->tempStatUpgradePower;
		tempStatUpgradeCritical = saveData->tempStatUpgradeCritical;
		tempNeedLacrimaCount = saveData->tempNeedLacrimaCount;
		tempCurrentStatPoint = saveData->tempCurrentStatPoint;
		currentLacrima = saveData->currentLacrima;
		needUpgradeLacrimaCount = saveData->needUpgradeLacrimaCount;
		needUpgradeLacrimaCount = 300;
	}
	else
	{
		needUpgradeLacrimaCount = 300;
		level = 1;
	}
	ApplyLevelStat();
	ApplySavedStatBonus();

	//풀피로 세팅
	currentHp = stats[EBaseStatType::Hp].EntryTotal();
	
	//UI업뎃
	UpdateCurrentStats();
	onLevelChanged.Broadcast(level);
	onExpChanged.Broadcast(currentExp, expNextLevel, level);
}

 

BeginPlay에서 저장 데이터를 임시로 담을 포인터로 선언하여 게임 시작시

저장 데이터를 불러와 캐릭터 상태를 초기화해줍니다

void UStatComponent::BeginPlay()
{
	Super::BeginPlay();

	USaveFile* saveData = nullptr;
	if (UProjectGameInstance* gameIst = Cast<UProjectGameInstance>(GetWorld()->GetGameInstance()))
	{
		if (gameIst->saveManager)
			saveData = gameIst->saveManager->GetCurrentSaveFile();
	}
	InitStats(saveData);
}

 

저장하기를 구현해줍니다

해당 함수는 저장해야할때 호출하면됩니다

//save 저장하기
void UStatComponent::SaveStat(USaveFile* saveData)
{
	if (!saveData)
		return;

	saveData->playerLevel = level;
	saveData->currentHp = currentHp;
	saveData->currentExp = currentExp;
	saveData->currentCriticalRate = currentCriticalRate;
	saveData->currentAttackSpeed = currentAttackSpeed;
	saveData->currentStatPoint = currentStatPoint;
	saveData->totalStatPoint = totalStatPoint;
	saveData->currentAbilityPoint = currentAbilityPoint;
	saveData->totalAbilityPoint = totalAbilityPoint;
	saveData->statUpgradeStrength = statUpgradeStrength;
	saveData->statUpgradeSpeed = statUpgradeSpeed;
	saveData->statUpgradePower = statUpgradePower;
	saveData->statUpgradeCritical = statUpgradeCritical;
	saveData->tempStatUpgradeStrength = tempStatUpgradeStrength;
	saveData->tempStatUpgradeSpeed = tempStatUpgradeSpeed;
	saveData->tempStatUpgradePower = tempStatUpgradePower;
	saveData->tempStatUpgradeCritical = tempStatUpgradeCritical;
	saveData->tempNeedLacrimaCount = tempNeedLacrimaCount;
	saveData->tempCurrentStatPoint = tempCurrentStatPoint;
	saveData->currentLacrima = currentLacrima;
	saveData->needLacrimaCount = needLacrimaCount;
	saveData->needUpgradeLacrimaCount = needUpgradeLacrimaCount;

}

 

BeginPlay에서 InitStats를 실행하면, 세이브 파일의 데이터를 받아서

컴포넌트 내 각 스탯과 값들에 동기화합니다.

이후 SaveStat 함수를 사용하면, 현재 컴포넌트의 상태를 세이브 파일에 저장할 수 있습니다.

 

배열로 저장되는 아이템은 인벤토리 컴포넌트 내에서 이와같이 저장 / 로드 하였습니다

void UInventoryComponent::LoadInventoryToSave(USaveFile* saveData)
{
	if (!saveData)
		return;

	items.SetNum(baseInvenSlotCount);
	for (FItemSlot& slot : items)
		slot.Clear();

	for (const FSaveItemData& saveItem : saveData->inventoryItems)
	{
		int32 itemID = FCString::Atoi(*saveItem.itemID);
		AddItem(itemID, saveItem.itemCount);
	}
}

void UInventoryComponent::SaveInventoryToSave(USaveFile* saveData)
{
	if (!saveData)
		return;

	saveData->inventoryItems.Empty();
	for (const FItemSlot& slot : items)
	{
		if (!slot.isEmpty())
		{
			FSaveItemData saveItem;
			saveItem.itemID = FString::FromInt(slot.itemID);
			saveItem.itemCount = slot.inCount;
			saveData->inventoryItems.Add(saveItem);
		}
	}
}

 

최종 세이브 호출은 이와 같습니다

void UCubeWidget::ButtonClickSave()
{
	if (!saveData)
		saveData = gameIst->saveManager->GetCurrentSaveFile();

	if (!saveData)
		return;

	statComp->SaveStat(saveData);

	invenComp->SaveInventoryToSave(saveData);
	gameIst->saveManager->SaveGame();
}

 

결과

저장할시 Saved -> SaveGame 폴더에 세이브파일이 갱신됩니다

새로하기를 원하면 해당 파일을 삭제하시면됩니다

 

큐브에 인터렉션하여 저장할수 있도록 설계하였습니다

 

저장영상

 

 

저작자표시 비영리 변경금지 (새창열림)

'Unreal 프로젝트 다이어리 > 두번째 프로젝트' 카테고리의 다른 글

Unreal - 스킬 트리  (0) 2025.12.10
Unreal - 스탯창  (0) 2025.12.02
Unreal - 경험치, 레벨업 구현하기  (0) 2025.12.02
Unreal - 데미지 오버레이  (0) 2025.12.01
Unreal - 인벤토리(5-2) (UI 디테일 추가)  (0) 2025.11.29
'Unreal 프로젝트 다이어리/두번째 프로젝트' 카테고리의 다른 글
  • Unreal - 스킬 트리
  • Unreal - 스탯창
  • Unreal - 경험치, 레벨업 구현하기
  • Unreal - 데미지 오버레이
lucodev
lucodev
커피와 노트북 그리고 개발
  • lucodev
    루코 개발테이블
    lucodev
  • 전체
    오늘
    어제
    • 분류 전체보기 (210) N
      • Unreal 프로젝트 다이어리 (107) N
        • 첫번째 프로젝트 (73)
        • 두번째 프로젝트 (34) N
      • Unreal 팁 (8)
      • Unreal 디버깅 (8)
      • C++ 프로그래머스 (52)
        • Stack,Queue (7)
        • Hash (4)
        • Heap (2)
        • Sort (5)
        • Exhaustive search (5)
        • Greedy (2)
        • BFS , DFS (7)
        • Graph (2)
        • Dynamic Programming (1)
        • C++ Math (2)
        • 기타 문제 (14)
      • C++ 백준 (4)
      • C++ 팁 (1)
      • 개인 코테 & 스타디 <비공개> (29)
        • 코드 개인보관함 (9)
        • 코딩테스트+@ (11)
        • 알고리즘 스타디 (6)
        • 알고리즘 스타디 과제 (3)
        • 비공개 (0)
  • 인기 글

  • 최근 글

  • 최근 댓글

  • 링크

  • 공지사항

  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 태그

    언리얼 parkour
    unreal 인벤토리
    언리얼 인벤토리
    언리얼 파쿠르
    unreal inventory
    언리얼 시퀀스
    unreal 파쿠르
    언리얼 프로그래스바
    언리얼 motionmatching
    언리얼 모션매칭
    언리얼
    Unreal Parkour
    언리얼 상호작용
    언리얼 behavior tree
    언리얼 behaviortree
    언리얼 컷씬
    unreal 시퀀스
    언리얼 비헤이비어트리
    unreal 모션매칭
    언리얼 ui
  • hELLO· Designed By정상우.v4.10.3
lucodev
Unreal - 세이브 게임
상단으로

티스토리툴바