본문 바로가기

SaveGame 시스템

USaveGame 파생 클래스에 진행도와 설정 데이터를 설계하고 슬롯 단위로 저장·불러오는 흐름을 구현합니다.

이전 장에서는 UMG로 게임 UI를 만들고 관리하는 방법을 다뤘습니다.

이번에는 게임의 또 다른 핵심 요소인 데이터 저장(Saving Data)으로 넘어갑니다.

플레이어가 게임을 다시 실행했을 때 진행도, 아이템, 설정을 그대로 불러오려면 SaveGame 시스템이 필요합니다.

이번 절에서는 SaveGame 시스템의 핵심 개념과 저장/불러오기 구현 흐름을 정리합니다.


SaveGame 시스템이란?

언리얼 엔진의 SaveGame 시스템은 게임 데이터를 디스크에 저장하고, 필요할 때 다시 불러올 수 있도록 설계된 추상화된 계층입니다.

이 시스템의 핵심은 USaveGame이라는 특별한 UObject 클래스이며, 이 클래스의 인스턴스에 저장하고자 하는 모든 데이터를 담아 직렬화(Serialization)하여 파일로 만듭니다.

SaveGame 시스템의 주요 특징

  • 플랫폼 독립적: PC, 콘솔, 모바일 등 다양한 플랫폼에서 일관된 방식으로 데이터를 저장하고 불러올 수 있습니다.
  • 쉬운 사용성: 복잡한 파일 I/O를 직접 다룰 필요 없이, 언리얼 엔진이 제공하는 함수를 통해 직관적으로 데이터를 저장하고 불러옵니다.
  • 데이터 직렬화: 기본 SaveGameToSlot은 USaveGame의 non-transient 속성을 기록합니다. 이 함수는 SaveGame 지정자 유무를 검사하지 않습니다. 객체 참조를 기록하더라도 대상 액터의 전체 상태가 함께 저장되지는 않습니다.
  • 여러 Save Slot 지원: 여러 개의 독립적인 저장 슬롯을 지원하여, 플레이어가 여러 게임 진행 상태를 관리할 수 있습니다.

SaveGame 데이터 구조 설계

게임을 저장하려면, 먼저 어떤 데이터를 저장할지 결정하고 이를 담을 USaveGame을 상속받는 C++ 클래스를 정의해야 합니다.

이 클래스는 마치 저장될 데이터의 설계도와 같습니다.

MySaveGame.h
#pragma once

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

UCLASS()
class MYPROJECT_API UMySaveGame : public USaveGame
{
    GENERATED_BODY()

public:
    // 생성자 (선택 사항, 필요에 따라 초기화 로직 추가)
    UMySaveGame();

    // 저장될 데이터들 정의 (UPROPERTY 매크로 필수)
    // BlueprintReadWrite를 사용하여 블루프린트에서도 접근 가능하게 할 수 있습니다.

    UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Basic")
    FString PlayerName; // 플레이어 이름

    UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Basic")
    int32 PlayerScore; // 플레이어 점수

    UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Basic")
    float PlayerHealth; // 플레이어 체력

    UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Basic")
    FVector PlayerLocation; // 플레이어 위치 (월드 좌표)

    UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Basic")
    FRotator PlayerRotation; // 플레이어 회전 (월드 회전)

    UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Inventory")
    TArray<FString> InventoryItems; // 인벤토리 아이템 목록 (문자열 배열)

    UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Game Progress")
    TMap<FString, bool> QuestsCompleted; // 완료된 퀘스트 목록 (퀘스트 이름, 완료 여부)

    UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Game Progress")
    int32 CurrentLevelIndex; // 현재 레벨 인덱스
};
MySaveGame.cpp
#include "GameFramework/PlayerController.h"
#include "GameFramework/Pawn.h"
#include "MySaveGame.h"

