본문으로 건너뛰기

안동민 개발노트

본문 시작

저장 및 로드 기본 구현

SaveGame 클래스를 정의하고 슬롯에 데이터를 저장·불러와 게임을 다시 실행해도 상태가 유지되도록 구현합니다.

게임 개발에서 데이터 저장(Save)과 로드(Load) 기능은 필수적입니다.

플레이어가 게임을 껐다가 다시 켰을 때, 이전에 진행했던 상태를 그대로 이어서 플레이할 수 있어야 하기 때문이죠.

단순히 점수나 설정뿐만 아니라, 플레이어의 위치, 인벤토리, 퀘스트 진행도, 월드 상태 등 다양한 데이터를 저장하고 불러올 수 있어야 합니다.

언리얼 엔진은 이러한 저장 및 로드 기능을 쉽게 구현할 수 있도록 저장 게임 오브젝트(Save Game Object) 시스템을 제공합니다.


저장 게임 오브젝트란 무엇인가?

저장 게임 오브젝트(Save Game Object)는 게임에 저장하고 싶은 모든 데이터를 담는 특별한 종류의 오브젝트입니다.

SaveGame 오브젝트 자체는 메모리의 객체입니다. 현재 값을 채운 뒤 슬롯 저장을 성공해야 디스크에 기록되며, 다음 실행에서는 저장값으로 새 메모리 객체를 복원합니다. 객체 생성만으로 자동 저장되거나 앱 종료 뒤 객체가 살아 있는 것은 아닙니다.

저장 게임 오브젝트는 다음과 같은 특징을 가집니다.

  • 영속성(Persistence): 슬롯에 성공적으로 기록한 데이터는 다음 게임 실행에서 다시 읽을 수 있습니다.
  • 사용자 정의 가능: 개발자가 저장하고 싶은 데이터(변수)를 직접 정의할 수 있습니다.
  • 블루프린트 접근성: 블루프린트에서 쉽게 생성하고 데이터를 읽고 쓸 수 있습니다.
  • 플랫폼 독립성: 내부적으로 파일 시스템을 다루는 복잡한 과정을 언리얼 엔진이 처리해주므로, 개발자는 저장/로드 로직에만 집중할 수 있습니다.

저장 게임 오브젝트 생성 및 정의

가장 먼저 할 일은 어떤 데이터를 저장할지 정의하는 것입니다.

이는 저장 게임 오브젝트 블루프린트를 생성하여 그 안에 저장할 변수들을 정의하는 방식으로 이루어집니다.

저장 게임 블루프린트 생성
  • 콘텐츠 브라우저에서 마우스 오른쪽 버튼을 클릭합니다.
  • 블루프린트 클래스(Blueprint Class)를 선택합니다.
  • 모든 클래스(All Classes)를 펼친 후 SaveGame을 검색하여 선택합니다.
  • 이름을 지정합니다. (예: BP_MySaveGame - 일반적으로 BP_ 접두사를 사용합니다.)
저장할 변수 추가
  • 생성된 BP_MySaveGame 블루프린트 에디터를 엽니다.
  • 변수 패널에서 저장하고 싶은 데이터를 나타내는 변수들을 추가합니다.
    • 예시
      • PlayerScore (Integer)
      • PlayerLocation (Vector)
      • UnlockedLevels (Array of Integer)
      • PlayerName (Text)
  • 컴파일하고 저장합니다.

이제 BP_MySaveGame은 게임 상태를 담을 수 있는 데이터 컨테이너가 되었습니다.


데이터 저장(Save) 기본 구현

월드 상태와 저장 객체·슬롯의 경계

월드 상태와 저장 객체·슬롯의 경계

저장과 복원은 서로 다른 방향의 처리저장할 때 월드 값을 SaveGame 메모리 객체로 복사하고 슬롯에 기록합니다. 로드할 때 슬롯에서 메모리 객체를 복원하고 검증한 값을 월드에 적용합니다.월드 상태SaveGame메모리 객체슬롯 기록복사저장적용로드
저장과 복원은 서로 다른 방향의 처리저장할 때 월드 값을 SaveGame 메모리 객체로 복사하고 슬롯에 기록합니다. 로드할 때 슬롯에서 메모리 객체를 복원하고 검증한 값을 월드에 적용합니다.월드 상태SaveGame메모리 객체슬롯 기록복사적용저장로드

