체크포인트와 세이브 설계
지금까지 게임의 핵심 로직과 네트워크 동기화에 대해 알아보았습니다. 이제 플레이어가 게임을 중단했다가 나중에 다시 시작할 수 있도록 게임의 진행 상황을 저장하고 불러오는 시스템을 설계하는 방법에 대해 다룰 차례입니다. 이는 플레이어 경험에 필수적인 요소이자, 게임 디자인 단계에서부터 신중하게 고려해야 할 부분입니다.
이번 절에서는 언리얼 엔진에서 체크포인트(Checkpoint) 와 세이브/로드(Save/Load) 시스템을 구현하는 기본 개념과 설계 고려 사항에 대해 알아보겠습니다.
체크포인트와 세이브 시스템이란 무엇인가?
-
체크포인트(Checkpoint)
- 게임플레이 중 특정 지점에 도달했을 때, 플레이어의 현재 상태(위치, 체력, 인벤토리 등)를 자동으로 저장하는 지점입니다.
- 주로 플레이어가 사망했을 때 마지막 체크포인트에서 다시 시작하도록 하여, 플레이어가 좌절하지 않고 게임을 계속 진행할 수 있도록 돕습니다.
- 체크포인트는 일반적으로 플레이어의 명시적인 행동 없이 자동으로 이루어집니다.
-
세이브/로드(Save/Load) 시스템
- 플레이어가 원할 때 게임의 진행 상황을 파일로 저장하고, 나중에 이 파일을 불러와 게임을 재개할 수 있도록 하는 시스템입니다.
- 게임의 모든 중요한 상태(전역 변수, 레벨 상태, 퀘스트 진행도 등)를 저장하고 불러올 수 있어야 합니다.
왜 세이브/로드 시스템이 필요한가?
- 플레이어 편의성: 플레이어가 원하는 시간에 게임을 중단하고 다시 시작할 수 있도록 합니다.
- 진행 상황 유지: 플레이어가 공들여 쌓은 게임 진행 상황과 성과를 잃지 않도록 보장합니다.
- 게임 경험 개선: 플레이어가 실패했을 때 너무 많은 진행 상황을 잃지 않도록 하여 재시도를 독려하고 게임 진입 장벽을 낮춥니다.
- 디버깅 및 개발: 개발 과정에서도 특정 지점부터 테스트하거나 재현하는 데 유용합니다.
언리얼 엔진의 세이브 시스템
언리얼 엔진은 게임 데이터를 저장하고 불러오는 데 SaveGame
클래스를 사용합니다. 이 클래스는 마치 일반적인 블루프린트처럼 변수를 선언하고 값을 저장할 수 있는 데이터 컨테이너 역할을 합니다.
SaveGame
클래스의 특징
- 비-액터(Non-Actor) 클래스:
SaveGame
클래스는 월드에 존재하지 않는 순수 데이터 클래스입니다. 액터가 아니므로 틱(Tick)이 없고, 메시나 콜리전 같은 컴포넌트도 없습니다. - 직렬화(Serialization):
SaveGame
오브젝트에 저장된 변수들은 자동으로 바이너리 파일로 직렬화되어 디스크에 저장됩니다. - 데이터 컨테이너: 게임의 중요한 데이터를 담는 용도로만 사용됩니다.
세이브/로드 시스템 설계 및 구현 단계
저장할 데이터 정의
먼저 게임에서 어떤 데이터들을 저장해야 할지 결정하고, 이를 담을 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
함수 호출.
세이브 시스템 설계 시 주요 고려사항
- 저장할 데이터의 범위: 모든 것을 저장할 필요는 없습니다. 다시 생성하거나 쉽게 유추할 수 있는 데이터는 저장하지 않아도 됩니다. (예: 임시 이펙트, 단기적인 변수)
- 복잡한 액터 저장: 액터 자체를 저장하는 것은 불가능합니다. 액터의
Unique ID
(또는Tag
),Class
,Transform
등의 핵심 정보를 저장하고, 로드 시 해당 정보를 바탕으로 액터를 다시 스폰하거나 찾아 데이터를 복원해야 합니다.- 특히 월드에 이미 배치되어 있는 액터(Placed Actor)의 경우, 그 액터의 고유한 이름을 저장하고, 로드 시
Get All Actors Of Class
등으로 해당 액터를 찾아 데이터를 복원하는 방식이 흔히 사용됩니다.
- 특히 월드에 이미 배치되어 있는 액터(Placed Actor)의 경우, 그 액터의 고유한 이름을 저장하고, 로드 시
- 로드 시점: 게임을 로드할 때 어떤 레벨에서 로드할 것인지, 그리고 로드된 레벨에서 캐릭터가 어디에 스폰될지 등을 고려해야 합니다.
- 일반적으로 저장된 레벨로 이동 후, 해당 레벨의
BeginPlay
또는 특정 로드 이벤트에서PlayerController
나GameMode
가 로드된 데이터를 바탕으로 플레이어와 월드 상태를 복원합니다.
- 일반적으로 저장된 레벨로 이동 후, 해당 레벨의
- 성능: 너무 많은 데이터를 저장하거나, 너무 자주 저장하면 성능에 영향을 미칠 수 있습니다. 필요한 최소한의 데이터만 저장하고, 저장 빈도를 조절합니다.
- 데이터 버전 관리: 게임 업데이트 시 세이브 파일의 구조가 변경될 수 있습니다.
SaveGame
클래스에SaveGameVersion
(Int) 변수를 추가하여, 로드 시 버전 불일치를 감지하고 이전 버전 데이터를 변환하는 로직을 구현할 수 있습니다. - 보안: 중요한 세이브 데이터(치트 방지)의 경우 암호화나 해시를 적용하여 변조를 방지할 수 있습니다.
- UI 피드백: 저장/로드 진행 중임을 플레이어에게 알려주는 UI(예: "저장 중...", 로딩 바)를 제공하여 사용자 경험을 개선합니다.
체크포인트와 세이브 시스템은 플레이어가 게임을 지속적으로 즐길 수 있도록 하는 필수적인 백본 시스템입니다. 잘 설계된 세이브 시스템은 플레이어의 몰입도를 높이고 게임의 만족도를 향상시킵니다.
이번 절에서는 언리얼 엔진에서 체크포인트와 세이브/로드 시스템을 설계하고 구현하는 기본 개념과 SaveGame
클래스 활용법에 대해 알아보았습니다. 게임의 진행 상황을 안정적으로 저장하고 불러오는 것은 플레이어에게 매우 중요한 기능입니다.