UMySaveGame::UMySaveGame()
{
    // 변수들의 기본값 설정 (선택 사항)
    PlayerName = TEXT("DefaultPlayer");
    PlayerScore = 0;
    PlayerHealth = 100.0f;
    PlayerLocation = FVector::ZeroVector;
    PlayerRotation = FRotator::ZeroRotator;
    InventoryItems.Empty();
    QuestsCompleted.Empty();
    CurrentLevelIndex = 0;
}
중요 사항
  • USaveGame을 상속받아야 합니다.
  • 저장하고자 하는 모든 변수는 반드시 UPROPERTY() 매크로로 선언되어야 합니다. 그렇지 않으면 언리얼 엔진의 직렬화 시스템이 해당 변수를 인식하지 못하고 파일에 저장되지 않습니다.
  • 객체의 메모리 주소는 다음 실행에서 재사용할 식별자가 아닙니다. 런타임 액터는 프로젝트가 관리하는 안정적인 ID로 다시 찾거나 생성해야 합니다. 액터 이름만으로 실행 간 고유성과 안정성이 보장되지는 않습니다.

게임 데이터 저장하기

게임을 저장하는 과정은 다음 단계로 진행됩니다.

아래는 GameMode에서 호출하는 단일 플레이어 예제입니다. 멀티플레이에서 GameMode는 서버에만 있으므로 원격 클라이언트의 로컬 저장을 대신하지 않습니다. UserIndex는 저장 플랫폼 사용자 식별자이며, 예제의 로컬 PlayerController 인덱스 0과 별개입니다.

CreateSaveGameObject(): 저장할 USaveGame 클래스의 인스턴스를 생성합니다.

데이터 채우기: 생성된 SaveGame 오브젝트에 현재 게임 상태의 데이터를 채워 넣습니다.

SaveGameToSlot(): SaveGame 오브젝트를 특정 슬롯 이름과 사용자 인덱스를 사용하여 디스크에 저장합니다.

MyGameModeBase.h (GameMode에서 저장 로직 예시)
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MyGameModeBase.generated.h"

class UMySaveGame; // 우리가 만든 SaveGame 클래스 선언

UCLASS()
class MYPROJECT_API AMyGameModeBase : public AGameModeBase
{
    GENERATED_BODY()

public:
    AMyGameModeBase();

    // 블루프린트에서 할당할 SaveGame 클래스
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "SaveGame")
    TSubclassOf<UMySaveGame> SaveGameClass;

    // 게임 저장 함수
    UFUNCTION(BlueprintCallable, Category = "SaveGame")
    void SaveMyGame(FString SlotName, int32 UserIndex);
};
MyGameModeBase.cpp
#include "MyGameModeBase.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/Pawn.h"
#include "MySaveGame.h" // 우리가 만든 SaveGame 클래스 헤더
#include "Kismet/GameplayStatics.h" // UGameplayStatics 함수들을 위해 포함

AMyGameModeBase::AMyGameModeBase()
{
    // ...
}

void AMyGameModeBase::SaveMyGame(FString SlotName, int32 UserIndex)
{
    // 1. SaveGameClass가 유효한지 확인
    if (!SaveGameClass)
    {
        UE_LOG(LogTemp, Error, TEXT("SaveGameClass is not set in MyGameModeBase!"));
        return;
    }

    // 2. SaveGame 오브젝트 인스턴스 생성
    // CreateSaveGameObject(SaveGameClass)
    UMySaveGame* SaveGameInstance = Cast<UMySaveGame>(UGameplayStatics::CreateSaveGameObject(SaveGameClass));
    if (!SaveGameInstance)
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to create SaveGame object."));
        return;
    }

    // 3. 현재 게임 데이터로 SaveGame 인스턴스 채우기
    // 이 부분은 실제 게임의 상태에 따라 달라집니다.
    // 예: 플레이어 컨트롤러에서 플레이어 데이터 가져오기
    APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0);
    if (PlayerController)
    {
        // 예시 데이터 설정
        SaveGameInstance->PlayerName = TEXT("Hero");
        SaveGameInstance->PlayerScore = 12345;
        SaveGameInstance->PlayerHealth = 75.5f;

        // 플레이어 캐릭터의 위치와 회전 가져오기 (예시)
        APawn* PlayerPawn = PlayerController->GetPawn();
        if (PlayerPawn)
        {
            SaveGameInstance->PlayerLocation = PlayerPawn->GetActorLocation();
            SaveGameInstance->PlayerRotation = PlayerPawn->GetActorRotation();
        }

        SaveGameInstance->InventoryItems.Add(TEXT("Sword"));
        SaveGameInstance->InventoryItems.Add(TEXT("Shield"));
        SaveGameInstance->QuestsCompleted.Add(TEXT("FindTheLostGem"), true);
        SaveGameInstance->CurrentLevelIndex = 0; // 예시 값: 프로젝트의 진행 단계 인덱스로 대체
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("Could not get local PlayerController 0. Saving default data."));
    }

    // 4. SaveGame 오브젝트를 지정된 슬롯에 저장
    // SaveGameToSlot(SaveGameObject, SlotName, UserIndex)
    if (UGameplayStatics::SaveGameToSlot(SaveGameInstance, SlotName, UserIndex))
    {
        UE_LOG(LogTemp, Warning, TEXT("Game saved successfully to Slot: %s, User: %d"), *SlotName, UserIndex);
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to save game to Slot: %s, User: %d"), *SlotName, UserIndex);
    }
}

