체크포인트와 세이브 설계
SaveGame에 진행 상태를 정의하고 체크포인트 저장·슬롯 로드·실패 복구 흐름을 설계해 재개 기능을 구현합니다.
지금까지는 게임 핵심 로직과 네트워크 동기화를 다뤘습니다.
이제 플레이어가 중단 후 재개할 수 있도록 진행 상황 저장/불러오기 시스템을 설계해야 합니다.
이 기능은 플레이어 경험에 직접 영향을 주므로 게임 디자인 단계부터 신중히 고려해야 합니다.
이번 절에서는 체크포인트(Checkpoint)와 세이브/로드(Save/Load) 구현의 기본 개념과 설계 고려 사항을 정리합니다.
체크포인트가 만든 스냅샷을 저장하고, 로드할 때 의존 순서대로 복원합니다.
- 1체크포인트 갱신과 로드 복원 순서
트리거가 발동하면 이번 진행을 저장할 시점을 확정합니다. ↓ 2 월드 진행도·플레이어 상태·퀘스트 키를 SaveGame 데이터로 복사합니다. ↓ 3 슬롯 이름·사용자 인덱스·데이터 버전을 함께 기록합니다. ↓ 4 슬롯을 읽은 뒤 월드 → 플레이어 → 파생 UI 순서로 상태를 되살립니다.
- 2체크포인트 진입
트리거가 발동하면 이번 진행을 저장할 시점을 확정합니다.
- 3스냅샷 구성
월드 진행도·플레이어 상태·퀘스트 키를 SaveGame 데이터로 복사합니다.
- 4슬롯 기록
슬롯 이름·사용자 인덱스·데이터 버전을 함께 기록합니다.
- 5로드·순차 복원
슬롯을 읽은 뒤 월드 → 플레이어 → 파생 UI 순서로 상태를 되살립니다.
- 6복원 순서를 지키는 이유
파괴한 오브젝트와 열린 문을 먼저 복원해 플레이 공간을 확정합니다. 확정된 월드 위에 위치·체력·인벤토리를 적용합니다. 복원된 실제 상태를 읽어 퀘스트 표시와 HUD를 다시 계산합니다.
체크포인트와 세이브 시스템이란 무엇인가?
-
체크포인트(Checkpoint)
- 게임플레이 중 특정 지점에 도달했을 때, 플레이어의 현재 상태(위치, 체력, 인벤토리 등)를 자동으로 저장하는 지점입니다.
- 주로 플레이어가 사망했을 때 마지막 체크포인트에서 다시 시작하도록 하여, 플레이어가 좌절하지 않고 게임을 계속 진행할 수 있도록 돕습니다.
- 체크포인트는 일반적으로 플레이어의 명시적인 행동 없이 자동으로 이루어집니다.
-
세이브/로드(Save/Load) 시스템
- 플레이어가 원할 때 게임의 진행 상황을 파일로 저장하고, 나중에 이 파일을 불러와 게임을 재개할 수 있도록 하는 시스템입니다.
- 게임의 모든 중요한 상태(전역 변수, 레벨 상태, 퀘스트 진행도 등)를 저장하고 불러올 수 있어야 합니다.
왜 세이브/로드 시스템이 필요한가?
- 플레이어 편의성: 플레이어가 원하는 시간에 게임을 중단하고 다시 시작할 수 있도록 합니다.
- 진행 상황 유지: 플레이어가 공들여 쌓은 게임 진행 상황과 성과를 잃지 않도록 보장합니다.
- 게임 경험 개선: 플레이어가 실패했을 때 너무 많은 진행 상황을 잃지 않도록 하여 재시도를 독려하고 게임 진입 장벽을 낮춥니다.
- 디버깅 및 개발: 개발 과정에서도 특정 지점부터 테스트하거나 재현하는 데 유용합니다.
언리얼 엔진의 세이브 시스템
언리얼 엔진은 게임 데이터를 저장하고 불러오는 데 SaveGame 클래스를 사용합니다.
이 클래스는 마치 일반적인 블루프린트처럼 변수를 선언하고 값을 저장할 수 있는 데이터 컨테이너 역할을 합니다.
SaveGame 클래스의 특징
- 비-액터(Non-Actor) 클래스:
SaveGame클래스는 월드에 존재하지 않는 순수 데이터 클래스입니다. 액터가 아니므로 틱(Tick)이 없고, 메시나 콜리전 같은 컴포넌트도 없습니다. - 직렬화(Serialization):
SaveGame오브젝트에 저장된 변수들은 자동으로 바이너리 파일로 직렬화되어 디스크에 저장됩니다. - 데이터 컨테이너: 게임의 중요한 데이터를 담는 용도로만 사용됩니다.
세이브/로드 시스템 설계 및 구현 단계
저장은 객체 자체를 보관하는 일이 아니라 복원에 필요한 값만 추출해 슬롯과 월드 사이를 왕복시키는 흐름이다.
- 1Collect
위치, HP, 인벤토리처럼 다시 만들 값만 고른다.
- 2Serialize
SaveGame 객체의 원시 값과 작은 구조체로 복사한다.
- 3Save Slot
Slot Name과 User Index를 키로 디스크에 기록한다.
- 4Load Slot
슬롯을 읽고 저장 버전에 맞는 데이터인지 확인한다.
- 5Apply
캐릭터와 월드 액터를 찾은 뒤 값을 다시 적용한다.
- 6저장 범위
Actor 전체가 아니라 복원 가능한 데이터만 담는다.
- 7버전 경계
구조 변경에 대비해 SaveVersion과 변환 규칙을 둔다.
- 8실패 경계
슬롯 없음, 읽기 실패, 적용 실패를 서로 분리한다.
저장할 데이터 정의
먼저 게임에서 어떤 데이터들을 저장해야 할지 결정하고, 이를 담을 SaveGame 블루프린트를 만듭니다.
새 블루프린트 클래스 생성: 콘텐츠 브라우저에서 마우스 오른쪽 버튼 클릭 > 블루프린트 클래스(Blueprint Class) > All Classes에서 SaveGame을 검색하여 선택합니다.
- 이름을 지정합니다. (예:
BP_MySaveGame)
저장할 변수 추가: BP_MySaveGame 블루프린트를 열고, 게임의 상태를 나타내는 필요한 변수들을 추가합니다.
- 플레이어 관련:
PlayerLocation(Vector),PlayerHealth(Float),PlayerInventory(Array of Structs),CurrentWeapon(Enum/String) - 게임 진행 관련:
CurrentLevelName(Name),QuestProgress(Map/Struct),UnlockedAbilities(Array of Enum) - 월드 상태 관련:
DestroyedEnemies(Array of UniqueIDs),CollectedItems(Array of UniqueIDs) - 중요:
SaveGame클래스에 저장할 변수들은SaveGame클래스 자체가 지원하는 변수 타입이어야 합니다. 일반적으로 기본 변수 타입(Int, Float, Bool, String, Vector, Rotator, Transform 등), Enum, Struct, 그리고 이들의 배열 타입은 지원됩니다. 액터 레퍼런스(Actor Reference)나 컴포넌트 레퍼런스는 직접 저장할 수 없으며, 대신 해당 액터의Unique ID나Tag, 또는 액터를 식별할 수 있는 다른 방식으로 정보를 저장하고 로드 시 다시 액터를 찾아 설정해야 합니다.
체크포인트 시스템 구현
체크포인트 액터를 만들고, 플레이어가 체크포인트에 도달하면 자동으로 게임 상태를 저장하도록 합니다.
BP_Checkpoint 액터 생성- 새로운 액터 블루프린트(예:
BP_Checkpoint)를 만들고,Box Collision컴포넌트와Static Mesh(시각적 표시용)를 추가합니다. Box Collision컴포넌트에서On Component Begin Overlap이벤트를 생성합니다.
On Component Begin Overlap이벤트에서Other Actor를Cast To BP_PlayerCharacter로 캐스팅합니다.- 캐스팅 성공 시,
Get Game Mode노드를 호출하고 여러분의 게임 모드(GM_MyGame)로 캐스팅합니다. GM_MyGame에서Save Game커스텀 이벤트를 호출하도록 연결합니다. (또는GM_MyGame에서 해당 로직을 직접 구현)- 중복 저장 방지: 한 번 저장된 체크포인트는 다시 저장되지 않도록,
BP_Checkpoint내부에bIsActivated와 같은 불리언 변수를 추가하고, 저장 후True로 설정한 뒤 다음 오버랩 시 이 변수를 확인하는 로직을 추가합니다.
게임 저장 및 불러오기 로직
게임 저장/불러오기 로직은 보통 GameMode (싱글 플레이어 게임) 또는 PlayerController (멀티플레이어 게임의 경우 클라이언트 측 저장)에서 구현됩니다.
여기서는 GameMode를 기준으로 설명합니다.
GM_MyGame 블루프린트 열기세이브 슬롯 이름 정의: Save Slot Name 변수(String)를 생성하고 MyGameSaveSlot 등으로 이름을 지정합니다. (여러 개의 세이브 파일을 관리할 경우 슬롯 번호 등을 추가)
SaveGame)SaveGame이라는 커스텀 이벤트를 생성합니다.Does Save Game Exist노드를 사용하여 해당 슬롯에 기존 세이브 파일이 있는지 확인합니다.Slot Name핀에 미리 정의한Save Slot Name변수를 연결합니다.
-
True(기존 세이브 파일 있음)Load Game From Slot노드를 호출하여 기존SaveGame오브젝트를 로드합니다.Return Value를Cast To BP_MySaveGame으로 캐스팅합니다.- 로드된
BP_MySaveGame오브젝트에 현재 게임 상태 데이터를 업데이트합니다. (예:Set PlayerLocation,Set PlayerHealth등)
-
False(기존 세이브 파일 없음)Create SaveGame Object노드를 호출하여 새로운BP_MySaveGame오브젝트를 생성합니다.SaveGame Class핀에BP_MySaveGame을 선택합니다.Return Value를Cast To BP_MySaveGame으로 캐스팅합니다.
-
공통 로직 (새로 생성했든 로드했든)
- 플레이어의 현재 위치:
Get Player Pawn>Get Actor Location→BP_MySaveGame의PlayerLocation변수에 저장. - 플레이어의 현재 체력:
Get Player Pawn> (Cast to PlayerCharacter) >Get Health→BP_MySaveGame의PlayerHealth변수에 저장. - 다른 모든 저장할 데이터들도 유사하게
BP_MySaveGame오브젝트의 해당 변수에 업데이트합니다. - 최종적으로
Save Game To Slot노드를 호출합니다.SaveGame Object핀에 업데이트된BP_MySaveGame오브젝트를 연결합니다.Slot Name핀에Save Slot Name변수를 연결합니다.User Index는 보통0으로 설정합니다. (단일 유저 세이브)
Print String등으로 게임 저장 완료! 메시지를 출력합니다.
- 플레이어의 현재 위치:
LoadGame)LoadGame이라는 커스텀 이벤트를 생성합니다. (UI 버튼 클릭 시 호출되도록)Does Save Game Exist노드를 사용하여 해당 슬롯에 세이브 파일이 있는지 확인합니다.False일 경우,Print String으로 저장된 게임이 없습니다! 메시지를 출력하고 함수 종료.
-
True일 경우Load Game From Slot노드를 호출하여BP_MySaveGame오브젝트를 로드합니다.Return Value를Cast To BP_MySaveGame으로 캐스팅합니다.- 로드된
BP_MySaveGame오브젝트의 변수 값을 읽어서 게임 상태를 복원합니다.- 플레이어 위치:
Get Player Pawn>Set Actor Location을 호출하고PlayerLocation변수 값을 연결합니다. - 플레이어 체력:
Get Player Pawn> (Cast to PlayerCharacter) >Set Health를 호출하고PlayerHealth변수 값을 연결합니다. - 레벨 로드: 만약 저장된 레벨 이름(
CurrentLevelName)이 현재 레벨과 다르다면,Open Level (by Name)노드를 호출하여 해당 레벨로 이동합니다. (이 경우 레벨 로드 후On Level Loaded이벤트에서 다시 플레이어 위치 등을 설정해야 할 수 있습니다.) - 월드 상태 복원:
DestroyedEnemies나CollectedItems같은 배열을 순회하며 해당 액터들을 월드에서 파괴하거나 숨기는 로직을 구현합니다. (이를 위해 액터들에게 고유한 ID를 부여하는 것이 중요합니다.)
- 플레이어 위치:
Print String등으로 게임 불러오기 완료! 메시지를 출력합니다.
UI 버튼에 연결 (선택 사항)
메인 메뉴나 인게임 메뉴의 세이브/로드 버튼에 위에서 구현한 SaveGame과 LoadGame 함수를 연결합니다.
- 버튼
On Clicked이벤트 →Get Game Mode(또는Get Player Controller) →Cast To GM_MyGame→SaveGame또는LoadGame함수 호출.
SaveGame에는 월드를 그대로 복사하는 대신 다시 만들 수 있는 최소 정보만 남깁니다. 값은 직접 저장하고, 액터는 ID와 Transform으로 복원하며, 일시 효과는 저장 대상에서 제외합니다.
- slot design저장 전에 물어볼 세 가지
slot design 값인가? 위치, 체력, 레벨 이름처럼 기본 타입이면 SaveGame 변수에 직접 넣습니다. 액터인가? Actor 참조 대신 고유 ID, Class, Transform, Tag를 저장하고 로드 때 찾아 복원합니다. 다시 계산되나? 이펙트, 임시 타이머, 짧은 계산값은 BeginPlay나 로직으로 재생성하는 편이 안정적입니다. 바뀔 구조인가? 업데이트 가능성이 있는 저장 구조는 SaveGameVersion을 두고 변환 경로를 마련합니다.
- 플레이어와 진행 상태
직접 저장 PlayerLocation, PlayerHealth, CurrentLevelName, QuestProgress처럼 로드 직후 바로 써야 하는 값입니다.
- 월드에 남거나 사라지는 액터
ID로 복원 DestroyedEnemies, CollectedItems는 액터 참조가 아니라 ID 배열로 저장해야 레벨 재진입 후 매칭됩니다.
- 순간 상태와 표시 효과
저장 제외 피격 플래시, 일회성 사운드, 짧은 보간값은 파일 크기와 오류 가능성만 늘리므로 다시 재생성합니다.
- 같은 레벨
슬롯을 읽은 뒤 현재 Pawn에 위치와 체력을 바로 적용합니다.
- 다른 레벨
Open Level 후 로드 완료 지점에서 저장값 적용을 한 번 더 호출합니다.
- 월드 상태
고유 ID를 기준으로 아이템 획득, 적 제거, 문 열림 상태를 재적용합니다.
세이브 시스템 설계 시 주요 고려사항
- 저장할 데이터의 범위: 모든 것을 저장할 필요는 없습니다. 다시 생성하거나 쉽게 유추할 수 있는 데이터는 저장하지 않아도 됩니다. (예: 일회성 이펙트, 단기 계산 변수)
- 복잡한 액터 저장: 액터 자체를 저장하는 것은 불가능합니다. 액터의
Unique ID(또는Tag),Class,Transform등의 핵심 정보를 저장하고, 로드 시 해당 정보를 바탕으로 액터를 다시 스폰하거나 찾아 데이터를 복원해야 합니다.- 특히 월드에 이미 배치되어 있는 액터(Placed Actor)의 경우, 그 액터의 고유한 이름을 저장하고, 로드 시
Get All Actors Of Class등으로 해당 액터를 찾아 데이터를 복원하는 방식이 흔히 사용됩니다.
- 특히 월드에 이미 배치되어 있는 액터(Placed Actor)의 경우, 그 액터의 고유한 이름을 저장하고, 로드 시
- 로드 시점: 게임을 로드할 때 어떤 레벨에서 로드할 것인지, 그리고 로드된 레벨에서 캐릭터가 어디에 스폰될지 등을 고려해야 합니다.
- 일반적으로 저장된 레벨로 이동 후, 해당 레벨의
BeginPlay또는 특정 로드 이벤트에서PlayerController나GameMode가 로드된 데이터를 바탕으로 플레이어와 월드 상태를 복원합니다.
- 일반적으로 저장된 레벨로 이동 후, 해당 레벨의
- 성능: 너무 많은 데이터를 저장하거나, 너무 자주 저장하면 성능에 영향을 미칠 수 있습니다. 필요한 최소한의 데이터만 저장하고, 저장 빈도를 조절합니다.
- 데이터 버전 관리: 게임 업데이트 시 세이브 파일의 구조가 변경될 수 있습니다.
SaveGame클래스에SaveGameVersion(Int) 변수를 추가하여, 로드 시 버전 불일치를 감지하고 이전 버전 데이터를 변환하는 로직을 구현할 수 있습니다. - 보안: 중요한 세이브 데이터(치트 방지)의 경우 암호화나 해시를 적용하여 변조를 방지할 수 있습니다.
- UI 피드백: 저장/로드 진행 중임을 플레이어에게 알려주는 UI(예: 저장 중..., 로딩 바)를 제공하여 사용자 경험을 개선합니다.
체크포인트와 세이브 시스템은 플레이어가 게임을 지속적으로 즐길 수 있도록 하는 필수적인 백본 시스템입니다.
잘 설계된 세이브 시스템은 플레이어의 몰입도를 높이고 게임의 만족도를 향상시킵니다.
세이브/로드 노드 흐름 표준 표현
- 저장 흐름:
입력 이벤트(체크포인트 오버랩 또는 Save 버튼)->분기(Does Save Game Exist)->핵심 노드(Create/Load SaveGame -> 변수 갱신 -> Save Game To Slot)->결과(슬롯 파일 갱신) - 불러오기 흐름:
입력 이벤트(Load 버튼)->분기(슬롯 존재 여부)->핵심 노드(Load Game From Slot -> 상태 복원 -> 필요 시 Open Level)->결과(플레이어/월드 상태 복원)
세이브/로드 실패 증상과 복구
-
증상: 저장 버튼을 눌러도 다음 실행에서 데이터가 사라짐
- 원인:
Save Slot Name불일치 또는Save Game To Slot미호출 - 복구: 저장/로드가 동일한 슬롯명을 쓰는지 확인하고, 저장 노드의 실행 핀 종료 지점에 로그를 남겨 실제 호출 여부를 검증합니다.
- 원인:
-
증상: 로드 직후 플레이어 위치가 원점으로 이동
- 원인:
Open Level이후 복원 로직이 재실행되지 않음 - 복구: 레벨 전환이 필요한 경우, 레벨 로드 완료 이벤트에서
Set Actor Location(PlayerLocation)을 다시 호출합니다.
- 원인:
-
증상: 수집 아이템 상태가 복원되지 않음
- 원인: 액터 참조 자체를 저장하려고 함
- 복구: 액터 레퍼런스 대신 고유 ID 배열을 저장하고, 로드 시 ID 매칭으로 월드 상태를 재적용합니다.
이번 절에서는 언리얼 엔진에서 체크포인트와 세이브/로드 시스템을 설계하고 구현하는 기본 개념과 SaveGame 클래스 활용법에 대해 알아보았습니다.
게임의 진행 상황을 안정적으로 저장하고 불러오는 것은 플레이어에게 매우 중요한 기능입니다.
세이브 시스템은 저장할 데이터, 슬롯명, 레벨 전환, 복원 시점을 명확히 나누면 실패 원인을 빠르게 찾을 수 있습니다.
플레이어 위치, 체력, 수집 상태, 퀘스트 진행은 저장할 수 있지만 월드 액터 자체는 ID와 Transform으로 다시 찾아야 합니다.
- 저장 이벤트
Trigger 체크포인트 오버랩, 메뉴 버튼, 자동 저장 조건에서 흐름을 시작합니다.
- 슬롯 확인
Slot `Does Save Game Exist`와 동일한 Slot Name을 저장과 로드에 사용합니다.
- 값 갱신
Write SaveGame 오브젝트 변수에 위치, 상태, ID 목록을 기록합니다.
- 월드 복원
Restore 레벨 로드 이후 플레이어와 수집 아이템 상태를 다시 적용합니다.
- 자주 깨지는 연결
저장과 로드의 이름이 다르면 파일이 있어도 없는 것처럼 보입니다. 수집 아이템과 파괴된 적은 액터 참조 대신 고유 ID로 추적합니다. 업데이트로 구조가 바뀌면 SaveGameVersion으로 변환 경로를 둡니다.
- Slot Name
저장과 로드의 이름이 다르면 파일이 있어도 없는 것처럼 보입니다.
- Unique ID
수집 아이템과 파괴된 적은 액터 참조 대신 고유 ID로 추적합니다.
- Version
업데이트로 구조가 바뀌면 SaveGameVersion으로 변환 경로를 둡니다.
릴리스 전에는 저장 전환, 불러오기, 버전 변경, 실패 복구를 한 루틴으로 묶어 세이브 파일 안정성을 검증합니다.
저장할 값, 슬롯명, 레벨 전환, 버전 변경이 따로 움직이면 플레이어 진행도가 깨집니다. 릴리스 전에는 같은 세이브 파일로 여러 경로를 통과시킵니다.
- data저장 값
data 플레이어 위치, 진행 플래그, 수집 ID처럼 다시 만들 수 없는 값만 남깁니다.
- slot슬롯 규칙
slot Slot Name 과 유저 인덱스를 저장과 로드에서 같은 상수로 사용합니다.
- level레벨 전환
level Open Level 뒤 복원 이벤트가 다시 호출되는지 로그로 확인합니다.
- version버전 관리
version 새 필드가 추가돼도 이전 파일을 읽고 기본값을 채우는지 테스트합니다.
마지막으로 체크포인트와 세이브를 연결할 때 저장 값, 트리거, 로드 적용 순서를 정리합니다.
체크포인트는 위치만 저장하는 기능이 아니라 레벨, 플레이어 상태, 진행 플래그를 함께 복원하는 약속입니다.
- 도달 순간과 복귀 순간을 분리
체크포인트에 들어갈 때 저장할 값을 모으고, 로드할 때는 레벨 준비와 Pawn 생성 이후 안전하게 적용합니다. 01 Level Name, Checkpoint ID, Player Transform, Health, Quest Flags처럼 다시 시작에 필요한 최소 상태를 SaveGame에 둡니다. 02 Checkpoint Actor의 Overlap은 중복 저장을 막고 가장 최근 Checkpoint ID만 갱신합니다. 03 Load Game from Slot 후 레벨 이동, Pawn 위치 설정, UI 갱신 순서로 적용해 BeginPlay와 충돌을 줄입니다.
- 도달 순간과 복귀 순간을 분리
체크포인트에 들어갈 때 저장할 값을 모으고, 로드할 때는 레벨 준비와 Pawn 생성 이후 안전하게 적용합니다.
- 저장 값
Level Name, Checkpoint ID, Player Transform, Health, Quest Flags처럼 다시 시작에 필요한 최소 상태를 SaveGame에 둡니다.
- 트리거 처리
Checkpoint Actor의 Overlap은 중복 저장을 막고 가장 최근 Checkpoint ID만 갱신합니다.
- 로드 적용
Load Game from Slot 후 레벨 이동, Pawn 위치 설정, UI 갱신 순서로 적용해 BeginPlay와 충돌을 줄입니다.
체크포인트와 세이브 설계에서는 슬롯명, 저장 범위, 로드 시점, 버전 변경 대응을 한 번 더 점검합니다.