게임 개발 (언리얼 엔진)

UE4 게임 개발 EscapeGame - 19 : SaveGame

종황이 2020. 12. 27. 23:11

1. SaveGame을 상속받은 cpp 클래스를 생성해주고, 저장하고싶은 데이터들을 멤버로 넣어줍니다.

// MySaveGame.h

#pragma once

#include "GameInfo.h"
#include "GameFramework/SaveGame.h"
#include "MySaveGame.generated.h"

UCLASS()
class ESCAPEGAME_API UMySaveGame : public USaveGame
{
	GENERATED_BODY()
	
public:
	UMySaveGame();

public:
	// 게임 상태
	UPROPERTY(VisibleAnywhere, Category = Basic)
	FVector SaveLocation;
	UPROPERTY(VisibleAnywhere, Category = Basic)
	int32 LevelIndex;
	UPROPERTY(VisibleAnywhere, Category = Basic)
	float PlayTime;
	UPROPERTY(VisibleAnywhere, Category = Basic)
	int32 DeathCount;
	// 인벤토리
	UPROPERTY(VisibleAnywhere, Category = Basic)
	int32 NoPainPotionCount; // 0
	UPROPERTY(VisibleAnywhere, Category = Basic)
	int32 InvisibleClockCount; // 1
	UPROPERTY(VisibleAnywhere, Category = Basic)
	int32 PenetrateStoneCount; // 2
	// 슬롯 이름과 인덱스
	UPROPERTY(VisibleAnywhere, Category = Basic)
	FString SaveSlotName;
	UPROPERTY(VisibleAnywhere, Category = Basic)
	int32 UserIndex;

};

// MySaveGame.cpp


#include "MySaveGame.h"

UMySaveGame::UMySaveGame()
{
	SaveSlotName = TEXT("EscapeGameSaveSlot");
	UserIndex = 0;
}

2. 메뉴에서 세이브 버튼을 누르면 플레이어에서 세이브를 해줍니다.

void AMyPlayerCharacter::SaveGameState()
{
	UMySaveGame* SaveGameInstance = Cast<UMySaveGame>(UGameplayStatics::CreateSaveGameObject(UMySaveGame::StaticClass()));
	UEscapeGameInstance* GameInst = Cast<UEscapeGameInstance>(GetGameInstance());

	AMainGameMode* GameMode = Cast<AMainGameMode>(GetWorld()->GetAuthGameMode());

	if (IsValid(GameMode))
	{
		UWidgetMainHUD* MainHUD = GameMode->GetWidgetMainHUD();

		if (IsValid(MainHUD))
		{
			Inventory = MainHUD->GetInventory();
		}
	}

	// List에 아이템 세팅
	List = Inventory->GetList();

	if (IsValid(SaveGameInstance) && IsValid(GameInst))
	{
		// 플레이어 상태 저장
		SaveGameInstance->PlayTime = GameInst->fPlayTime;
		SaveGameInstance->DeathCount = GameInst->iDeathCount;
		SaveGameInstance->LevelIndex = GameInst->GetLevelIndex();
		SaveGameInstance->SaveLocation = GetActorLocation();
		// 인벤토리 저장
		SaveGameInstance->NoPainPotionCount = Cast<UInventoryItemData>(List->GetItemAt(0))->GetItemCount();
		SaveGameInstance->InvisibleClockCount = Cast<UInventoryItemData>(List->GetItemAt(1))->GetItemCount();
		SaveGameInstance->PenetrateStoneCount = Cast<UInventoryItemData>(List->GetItemAt(2))->GetItemCount();

		UGameplayStatics::SaveGameToSlot(SaveGameInstance, SaveGameInstance->SaveSlotName, SaveGameInstance->UserIndex);
	}
}

세이브 버튼을 누르면 프로젝트의 Saved 폴더에 .sav 파일이 생성됩니다.