게임 데이터 불러오기

게임을 불러오는 과정은 저장 과정의 역순입니다.

DoesSaveGameExist(): 특정 슬롯에 저장된 게임이 존재하는지 먼저 확인합니다.

LoadGameFromSlot(): 지정된 슬롯에서 SaveGame 오브젝트를 불러옵니다.

데이터 사용: 불러온 SaveGame 오브젝트의 데이터를 게임의 현재 상태에 적용합니다.

MyGameModeBase.h (GameMode에서 불러오기 로직 예시)
#pragma once

// ... 기존 코드 ...

UCLASS()
class MYPROJECT_API AMyGameModeBase : public AGameModeBase
{
    GENERATED_BODY()

public:
    // ... 기존 코드 ...

    // 게임 불러오기 함수
    UFUNCTION(BlueprintCallable, Category = "SaveGame")
    void LoadMyGame(FString SlotName, int32 UserIndex);
};
MyGameModeBase.cpp
#include "MyGameModeBase.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/Pawn.h"
#include "MySaveGame.h"
#include "Kismet/GameplayStatics.h"

// ... 기존 SaveMyGame 함수 ...

void AMyGameModeBase::LoadMyGame(FString SlotName, int32 UserIndex)
{
    // 1. 지정된 슬롯에 저장된 게임이 있는지 확인
    if (!UGameplayStatics::DoesSaveGameExist(SlotName, UserIndex))
    {
        UE_LOG(LogTemp, Warning, TEXT("No SaveGame found for Slot: %s, User: %d. Starting new game."), *SlotName, UserIndex);
        // 저장된 게임이 없으므로, 새 게임 시작 로직을 여기에 구현
        return;
    }

    // 2. SaveGame 오브젝트 불러오기
    // LoadGameFromSlot(SlotName, UserIndex)
    UMySaveGame* LoadedGameInstance = Cast<UMySaveGame>(UGameplayStatics::LoadGameFromSlot(SlotName, UserIndex));
    if (!LoadedGameInstance)
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to load SaveGame from Slot: %s, User: %d."), *SlotName, UserIndex);
        return;
    }

    // 3. 불러온 데이터로 게임 상태 업데이트
    UE_LOG(LogTemp, Warning, TEXT("Game loaded successfully from Slot: %s, User: %d"), *SlotName, UserIndex);
    UE_LOG(LogTemp, Warning, TEXT("Player Name: %s"), *LoadedGameInstance->PlayerName);
    UE_LOG(LogTemp, Warning, TEXT("Player Score: %d"), LoadedGameInstance->PlayerScore);
    UE_LOG(LogTemp, Warning, TEXT("Player Health: %f"), LoadedGameInstance->PlayerHealth);
    UE_LOG(LogTemp, Warning, TEXT("Player Location: %s"), *LoadedGameInstance->PlayerLocation.ToString());
    UE_LOG(LogTemp, Warning, TEXT("Player Rotation: %s"), *LoadedGameInstance->PlayerRotation.ToString());

    // 인벤토리 아이템 출력
    for (const FString& Item : LoadedGameInstance->InventoryItems)
    {
        UE_LOG(LogTemp, Warning, TEXT("  - Inventory Item: %s"), *Item);
    }

    // 퀘스트 완료 여부 출력
    for (const TPair<FString, bool>& Quest : LoadedGameInstance->QuestsCompleted)
    {
        UE_LOG(LogTemp, Warning, TEXT("  - Quest '%s' Completed: %s"), *Quest.Key, Quest.Value ? TEXT("True") : TEXT("False"));
    }

    // 레벨 이동은 미구현: 프로젝트의 단계 인덱스 → 레벨 이름 매핑을 먼저 정의합니다.

    // 이 데이터를 게임의 실제 액터와 컴포넌트에 적용하는 로직 구현
    APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0);
    if (PlayerController)
    {
        APawn* PlayerPawn = PlayerController->GetPawn();
        if (PlayerPawn)
        {
            PlayerPawn->SetActorLocation(LoadedGameInstance->PlayerLocation);
            PlayerPawn->SetActorRotation(LoadedGameInstance->PlayerRotation);
            // 플레이어 체력/인벤토리 등도 업데이트
        }
    }
}