로드는 저장값을 메모리 객체로 복원합니다. 월드에 적용하는 작업과 저장 성공 여부 확인은 호출자의 로직입니다.

데이터 저장 과정은 주로 플레이어의 입력(예: S 키), 특정 이벤트(예: 체크포인트 도달, 레벨 전환), 또는 게임 종료 시점에 이루어집니다.

저장 로직을 구현할 블루프린트 열기: (예: BP_PlayerCharacter 또는 BP_GameManager)

Create SaveGame Object 노드
  • 입력을 받도록 구성된 블루프린트에서 저장 입력(예: S 키 또는 대응하는 Input Action)에 Create SaveGame Object를 연결합니다.
  • Save Game Class 핀에 여러분이 만든 BP_MySaveGame을 지정합니다.
  • 이 노드는 새로운 BP_MySaveGame 인스턴스를 메모리에 생성하여 반환합니다.
데이터 채우기 (캐스팅 후 변수 설정)
  • Create SaveGame Object의 Return Value 출력 핀에서 드래그하여 Cast To BP_MySaveGame 노드를 연결합니다. (생성된 오브젝트를 실제 클래스 타입으로 캐스팅하여 접근하기 위함입니다.)
  • Cast To BP_MySaveGame의 As BP My Save Game 출력 핀에서 드래그하여 Set PlayerScore (또는 여러분이 정의한 다른 변수)와 같은 노드를 추가합니다.
  • 캐스트 성공 실행 선에서 각 Set 노드로 순서대로 연결합니다. GameInstance 점수와 유효한 플레이어 캐릭터의 Get Actor Location 값을 저장 객체에 복사합니다. GameManager에서 실행한다면 자신의 위치가 아니라 플레이어 참조를 Target으로 사용합니다.
Save Game to Slot 노드
  • 데이터를 모두 채운 후, Set 노드의 실행 핀 뒤에 Save Game to Slot 노드를 추가합니다.
  • SaveGame Object 핀에는 Cast To BP_MySaveGame의 As BP My Save Game 출력 핀을 연결합니다.
  • Slot Name 핀에는 저장 슬롯 이름을 String 형태로 입력합니다. (예: "SaveSlot1") 이 이름은 게임 내에서 저장 파일을 구분하는 데 사용됩니다.
  • User Index는 저장하는 플랫폼 사용자의 인덱스이며 일부 플랫폼에서는 무시됩니다. 네트워크 플레이어 ID와 같은 개념이 아닙니다. 이 단일 사용자 예제에서는 저장·로드 모두 0을 사용합니다.
  • Return Value (불리언)를 Branch에 연결해 성공과 실패를 구분합니다. 실패했는데 완료 메시지를 표시하지 않도록 하고, 재시도나 사용자 안내를 처리합니다.

데이터 로드(Load) 기본 구현

데이터 로드 과정은 주로 게임 시작 시(예: Event BeginPlay에서 로드 확인), 또는 메인 메뉴에서 이어하기 버튼을 눌렀을 때 이루어집니다.

로드 로직을 구현할 블루프린트 열기: (예: BP_PlayerCharacter 또는 BP_GameManager)

Does SaveGame Exist 노드 (선택 사항, 안정성)
  • 로드를 시도하기 전에, 먼저 해당 슬롯 이름으로 저장된 파일이 존재하는지 확인하는 것이 좋습니다.
  • Event BeginPlay (또는 로드 버튼 클릭 이벤트) 뒤에 Does SaveGame Exist 노드를 추가합니다.
  • Slot Name과 User Index를 저장 시 사용했던 것과 동일하게 입력합니다.
  • 이 노드의 Return Value를 Branch 노드의 Condition에 연결합니다.
    • True (저장 파일 존재): 로드 로직으로 진행합니다.
    • False (저장 파일 없음): 새 게임 시작 로직으로 진행하거나, 로드 실패 메시지를 출력합니다.
