Save/Load 시스템
USaveGame 클래스를 활용한 게임 데이터 저장과 로드를 구현합니다
SaveGame 클래스
UE5의 직렬화 기반 세이브 시스템
UE5는 USaveGame 클래스를 기반으로 게임 데이터를 직렬화(Serialize)하여 파일로 저장합니다. 블루프린트에서 우클릭 > Blueprint Class > Save Game으로 생성합니다.
BP_MySaveGame (Parent: SaveGame)
// 저장할 데이터를 변수로 정의
├─ PlayerName: String
├─ PlayerLevel: Integer
├─ PlayerLocation: Vector
├─ PlayerRotation: Rotator
├─ CurrentHealth: Float
├─ MaxHealth: Float
├─ Inventory: Array<S_ItemData>
├─ CompletedQuests: Array<Name>
├─ ActiveQuests: Map<Name, S_QuestProgress>
├─ PlayTime: Float
└─ SaveTimestamp: DateTime
세이브/로드 핵심 노드
| 노드 | 설명 |
|---|---|
| Create Save Game Object | SaveGame 인스턴스 생성 (메모리에만) |
| Save Game to Slot | 인스턴스를 파일로 저장 |
| Load Game from Slot | 파일에서 인스턴스 로드 |
| Does Save Game Exist | 세이브 파일 존재 확인 |
| Delete Game in Slot | 세이브 파일 삭제 |
저장(Save) 구현
게임 데이터를 수집하여 파일로 저장하기
// 함수: SaveGame(SlotName: String, UserIndex: Integer)
[Create Save Game Object]
└─ Save Game Class: BP_MySaveGame
│
└──> [Cast to BP_MySaveGame]
│
├──> [Set PlayerLocation] = GetActorLocation()
├──> [Set PlayerRotation] = GetActorRotation()
├──> [Set CurrentHealth] = HealthComponent.GetHealth()
├──> [Set Inventory] = InventoryComponent.GetAllItems()
├──> [Set PlayerLevel] = GetLevel()
├──> [Set SaveTimestamp] = Now()
│
└──> [Save Game to Slot]
├─ Save Game Object: (위 인스턴스)
├─ Slot Name: "SaveSlot_1"
├─ User Index: 0
└─ Return Value: Boolean (성공 여부)
Slot Name은 파일 이름으로 사용됩니다. 여러 세이브 슬롯을 지원하려면 "SaveSlot_1", "SaveSlot_2" 등으로 구분합니다. User Index는 로컬 멀티플레이어에서 플레이어를 구분하며, 싱글플레이어에서는 항상 0입니다.
로드(Load) 구현
저장된 데이터를 복원하여 게임 상태 재설정
// 함수: LoadGame(SlotName: String)
[Does Save Game Exist]
├─ Slot Name: "SaveSlot_1"
└─ Return: Boolean
│
└──> [Branch]
├─ True ──> [Load Game from Slot]
│ ├─ Slot Name: "SaveSlot_1"
│ ├─ User Index: 0
│ └─ Return: SaveGame
│ │
│ └──> [Cast to BP_MySaveGame]
│ │
│ ├──> [SetActorLocation](PlayerLocation)
│ ├──> [SetActorRotation](PlayerRotation)
│ ├──> [SetHealth](CurrentHealth)
│ ├──> [RestoreInventory](Inventory)
│ └──> [SetLevel](PlayerLevel)
│
└─ False ──> [Print "No save file found"]
비동기 저장/로드
대용량 데이터의 경우 Async Save Game to Slot과 Async Load Game from Slot을 사용하면 메인 스레드를 블로킹하지 않습니다.
// 비동기 저장
[Async Save Game to Slot]
├─ Save Game Object
├─ Slot Name
├─ User Index
└─ Delegate:
├─ On Success ──> [Show "Saved!" Notification]
└─ On Failure ──> [Show "Save Failed" Error]
// 비동기 로드
[Async Load Game from Slot]
├─ Slot Name
├─ User Index
└─ Delegate:
├─ On Success ──> (SaveGame Object 핀) ──> [Restore State]
└─ On Failure ──> [Start New Game]
Windows에서 세이브 파일은 [프로젝트]/Saved/SaveGames/ 폴더에 .sav 확장자로 저장됩니다. 패키지된 게임에서는 %LocalAppData%/[프로젝트명]/Saved/SaveGames/에 저장됩니다.
실전 세이브 시스템 설계
Game Instance 활용과 세이브 매니저 패턴
Game Instance를 세이브 매니저로 활용
UGameInstance는 레벨 전환 시에도 유지되므로, 세이브 관리의 중앙 허브로 적합합니다.
BP_GameInstance (Save Manager)
// 변수
├─ CurrentSaveData: BP_MySaveGame (Object Reference)
├─ CurrentSlotName: String = "SaveSlot_1"
│
// 함수
├─ SaveGame()
│ ├─ 현재 게임 상태를 CurrentSaveData에 기록
│ └─ Save Game to Slot(CurrentSaveData, CurrentSlotName, 0)
│
├─ LoadGame() → Boolean
│ ├─ Does Save Game Exist → Load Game from Slot
│ └─ CurrentSaveData에 저장, 성공 여부 반환
│
├─ GetSaveSlotInfo(SlotName) → S_SlotInfo
│ └─ 슬롯별 메타데이터 (레벨, 플레이 시간, 스크린샷)
│
└─ DeleteSave(SlotName)
└─ Delete Game in Slot
// 접근: Get Game Instance → Cast to BP_GameInstance
자동 저장 구현
// 체크포인트 Actor를 통한 자동 저장
BP_Checkpoint
└─ BoxCollision (OnOverlapBegin)
│
└──> [DoOnce] ──> [Get Game Instance]
└──> [Cast to BP_GameInstance]
└──> [SaveGame]
└──> [Show "Auto Saved"]
SaveGame에 Actor 참조를 직접 저장하면 안 됩니다 (로드 시 해당 Actor가 존재하지 않을 수 있음). 대신 Actor를 식별할 수 있는 Name, Transform, 클래스 정보를 저장하고 로드 시 재생성하세요. Widget, Material Instance 등의 런타임 오브젝트도 저장 불가합니다.
핵심 요약
- USaveGame 블루프린트에 저장할 변수를 정의하고, Save/Load Game to/from Slot으로 파일 입출력한다
- Does Save Game Exist로 파일 존재를 먼저 확인한 후 로드한다
- 대용량 데이터는 Async Save/Load를 사용하여 메인 스레드 블로킹을 방지한다
- GameInstance는 레벨 전환 시 유지되므로 세이브 매니저로 적합하다
- Actor 참조는 직접 저장할 수 없으며, 식별 가능한 데이터(Name, Transform)로 대체한다
- 세이브 파일은
Saved/SaveGames/폴더에.sav형식으로 저장된다
도전 과제
배운 내용을 직접 실습해보세요
USaveGame을 상속하는 BP_MySaveGame을 생성하고, PlayerName(String), HighScore(Integer), LastCheckpoint(Transform) 변수를 추가하세요. Save Game to Slot과 Load Game from Slot 노드를 사용하여 저장/불러오기를 구현하세요.
3개의 세이브 슬롯(Slot_1, Slot_2, Slot_3)을 관리하는 시스템을 만드세요. Does Save Game Exist로 슬롯 존재 여부를 확인하고, 각 슬롯의 저장 시간과 플레이 타임을 표시하는 UI를 구현하세요.
자동 저장(Auto Save) 시스템을 구현하세요. 타이머로 5분마다 자동 저장하고, 체크포인트 Trigger Volume 진입 시에도 저장합니다. 게임 설정(해상도, 볼륨, 키 바인딩)을 별도 슬롯에 저장하는 Settings Save 시스템도 분리 구현하세요.