두 GameMode 헤더는 같은 클래스의 저장·로드 선언을 나누어 보여 준 발췌입니다. 생성자와 클래스를 다시 정의하지 않고 LoadMyGame 선언을 기존 헤더에 합칩니다. 현재 맵이 그 게임 모드를 사용하도록 설정하고, 저장·로드 버튼 또는 별도 테스트 이벤트에서 같은 SlotName과 UserIndex를 넘기는 호출을 준비합니다. 함수 선언만 추가했다고 버튼이 자동으로 연결되지는 않습니다.

예시: 파일의 데이터와 실제 게임 상태 비교하기

저장 호출 순간의 Pawn 위치가 (100, 200, 300) cm라고 가정합니다. 원 SaveMyGame은 이 위치를 복사하지만 점수와 체력에는 실제 캐릭터 값을 조회하지 않고 12345와 75.5를 넣습니다. 따라서 저장 성공 로그는 그 예시 값과 수집한 위치를 슬롯에 기록했다는 범위로 읽습니다. 나중에 같은 슬롯을 읽으면 체력 75.5가 로그에 나오더라도, 원 LoadMyGame의 체력·인벤토리 적용은 주석이므로 캐릭터 체력이 자동으로 75.5가 되지 않습니다.

기본 데이터 저장과 부분 적용 판단하기

저장 시 PlayerController를 찾지 못했지만 SaveGame 객체 생성과 파일 저장은 성공했다고 가정합니다. 다음 로드에서 이름·점수·체력·위치의 로그 값은 무엇일까요? 유효한 Pawn이 있어 위치·회전 설정을 시도한 사실만으로 체력·인벤토리·레벨 이동까지 완료되었다고 판정해도 되는지도 설명하세요.

원 생성자와 실제 대입을 따른 풀이

생성자의 기본값인 DefaultPlayer, 점수 0, 체력 100, 위치 (0, 0, 0)이 기록됩니다. PlayerController가 없는 분기에서는 Hero·12345·75.5나 아이템을 채우지 않았습니다. 읽은 객체의 로그, 위치·회전 설정 시도, 다른 게임 데이터의 적용을 각각 구별합니다. 실제 상태 수집과 값 검증·전체 적용을 구현하고 위치 변경 결과도 확인해야 하며, 기본 데이터를 기존 슬롯에 덮어쓸지의 정책도 별도로 정합니다.

에디터에서 SaveGame 클래스 할당

위 코드를 컴파일한 후, 게임 모드 블루프린트 (예: BP_MyGameModeBase)를 열고 디테일 패널의 SaveGame 카테고리에서 Save Game Class 변수에 우리가 만든 UMySaveGame 블루프린트(또는 C++ 클래스 자체)를 할당해야 합니다.


저장된 파일의 위치

언리얼 엔진은 저장 파일을 플랫폼별로 적절한 위치에 자동으로 생성합니다.

  • Windows 패키지 예시: C:\Users\[사용자명]\AppData\Local\[프로젝트명]\Saved\SaveGames\. 에디터에서는 프로젝트의 Saved/SaveGames 등을 사용하며 실제 위치는 실행 환경과 플랫폼 구현에 따라 다릅니다.
  • Android/iOS: 각 플랫폼의 앱 데이터 디렉토리 내에 저장됩니다.