3. 플레이어의 비긴플레이쪽에서 세이브 데이터들을 로드해줍니다.

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

	PlayerAnim = Cast<UMyPlayerAnim>(GetMesh()->GetAnimInstance());

	AMainGameMode* GameMode = Cast<AMainGameMode>(GetWorld()->GetAuthGameMode());
	
	if (IsValid(GameMode))
	{
		UWidgetMainHUD* MainHUD = Cast<UWidgetMainHUD>(GameMode->GetWidgetMainHUD());

		if (IsValid(MainHUD))
		{
			Inventory = MainHUD->GetInventory();
			List = Inventory->GetList();
		}
	}

	UEscapeGameInstance* GameInst = Cast<UEscapeGameInstance>(GetGameInstance());

	if (IsValid(GameInst))
	{
		if (GameInst->GetIsNewGame())
		{
			//GameInst->ResetGameInstance();
		}
		// New Game이 아니라면
		else
		{
			// 저장된 정보들을 불러온다
			if (GameInst->bLoadEnable)
			{
				UMySaveGame* LoadInst = Cast<UMySaveGame>(UGameplayStatics::CreateSaveGameObject(UMySaveGame::StaticClass()));
				LoadInst = Cast<UMySaveGame>(UGameplayStatics::LoadGameFromSlot(LoadInst->SaveSlotName, LoadInst->UserIndex));


				SetActorLocation(LoadInst->SaveLocation);
				GameInst->SetLevelIndex(LoadInst->LevelIndex);
				GameInst->LoadPlayTime(LoadInst->PlayTime);
				GameInst->SetDeathCount(LoadInst->DeathCount);

				Cast<UInventoryItemData>(List->GetItemAt(0))->SetItemCount(LoadInst->NoPainPotionCount);
				Cast<UInventoryItemData>(List->GetItemAt(1))->SetItemCount(LoadInst->InvisibleClockCount);
				Cast<UInventoryItemData>(List->GetItemAt(2))->SetItemCount(LoadInst->PenetrateStoneCount);

				GameInst->bLoadEnable = false;
			}
		}
	}
}

4. 세이브 데이터가 있는지에 따라 스타트레벨에서도 조건 변화를 줍니다.

void UWidgetStartLevel::ButtonNewGameCallback()
{
	UMySaveGame* LoadInst = Cast<UMySaveGame>(UGameplayStatics::CreateSaveGameObject(UMySaveGame::StaticClass()));
	LoadInst = Cast<UMySaveGame>(UGameplayStatics::LoadGameFromSlot(LoadInst->SaveSlotName, LoadInst->UserIndex));

	// 세이브한 데이터가 있다면
	if (IsValid(LoadInst))
	{
		// 세이브 파일 있다고 메세지 출력
		CheckNewGame->VisibleCheck(ESlateVisibility::Visible);
	}
	// 그렇지 않다면 정상적으로 실행
	else
	{
		UEscapeGameInstance* GameInst = Cast<UEscapeGameInstance>(GetGameInstance());
		if (IsValid(GameInst))
		{
			GameInst->bLoadEnable = false;
			GameInst->SetLevelIndex();
			uint8 LevelIndex = GameInst->GetLevelIndex();
			UGameplayStatics::OpenLevel(GetWorld(), GameInst->LevelArray[LevelIndex]);
		}
	}
}

void UWidgetStartLevel::ButtonContinuePlayCallback()
{
	UMySaveGame* LoadInst = Cast<UMySaveGame>(UGameplayStatics::CreateSaveGameObject(UMySaveGame::StaticClass()));
	LoadInst = Cast<UMySaveGame>(UGameplayStatics::LoadGameFromSlot(LoadInst->SaveSlotName, LoadInst->UserIndex));

	// 세이브한 데이터가 있다면 세이브 지점으로
	if (IsValid(LoadInst))
	{
		UEscapeGameInstance* GameInst = Cast<UEscapeGameInstance>(GetGameInstance());

		if (IsValid(GameInst))
		{
			GameInst->SetIsNewGame(false);
			GameInst->bLoadEnable = true;
			GameInst->SetLevelIndex(LoadInst->LevelIndex);
			uint8 LevelIndex = GameInst->GetLevelIndex();
			UGameplayStatics::OpenLevel(GetWorld(), GameInst->LevelArray[LevelIndex]);
		}
	}
	// 그렇지 않다면
	else
	{
		// 저장된 파일 없다고 메세지 출력
		CheckContinueGame->VisibleCheck(ESlateVisibility::Visible);
	}
}

5. 관련 UI 제작

세이브한 게임이 있는데 뉴게임 버튼을 눌렀을 때, 체크해주는 메세지를 출력합니다.

세이브한 게임이 없는데 컨티뉴게임을 누르면 체크메세지를 출력합니다.