Load Game From Slot 노드
  • Branch 노드의 True 실행 핀 뒤에 Load Game From Slot 노드를 추가합니다.
  • Slot Name과 User Index를 저장 시 사용했던 것과 동일하게 입력합니다.
  • 읽기와 역직렬화가 성공하면 SaveGame 객체를 반환하며 실패하면 유효한 객체를 얻지 못할 수 있습니다. 슬롯 존재 확인은 로드 성공이나 저장된 클래스까지 보장하지 않습니다.
로드된 데이터 활용 (캐스팅 후 변수 가져오기)
  • Load Game From Slot의 Return Value 출력 핀에서 드래그하여 Cast To BP_MySaveGame 노드를 연결합니다.
  • 로드 실행 출력을 Cast To BP_MySaveGame의 실행 입력에 연결하고, 성공 경로에서만 As BP My Save Game으로 저장 변수들을 읽습니다. Cast Failed에서는 새 게임 기본값 또는 오류 안내로 분기합니다.
  • 점수·위치 등 값의 범위를 확인한 뒤 현재 GameInstance와 준비된 플레이어 액터에 적용합니다. Load Game From Slot 자체가 월드의 위치나 UI를 자동으로 변경하는 것은 아닙니다.
  • 예시: Set ActorLocation 노드를 연결하여 플레이어의 위치를 로드된 PlayerLocation으로 설정합니다.

로드 로직은 저장 파일이 항상 정상적으로 존재한다고 가정하지 않는 것이 중요합니다.


주의사항 및 고급 활용

  • 저장할 데이터만 SaveGame Object에: 저장 게임 오브젝트에는 정말로 저장해야 할 데이터만 포함해야 합니다. 임시적인 데이터나 게임 내에서 실시간으로 계산되는 데이터는 포함하지 않는 것이 좋습니다.
  • 비동기 저장/로드: 대용량 데이터를 저장하거나 로드할 때 게임이 잠깐 멈추는 현상(프레임 드롭)이 발생할 수 있습니다. 이를 방지하기 위해 Async Save Game to Slot 또는 Async Load Game from Slot과 같은 비동기 노드를 사용할 수 있습니다. 비동기 노드도 스냅샷 준비·직렬화·완료 처리 등의 비용이 있으므로 프레임 지연이 전혀 없다는 보장은 아닙니다. 완료 콜백과 성공 여부를 기준으로 다음 처리를 연결합니다.
  • 슬롯 이름 관리: 여러 개의 저장 파일을 지원하려면 각 저장 파일마다 고유한 Slot Name을 사용해야 합니다.
  • 유효성 검사: 캐스트의 성공/Cast Failed 실행 핀으로 분기합니다. IsValid는 별도 참조 검사이며 Cast의 출력 핀 이름이 아닙니다. 적용 대상 액터의 유효성도 확인해 Accessed None을 피합니다.
  • 버전 관리: 게임 업데이트 시 저장 게임 오브젝트의 구조가 변경될 수 있습니다. 이때 이전 버전의 저장 파일을 불러올 때 문제가 발생할 수 있으므로, 저장 게임 오브젝트에 버전 변수를 추가하여 호환성을 관리하는 고급 기법도 고려해야 합니다.

저장 및 로드 기능은 게임의 핵심적인 부분이며, 이 저장 게임 오브젝트 시스템을 통해 비교적 쉽게 구현할 수 있습니다.

복잡한 게임에서는 저장해야 할 데이터가 많아지므로, 데이터를 어떻게 구조화하고 어떤 시점에 저장/로드할지 신중하게 설계하는 것이 중요합니다.


이번 절에서는 언리얼 엔진의 저장 게임 오브젝트를 활용한 데이터 저장 및 로드의 기본 구현 방법에 대해 알아보았습니다.

이 기능은 플레이어의 진행 상황을 유지하고 몰입감을 높이는 데 필수적입니다.