파일 이름은 [SlotName].sav 형식으로 저장됩니다.


로드 성공 뒤에도 게임 규칙에 맞는 값 검증과 적용 로직이 필요합니다. 아래 표는 이 예제의 구현 범위를 요약합니다.

저장 성공·읽기 성공·게임 상태 적용은 다릅니다

슬롯 존재→로드 객체→UMySaveGame 캐스트/유효성→유효 Pawn의 위치·회전 설정 시도 또는 오류 반환의 경계이다. 설정의 실제 성공 반환은 별도로 확인하며 체력·인벤토리·레벨의 미구현 적용은 추가하지 않는다.

UNREAL C++ · CH7-1

저장 성공·읽기 성공·게임 상태 적용은 다릅니다

SaveMyGame과 LoadMyGame의 실제 코드 범위를 비교합니다. 파일을 저장하거나 객체를 읽었다고 해서 체력·인벤토리·레벨이 모두 복원된 것은 아닙니다.

로드한 객체의 유효성을 확인한 뒤 일부만 적용한다슬롯존재확인후LoadGameFromSlot의결과를UMySaveGame으로캐스트한다. 유효객체이면로그와Pawn위치회전설정경로를수행하고없으면오류로그후반환한다. 체력인벤토리적용과레벨이동은원코드주석또는미구현이다. 슬롯없음은더앞에서반환하는조건이다.존재 슬롯 읽기LoadGameFromSlot로드 객체 캐스트UMySaveGame객체 유효?캐스트 결과 확인예아니요로그 · 부분 적용Pawn 위치 · 회전 시도오류 뒤 반환게임 상태 적용 생략
로드한 객체의 유효성을 확인한 뒤 일부만 적용한다슬롯존재확인후LoadGameFromSlot의결과를UMySaveGame으로캐스트한다. 유효객체이면로그와Pawn위치회전설정경로를수행하고없으면오류로그후반환한다. 체력인벤토리적용과레벨이동은원코드주석또는미구현이다. 슬롯없음은더앞에서반환하는조건이다.존재 슬롯 읽기LoadGameFromSlot로드 객체 캐스트UMySaveGame객체 유효?캐스트 결과 확인예아니요로그 · 부분 적용Pawn 위치 · 회전 시도오류 뒤 반환게임 상태 적용 생략

슬롯 존재가 읽기 성공을 대신하지 않습니다. 유효한 Pawn이 있어야 위치·회전 설정을 시도하며, 그 반환 결과·체력·인벤토리·레벨 이동은 별도로 확인합니다.

저장·읽기·적용의 서로 다른 성공 범위
단계이 예제의 동작추가로 필요한 확인
저장이름·점수·체력·아이템은 예시 값, Pawn 위치·회전은 현재 값. SaveGameToSlot 반환값을 검사합니다.플레이어를 못 찾으면 기본 데이터도 저장됩니다. 실제 상태 수집과 슬롯 덮어쓰기 정책은 별도입니다.
읽기슬롯 존재 여부를 확인하고 불러온 객체를 UMySaveGame으로 캐스팅합니다.존재 여부만으로 읽기 성공·형식 호환·값의 유효성이 보장되지는 않습니다.
적용이름·점수·체력·인벤토리를 로그로 출력하고 Pawn의 위치·회전을 설정합니다.체력·인벤토리 반영과 레벨 이동은 구현하지 않았습니다. 위치 설정의 성공도 별도 확인 대상입니다.
저장
이 예제의 동작: 이름·점수·체력·아이템은 예시 값, Pawn 위치·회전은 현재 값. SaveGameToSlot 반환값을 검사합니다.
추가로 필요한 확인: 플레이어를 못 찾으면 기본 데이터도 저장됩니다. 실제 상태 수집과 슬롯 덮어쓰기 정책은 별도입니다.
읽기
이 예제의 동작: 슬롯 존재 여부를 확인하고 불러온 객체를 UMySaveGame으로 캐스팅합니다.
추가로 필요한 확인: 존재 여부만으로 읽기 성공·형식 호환·값의 유효성이 보장되지는 않습니다.
적용
이 예제의 동작: 이름·점수·체력·인벤토리를 로그로 출력하고 Pawn의 위치·회전을 설정합니다.
추가로 필요한 확인: 체력·인벤토리 반영과 레벨 이동은 구현하지 않았습니다. 위치 설정의 성공도 별도 확인 대상입니다.

