맵을 이동할시 변수들이 초기화되서 분명 hp, mp, 재화수치들 등등
변수값들이 맵을 이동했을때 초기화가 되는 현상이 있습니다
OpenLevel을 사용하여 맵이 새로 열리면 원래있던 객체는 전부 Destroy되고 전부 새로 스폰되기떄문입니다
이럴떄는 Unreal엔진에서 제공해주는 SaveGameAPI 를 사용하시면됩니다
먼저 SaveGame을 상속받아 C++클래스를 생성해줍니다

(ProjectSaveGame이라고 이름을 지정해줬습니다)
SaveGame이란 게임을 저장할 "틀" 이라고 생각하시면 되겠습니다
여기서 매우중요한점은 UPROPERTY를 반드시 사용 해야만 데이터가 저장됩니다
SaveGame을 상속받은 C++의 .h에서 저장할 변수값과
세이브 데이터를 식별하기 위한 필수 인자인
FString 슬롯 이름 과 int32 슬롯 인덱스를 필수로 선언해줍니다
필자는 플레이어의 hp, mp 의이름을 가진 float값 2개를 저장해보도록하겠습니다
UCLASS()
class BLASTERDREAM_API UProjectSaveGame : public USaveGame
{
GENERATED_BODY()
public:
//Save Game Data File Name
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MySettings")
FString saveSlotName;
//Save Game Data File Index
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MySettings")
int32 saveIndex;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MySettings")
float savePlayerHp;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MySettings")
float savePlayerMp;
};
여기서 해당 밑의 두변수는 SaveGame을 사용하여 게임을 저장할 때 필요한 필수 변수이므로
반드시 넣어주도록 합니다
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MySettings")
FString saveSlotName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MySettings")
int32 saveIndex;
여기서 반드시 알아야하는 개념은 레벨을 이동시 게임모드와 플레이어폰 등등
GameInstance를 제외하고 다시 재생성됩니다
넘겨줘야하는 변수값의 초기 위치는 GameInstance에 존재하면됩니다
GameInstance의 .h에서 플레이어의 hp와 mp값을 선언하면서 원하는수치로 초기화를 해줍니다
float playerSaveHp = 10000.0;
float playerSaveMp = 5000.0f;
이제 GameMode에서 세이브 과 로드 에 대한 함수를 만들어시면됩니다
GameplayStatics에서 제공하는 SaveGameToSlot 을 이용하여 .sav파일을 생성합니다
세이브 함수입니다
void ASwordPlayerGameBase::SavePlayerData()
{
UProjectSaveGame* saveGameInstance = Cast<UProjectSaveGame>(UGameplayStatics::CreateSaveGameObject(UProjectSaveGame::StaticClass()));
UStatGameInstance* gameInstance = Cast<UStatGameInstance>(GetGameInstance());
if (saveGameInstance)
{
//save data
saveGameInstance->saveSlotName = "PlayerSaveData";
saveGameInstance->saveIndex = 0;
saveGameInstance->savePlayerHp = gameInstance->playerSaveHp;
saveGameInstance->savePlayerMp = gameInstance->playerSaveMp;
UGameplayStatics::SaveGameToSlot(saveGameInstance, saveGameInstance->saveSlotName, saveGameInstance->saveIndex);
}
}
해당 함수가 호출이되면 SaveGame 클래스에 있는 값들이
GameInstance 값들의 변수값들로 세이브가 됩니다
세이브하고싶은 타이밍 에 GameInstance에서 값을 플레이어에게 할당후
게임모드에 만들어놓은 SavePlayerData 함수를 호출시켜주시면됩니다
ASwordPlayerGameBase* gameMode = Cast<ASwordPlayerGameBase>(UGameplayStatics::GetGameMode(this));
UStatGameInstance* gameInstance = Cast<UStatGameInstance>(GetGameInstance());
ASwordCharacter* player = Cast<ASwordCharacter>(UGameplayStatics::GetPlayerCharacter(this, 0));
if (gameMode && gameInstance)
{
gameInstance->playerSaveHp = player->playerCurrentHp;
gameInstance->playerSaveMp = player->playerCurrentMp;
}
gameMode->SavePlayerData();
필자의 경우 세이브 액터(세이브포인트) 에 오버랩이되면 발동되도록 하였습니다
SavePlayerData함수안의 SaveGameToSlot함수가 호출이 되면
Saved -> SaveGames 폴더에 세이브 파일(.sav)가 만들어진것을 확인할수있습니다

이제 저장된 세이브값에서 값을 로드해서 사용해보도록 하겠습니다
로드 함수입니다
GameplayStatics에서 제공하는 LoadGameFromSlot 을 이용하여 .sav파일에 저장된 값을 로드합니다
void ASwordPlayerGameBase::LoadPlayerData()
{
UProjectSaveGame* loadGameInstance = Cast<UProjectSaveGame>(UGameplayStatics::CreateSaveGameObject(UProjectSaveGame::StaticClass()));
UStatGameInstance* gameInstance = Cast<UStatGameInstance>(GetGameInstance());
if (loadGameInstance && gameInstance)
{
//load data
loadGameInstance->saveSlotName = "PlayerSaveData";
loadGameInstance->saveIndex = 0;
loadGameInstance = Cast<UProjectSaveGame>(UGameplayStatics::LoadGameFromSlot(loadGameInstance->saveSlotName, loadGameInstance->saveIndex));
if (loadGameInstance)
{
gameInstance->playerSaveHp = loadGameInstance->savePlayerHp;
gameInstance->playerSaveMp = loadGameInstance->savePlayerMp;
}
}
}
해당 로드함수 (LoadPlayerData) 의 호출위치는 GameMode의 BeinPlay에서 호출시켜주시면됩니다
플레이어의 BeginPlay에서 게임인스턴스에서 값을 받아 PlayerCurrentHp에 복사하여 hp를 동기화시킵니다
만약 .Sav파일이 삭제가 되는일이 아닌이상 해당 넘겨지는 변수(hp, mp)값들은 값이 유지됩니다
플레이어 BeinPlay에서 값을 동기화시켜주시면됩니다
//Reanual Stat
UStatGameInstance* myInstance = Cast<UStatGameInstance>(GetGameInstance());
ASwordPlayerGameBase* gameMode = Cast<ASwordPlayerGameBase>(UGameplayStatics::GetGameMode(this));
if (gameMode)
{
playerCurrentHp = myInstance->playerSaveHp;
playerCurrentMp = myInstance->playerSaveMp;
}
전체 로직이 돌아가는 순서입니다
게임을 저장할 때
GameInstance -> SaveGame 클래스에 데이터를 복사 ( SaveGameToSlot )
레벨 이동 시
GameMode::BeginPlay ->LoadPlayerData함수 실행( LoadGameFromSlot ) ->
SaveGame에서 GameInstance로 변수값 복사
캐릭터 생성 시
Character::BeginPlay -> GameInstance에서 값을 받아서 PlayerCurrentHp..등등 가져올 변수값에 복사
이러면 맵을 저장할때 영구적으로 해당 변수값들이 저장이 됩니다
만약 값을 초기화 하고싶으면 .Sav파일을 삭제하시면됩니다
.Sav파일이 삭제가 되는일이 아닌이상 해당 넘겨지는 변수(hp, mp)값들은 값이 유지됩니다
++Detail
저장을 했다 라는 표식을 위한 Widget, WidgetAnimation을 만들어주었습니다

위젯을 띄워주는 세이브액터 .h입니다
//Save
UPROPERTY(EditAnywhere, Category = "MySettings")
TSubclassOf<USaveWidget> saveWidgetClass;
USaveWidget* saveWidgetInstance;
오버랩되는부분입니다
해당 위젯이 없다면 띄워주고 Lamda함수를 사용하여 1.5초의 라이프주기를 가지도록 설정하였습니다
APlayerController* pC = UGameplayStatics::GetPlayerController(this, 0);
if (hitActorName.Contains(TEXT("SwordCharacter")))
{
if (pC)
{
if (saveWidgetInstance)
{
saveWidgetInstance->RemoveFromParent();
saveWidgetInstance = nullptr;
}
saveWidgetInstance = CreateWidget<USaveWidget>(pC, saveWidgetClass);
saveWidgetInstance->AddToViewport();
FTimerHandle th_RemoveSaveWidget;
USaveWidget* captureSaveWidget = saveWidgetInstance;
GetWorld()->GetTimerManager().SetTimer(th_RemoveSaveWidget, [captureSaveWidget]()
{
captureSaveWidget->RemoveFromParent();
}, 1.5f, false);
}
//..생략
}
할당 꼭 해줍니다

ViewPort 우측하단에 위젯이 뜨게 세팅하였습니다

그러면 해당 위젯이 뜨면 세이브가 되었다 라는걸 시각적으로 알수있게됩니다
최종결과물

에디터를 종료후 다시 시작해도 맵을 이동해도 세이브가 되면 값이 유지되는것을 확인할수있습니다
'Unreal 프로젝트 다이어리 > 첫번째 프로젝트' 카테고리의 다른 글
| Unreal - 부숴지는 벽 만들기 (Chaos Distruction) (0) | 2025.05.22 |
|---|---|
| Unrela - 시퀀스 만들기 (캐릭터편) (0) | 2025.05.21 |
| Unreal - 몬스터 웨이브 만들기 (0) | 2025.05.17 |
| Unreal - 버튼 위젯 사용하기 (0) | 2025.05.16 |
| Unreal - 로딩 창 (0) | 2025.05.16 |