SaveGameToSlot의 bool과 LoadGameFromSlot의 객체를 각각 검사합니다. UserIndex는 저장 플랫폼 사용자이고 PlayerController 인덱스 0과 별개입니다. 멀티플레이의 GameMode는 서버에만 있습니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
원 도표의 제목·노드·관계·조건·주석
범위원문
관계·조건 설명슬롯 존재 확인 후 LoadGameFromSlot의 결과를 UMySaveGame으로 캐스트한다. 유효한 객체이면 로그를 남기고 Pawn 위치·회전 설정 경로를 수행하며, 객체가 없으면 오류 로그 후 반환한다. 체력·인벤토리 적용과 레벨 이동은 원 코드에서 주석으로만 제시되거나 구현되지 않은 부분이다. 슬롯이 없으면 더 앞 단계에서 반환한다.
노드·연결선 라벨존재하는 슬롯 읽기
예의 값과 변화
관찰·상황값·보충 정보변화·이유
슬롯 로드

슬롯 존재를 확인한 뒤 저장 객체를 로드한다.

  • 입력: 저장 슬롯
  • 산출물: Load 객체
  • 존재 여부만으로 내용의 타입과 유효성을 보장하지 않는다.
타입 검사

UMySaveGame 캐스트와 유효성 조건을 확인한다.

  • 기대 타입: UMySaveGame
  • 실패: 오류 반환
  • 실패한 캐스트를 정상 데이터로 사용하지 않는다.
부분 설정 시도

유효한 Pawn이면 위치·회전 설정을 시도하며 실제 성공 반환은 별도로 확인한다.

  • 선행 조건: 유효한 Pawn
  • 실행 범위: 위치·회전 설정 시도
  • 성공 판정: 설정 반환 결과 별도 확인
  • 미구현: 체력·인벤토리·레벨 적용
  • 로드 객체가 유효해도 Pawn과 설정 결과가 별도 조건이다. 설정을 시도한 사실만으로 전체 게임 상태 복원 완료를 확정하지 않는다.

저장 성공·읽기 성공·게임 상태 적용은 다릅니다

슬롯 존재→로드 객체→UMySaveGame 캐스트/유효성→유효 Pawn의 위치·회전 설정 시도 또는 오류 반환의 경계이다. 설정의 실제 성공 반환은 별도로 확인하며 체력·인벤토리·레벨의 미구현 적용은 추가하지 않는다.

UNREAL C++ · CH7-1

저장 성공·읽기 성공·게임 상태 적용은 다릅니다

SaveMyGame과 LoadMyGame의 실제 코드 범위를 비교합니다. 파일을 저장하거나 객체를 읽었다고 해서 체력·인벤토리·레벨이 모두 복원된 것은 아닙니다.

로드한 객체의 유효성을 확인한 뒤 일부만 적용한다슬롯존재확인후LoadGameFromSlot의결과를UMySaveGame으로캐스트한다. 유효객체이면로그와Pawn위치회전설정경로를수행하고없으면오류로그후반환한다. 체력인벤토리적용과레벨이동은원코드주석또는미구현이다. 슬롯없음은더앞에서반환하는조건이다.존재 슬롯 읽기LoadGameFromSlot로드 객체 캐스트UMySaveGame객체 유효?캐스트 결과 확인예아니요로그 · 부분 적용Pawn 위치 · 회전 시도오류 뒤 반환게임 상태 적용 생략
로드한 객체의 유효성을 확인한 뒤 일부만 적용한다슬롯존재확인후LoadGameFromSlot의결과를UMySaveGame으로캐스트한다. 유효객체이면로그와Pawn위치회전설정경로를수행하고없으면오류로그후반환한다. 체력인벤토리적용과레벨이동은원코드주석또는미구현이다. 슬롯없음은더앞에서반환하는조건이다.존재 슬롯 읽기LoadGameFromSlot로드 객체 캐스트UMySaveGame객체 유효?캐스트 결과 확인예아니요로그 · 부분 적용Pawn 위치 · 회전 시도오류 뒤 반환게임 상태 적용 생략

슬롯 존재가 읽기 성공을 대신하지 않습니다. 유효한 Pawn이 있어야 위치·회전 설정을 시도하며, 그 반환 결과·체력·인벤토리·레벨 이동은 별도로 확인합니다.

저장·읽기·적용의 서로 다른 성공 범위
단계이 예제의 동작추가로 필요한 확인
저장이름·점수·체력·아이템은 예시 값, Pawn 위치·회전은 현재 값. SaveGameToSlot 반환값을 검사합니다.플레이어를 못 찾으면 기본 데이터도 저장됩니다. 실제 상태 수집과 슬롯 덮어쓰기 정책은 별도입니다.
읽기슬롯 존재 여부를 확인하고 불러온 객체를 UMySaveGame으로 캐스팅합니다.존재 여부만으로 읽기 성공·형식 호환·값의 유효성이 보장되지는 않습니다.
적용이름·점수·체력·인벤토리를 로그로 출력하고 Pawn의 위치·회전을 설정합니다.체력·인벤토리 반영과 레벨 이동은 구현하지 않았습니다. 위치 설정의 성공도 별도 확인 대상입니다.
저장
이 예제의 동작: 이름·점수·체력·아이템은 예시 값, Pawn 위치·회전은 현재 값. SaveGameToSlot 반환값을 검사합니다.
추가로 필요한 확인: 플레이어를 못 찾으면 기본 데이터도 저장됩니다. 실제 상태 수집과 슬롯 덮어쓰기 정책은 별도입니다.
읽기
이 예제의 동작: 슬롯 존재 여부를 확인하고 불러온 객체를 UMySaveGame으로 캐스팅합니다.
추가로 필요한 확인: 존재 여부만으로 읽기 성공·형식 호환·값의 유효성이 보장되지는 않습니다.
적용
이 예제의 동작: 이름·점수·체력·인벤토리를 로그로 출력하고 Pawn의 위치·회전을 설정합니다.
추가로 필요한 확인: 체력·인벤토리 반영과 레벨 이동은 구현하지 않았습니다. 위치 설정의 성공도 별도 확인 대상입니다.

SaveGameToSlot의 bool과 LoadGameFromSlot의 객체를 각각 검사합니다. UserIndex는 저장 플랫폼 사용자이고 PlayerController 인덱스 0과 별개입니다. 멀티플레이의 GameMode는 서버에만 있습니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.

관계도의 보충 설명

원 도표의 제목·노드·관계·조건·주석
범위원문
관계·조건 설명슬롯 존재 확인 후 LoadGameFromSlot의 결과를 UMySaveGame으로 캐스트한다. 유효한 객체이면 로그를 남기고 Pawn 위치·회전 설정 경로를 수행하며, 객체가 없으면 오류 로그 후 반환한다. 체력·인벤토리 적용과 레벨 이동은 원 코드에서 주석으로만 제시되거나 구현되지 않은 부분이다. 슬롯이 없으면 더 앞 단계에서 반환한다.
노드·연결선 라벨존재하는 슬롯 읽기
예의 값과 변화
관찰·상황값·보충 정보변화·이유
슬롯 로드

슬롯 존재를 확인한 뒤 저장 객체를 로드한다.

  • 입력: 저장 슬롯
  • 산출물: Load 객체
  • 존재 여부만으로 내용의 타입과 유효성을 보장하지 않는다.
타입 검사

UMySaveGame 캐스트와 유효성 조건을 확인한다.

  • 기대 타입: UMySaveGame
  • 실패: 오류 반환
  • 실패한 캐스트를 정상 데이터로 사용하지 않는다.
부분 설정 시도

유효한 Pawn이면 위치·회전 설정을 시도하며 실제 성공 반환은 별도로 확인한다.

  • 선행 조건: 유효한 Pawn
  • 실행 범위: 위치·회전 설정 시도
  • 성공 판정: 설정 반환 결과 별도 확인
  • 미구현: 체력·인벤토리·레벨 적용
  • 로드 객체가 유효해도 Pawn과 설정 결과가 별도 조건이다. 설정을 시도한 사실만으로 전체 게임 상태 복원 완료를 확정하지 않는다.