Unity 저장 시스템(Save System) 튜토리얼
요약
본 튜토리얼은 Unity 엔진을 사용하여 게임의 저장 시스템(Save System) 구축 방법을 다룹니다. 플레이어 위치, 체력, 코인 개수 등 다양한 데이터를 JSON 형식으로 모듈화하여 저장하고 로드하는 과정을 단계별로 설명합니다.
핵심 포인트
- Unity에서 Save System 구현 방법을 배웁니다.
- 플레이어의 위치, 체력, 아이템 등의 데이터를 저장할 수 있습니다.
- JSON은 가볍고 확장성이 뛰어나 데이터 저장에 적합합니다.
- 모듈화된 구조를 통해 복잡한 게임 요소도 관리 가능합니다.
안녕하세요, 저는 Kudmaki입니다. 자, 그럼 여기에서 저장 시스템을 만드는 방법을 배워보겠습니다. 제가 캐릭터를 가지고 있고, 움직일 수 있고, 동전을 주울 수 있으며, 캐릭터는 피해를 입을 수도 있고, 열쇠를 주울 수도 있습니다. 그리고 세상의 오브젝트들과 상호작용할 수도 있습니다. 그런 다음 저장하기 위해 버튼을 누르면, 이 실제 저장 파일이 저장됩니다. 그리고 제가 게임을 멈추고 다시 플레이하면, 보세요, 모든 것이 이전과 정확히 똑같이 로드됩니다. 자, 여기에서 저장 시스템을 어떻게 하는지 봅시다. 그리고 중요한 점은, 이 저장 시스템은 매우 모듈화되어 있어서, 아주 간단한 것부터 아주 복잡한 것까지 모든 것을 저장할 수 있다는 것입니다. 정말로 이것을 기반으로 구축하는 것이며, 매우 모듈식이고, 매우 훌륭하고, 매우 강력합니다. 제가 여기서 가르쳐 드릴 것은 제 Steam 게임 대부분에서 사용했던 것과 동일합니다. 예를 들어, Dinky Guardians 같은 게임에는 엄청난 양의 오브젝트, 기계, 요소들이 포함되어 있습니다. 그리고 네, 이 모든 데이터는 제가 지금 보여드릴 정확한 프로세스를 사용하여 저장됩니다. 만약 단순히 드래그 앤 드롭만 하면 되는 것을 원하신다면, 실제로 제 CodeMonkeyToolKit에 저장 시스템이 있습니다. 이것은 게임을 더 좋고 빠르게 만드는 데 도움이 되는 50가지가 넘는 도구와 요소를 담고 있는 제 에셋입니다. 수백 시간을 절약하는 데 도움을 줄 엄청난 양의 도구를 포함하고 있으며, 현재 Unity 아이템 세일의 일부로 50% 할인 중이니 설명란의 링크를 확인해 보세요. 좋습니다. 자, 여기에서 저장 시스템을 만드는 방법을 배워봅시다. 시작점으로, 저는 플레이어 캐릭터와 간단한 장면(scene)을 가지고 있습니다. 움직일 수 있는 플레이어 캐릭터가 있고, 주울 수 있는 동전들이 있습니다. 캐릭터는 체력(health)이 있어서 제가 조금 피해를 입힐 수 있습니다. 그런 다음 열쇠도 주울 수 있습니다. 세상의 이 오브젝트들과 상호작용할 수도 있습니다. 이 상자를 밀거나 그 외에도 할 수 있습니다.
자, 여기 저장하고 싶은 데이터가 상당히 많이 있습니다. 그럼 시작하겠습니다. 하지만 항상 어떤 문제를 해결할 때 가장 작은 문제부터 푸는 것이 좋습니다. 이 모든 데이터를 어떻게 저장하는지 배우기 전에, 먼저 아주 간단한 것 하나만 저장하는 방법을 배워봅시다. 플레이어의 위치를 저장하는 것부터 시작해 보겠습니다. 이 프로젝트에는 간단한 플레이어 스크립트가 있습니다. 이것은 플레이어에 적용되어 실행됩니다. 여기에는 체력 시스템(health system), 코인 개수(coin amount), 열쇠 소지 여부(key)에 대한 참조가 있습니다. 아주 기본적인 플레이어 스크립트를 가지고 있는 거죠. 그리고 모든 것이 기본적으로 여기에 저장된 이유는 단순히 간단한 데모이기 때문입니다. 더 복잡한 게임에서는 이러한 요소들을 훨씬 더 많이 분리할 것입니다. 하지만 다시 말하지만, 간단한 데모를 위해 가능한 한 단순하게 유지해 봅시다. 제가 말씀드린 것처럼, 우선 플레이어의 위치만 저장하는 것을 시도해 보겠습니다. 그 위치란 문자 그대로 transform.position을 가져오는 것입니다. 이것이 우리가 저장하고 싶은 데이터 조각입니다. 그리고 이것은 실제로 X, Y, Z 위치를 가진 벡터 3(Vector3)입니다. 데이터를 저장하려면 우리의 저장 파일에 대한 일종의 형식을 생각해내야 합니다. 이를 위해 할 수 있는 것은 무수히 많습니다. 이진 데이터(binary data)를 직접 저장할 수도 있고, 사용자 지정 데이터 형식(custom data format)을 만들 수도 있습니다. 아니면 가장 간단한 것 중 하나를 사용할 수도 있습니다. JSON을 사용할 수 있습니다. 이것은 매우 간단하고 가벼운 데이터 형식입니다. 하지만 매우 간단하고 가볍지만, 실제로는 매우 확장성이 뛰어납니다. 원하는 거의 모든 것을 저장하는 데 사용할 수 있습니다. JSON에 대해 모른다면 공식 웹사이트에서 확인해 볼 수 있습니다. 아주 간단한 데이터 형식입니다. 하지만 동시에 그것에 대해 알 필요는 없습니다. 우리가 알아야 할 것은 JSON이 정말로 문자열(string)이라는 것입니다. 따라서 이 위치를 문자열로 변환한 다음 파일에 저장하고, 다시 벡터 3으로 파싱할 수 있습니다.
그리고 Unity에는 실제로 매우 유용한 JSON 유틸리티 클래스가 있습니다. 이 클래스는 두 가지 함수를 가지고 있습니다. 기본적으로 JSON에서 무언가를 파싱(parse)하는 기능과, 무언가를 JSON으로 변환하는 ToJson이 있습니다. 여기서는 단순히 ToJson을 호출하고, 그 안에 우리가 JSON으로 저장하고 싶은 객체 같은 것을 넣습니다. 이것은 본질적으로 우리가 가진 save object가 될 것입니다. 따라서 save object를 정의합니다. 우리가 무엇을 저장하고 싶은지 정의하는 것이죠. 그리고 이를 JSON으로 변환한 다음, 결국 다시 원래의 형태로 되돌립니다. 자, 정확히 그것을 정의해 봅시다. save object라고 부르는 클래스를 정의합시다. 그리고 그 안에 우리가 저장하고 싶은 것을 넣습니다. 제가 말했듯이, 위치(position)를 저장하고 싶습니다. 그래서 position이라고 이름 붙인 Vector3를 만듭니다. 이런 식으로요. 그리고 여기서는 ToJson을 사용하여 이 객체, 즉 save object를 구성합시다. 그 안에 position이 transform.position과 같도록 채워 넣는 겁니다. 따라서 우리는 본질적으로 이 객체를 구성한 다음, 이를 JSON으로 변환합니다. 그리고 여기에는 우리의 JSON이 있습니다. 왜냐하면 이것은 우리의 JSON 문자열을 반환할 것이기 때문입니다. 그래서 이렇게 됩니다. 그리고 이 JSON 문자열에 대해 디버그 로그(debug log)를 찍어 봅시다. 좋습니다, 한번 보겠습니다. 그리고 즉시 우리가 볼 수 있고, 만약 작동하는 것을 본다면요. 여기에는 우리의 JSON 문자열이 있습니다. 그리고 JSON을 알고 있다면 아실 겁니다. 네, 이것은 확실히 그 형식입니다. 그래서 우리는 기본적으로 중괄호(curly braces)를 얻게 되어 객체를 시작하고 끝내는 역할을 합니다. 그런 다음 안에는 필드(field)가 있고, 필드의 이름이 있습니다. 그것은 position이라고 이름 붙여져 있습니다. 그리고 그 안에는 X, Y, Z 좌표가 저장됩니다. 그리고 여기에는 다양한 값들이 있습니다. 좋습니다, 멋지네요. 네, 이렇게 해서 기본적으로 데이터 조각을 JSON으로 변환하는 방법을 보았습니다. 이제 본질적으로 반대편, 즉 어떻게 얻는지 봅시다. 그것은 JsonUtility의 FromJson을 사용하는 것입니다. 그리고 어떤 타입을 파싱할지 알기 위해 제네릭(generics)을 사용해야 합니다. 이 경우, save object에 파싱하는 것이죠. 그리고 JSON 문자열을 전달합니다.
그리고 여기 JSON의 경우, 콘솔에 있는 것을 복사하여 붙여넣겠습니다. 콘솔을 보시면 여기에 위치(position)가 있습니다. 이것을 그냥 복사하겠습니다. 그리고 여기요, 네, 그냥 위치만요, 이렇게요. 그래서 이것이 생겼습니다. 이전 JSON은 주석 처리하겠습니다. 이제 이 것이 정확히 무엇을 반환하는지 보겠습니다. 여기서 반환되는 객체를 봅시다. 여기는 save object, save object을 반환합니다. 그리고 save object의 position을 출력해 보겠습니다. 좋습니다. 이제 여기에 표시된 것과 동일한 위치를 나타내는지 보겠습니다. 알겠습니다, 네, 여기 있습니다. 마이너스 8, 0.05, 그리고 마이너스 4.8입니다. 그리고 이것이 우리가 가지고 있는 것과 정확히 일치한다면요. 좋습니다, 정말 멋집니다. 솔직히 말해서, 이것은 save 시스템이 작동하는 전체 과정이라고 할 수 있습니다. 저희는 save object를 정의하고, 그것을 JSON으로 변환한 다음, 그 JSON을 다시 save object로 파싱합니다. 하지만 물론, 이 데이터를 저장해서 그냥 메모리에만 두고 싶지는 않습니다. 실제 파일에 저장하고 싶습니다. 다행히도 이것 역시 매우 간단합니다. 그러니 다시 save 로직으로 돌아가서 어떻게 작동하는지 보겠습니다. 여기에는 JSON 문자열이 있습니다. 그리고 그것을 파일에 쓰려면 매우 간단합니다. system.io 내부에서 이 것을 사용할 수 있습니다. 여기가 네임스페이스입니다. 그리고 write all text를 할 수 있습니다. 왜냐하면 JSON은 본질적으로 텍스트 문자열이기 때문입니다. 그럼 JSON을 작성해 보겠습니다. 그리고 우리가 작성하고 싶은 경로의 경우, Unity는 게임의 데이터 폴더에 접근할 수 있는 몇 가지 상수(constants)를 가지고 있습니다. 그래서 web unlock을 사용하여 해보겠습니다. 볼 수 있도록요. 실제로 두 개가 있습니다. 하나는 application이고 여기에 data path가 있습니다. 다른 하나는 application.persistent data path입니다. 이 둘 다 볼 수 있도록 로그를 찍어보겠습니다. 줄 바꿈을 하겠습니다. 그리고 여기에서 볼 수 있습니다. 첫 번째 것은 기본적으로 접근 폴더를 반환합니다.
그리고 두 번째 것은 기본적으로 현재 로그인한 사용자에게 있는 경로를 반환합니다. 따라서 이 두 가지 중 제가 보통 사용하는 것은 데이터 경로(data path)입니다. 저장하는 것을 위해 다시 한번 저장해 보겠습니다. 텍스트에서 바로 애플리케이션의 데이터 경로를 찾았습니다. 그리고 'saves'와 같은 폴더, 'saves'를 추가하고요. 그리고 'savefile.save'와 같은 저장 파일이 필요합니다. 참고로 확장자(extension)는 사실 크게 중요하지 않습니다. 원하는 것을 무엇이든 넣을 수 있습니다. 상관없습니다. 하지만 동일한 파일이라면 보통 .save나 .dat가 적절할 것 같습니다. 그럼 이렇게 저장해 보겠습니다. 디렉토리가 존재하는지 확인하는 코드를 추가하지 않았기 때문에, 'saves' 폴더를 수동으로 생성하도록 하겠습니다. 그리고 이제 테스트해 보겠습니다. 자, 멈추겠습니다. 가보겠습니다. 프로젝트 파일 쪽을 살펴보겠습니다. 여기 있습니다. savefile이 있네요. 이것을 열어보면요. 여기에 있습니다. 정확히 같은 데이터로 저장된 savefile이 있습니다. JSON을 어떤 종류의 저장 파일에 넣는 것이 얼마나 간단한지 알 수 있죠. 그리고 반대로 해보겠습니다. 읽기(read) 작업은 그만큼 쉽습니다. 이 코드로 돌아가서, 문자열 JSON의 경우 file.ReadAllText를 사용하면 됩니다. 텍스트를 읽어 보겠습니다. 같은 savefile에서 다시 한번 읽어 보겠습니다. 저희는 JSON을 반환할 것이고, 이렇게 출력합니다. 그럼 빠르게 테스트해 보겠습니다. 콘솔에 보입니다. 좋습니다. 정말 멋지네요. 따라서 본질적으로 여기에는 어떤 종류의 저장 시스템을 만들 데 필요한 모든 핵심 구성 요소가 이미 갖춰져 있습니다. save 객체를 정의하고, 그것을 JSON으로 변환한 다음, 파일에 저장합니다. 그리고 반대로, 기본적으로 읽고(read), JSON을 파싱(parse)하여 원하는 변경 사항을 적용하기만 하면 됩니다. 다시 한번, 이 모든 것을 실제 사용 사례로 통합해 보겠습니다. 제가 말했듯이, 플레이어의 실제 위치를 여기에 저장해 보겠습니다.
저장 객체(save object)의 경우, 제가 개인적으로 좋아하는 방식은 모든 저장 로직을 파일 하단에 배치하는 것입니다. 그냥 개인적인 습관입니다. 그래서 여기 하단에서 저의 저장 객체를 정의합니다. 그리고 저장 함수를 만들고 싶습니다. 이 함수는 save 객체를 반환할 것이고, save라는 이름으로요. 그리고 이것은 객체를 구성하기만 하면 됩니다. 따라서 새로운 save 객체를 반환하도록 합니다. 내부에는 단순히 position을 this transform.position과 같게 설정합니다. 좋습니다. 이렇게 저장하는 것입니다. 이제 어딘가에서 이 함수를 호출해야 합니다. 그래서 여기서는 간단한 save system 스크립트를 만들겠습니다. 새 MonoBehaviour를 생성하고, 'save system'이라고 이름을 붙입니다. 그리고 이것에 대해서는 키를 요청해 보겠습니다. private void update를 사용하겠습니다. 만약 keyboard.current.tkey.wispress.frame으로 T 키를 누르면, 무언가를 저장하고 싶다고 가정합시다. save game 같은 함수를 호출할 것입니다. 이 함수는 private void saveGame이라고 하겠습니다. 그리고 여기서는 먼저 플레이어 데이터를 저장해야 합니다. 따라서 이 player에 접근해야 합니다. 제가 만드는 게임에는 플레이어가 하나밖에 없기 때문에, 싱글톤 패턴(singleton pattern)을 사용하기에 완벽합니다. 혹시 싱글톤 패턴이 무엇인지 모르신다면, 이것은 정말 멋지고 매우 유용한 패턴입니다. 저는 제 C-Shop 과정에서 이 주제만 다룬 전체 강의가 있습니다. 어쨌든 여기서는 public static playerState를 만들고, 이름을 instance라고 붙이고, get any private set을 사용합니다. 그리고 여기에 awake에서 instance = this로 설정하겠습니다. 그러면 여기서 playerState의 instance에 접근하여 save를 호출할 수 있습니다. 이 함수는 playerState.saveObject, saveObject를 반환할 것입니다. 이제 다시 한번, 같은 작업을 할 수 있습니다. JSON utility.toJson으로 이 save 객체를 JSON으로 변환합니다. 우리의 JSON을 위한 문자열(string)이 생기고, file.writeAllText로 JSON의 내용을 다시 파일에 씁니다. 아까 봤던 것과 똑같습니다. 네, 이렇게 해서 이전과 동일한 작업을 수행하고 있습니다. 다음은 반대 과정입니다. private void loadGame을 하겠습니다.
이번에는 마찬가지로 그 반대 과정을 진행하겠습니다. 먼저 파일의 ReadAllText를 사용하여 저장 파일에서 모든 텍스트를 읽어옵니다. 이 내용을 모두 읽어서 문자열(string) JSON으로 넣습니다. 그리고 JSONUtility를 이용해 JSON을 파싱합니다. 여기서는 플레이어 상태(player state)의 저장 객체(save object)가 됩니다. 우리가 저장했던 JSON이 바로 이 플레이어 상태의 저장 객체, 즉 save object입니다. 이제 우리는 그 변경 사항들을 적용하기만 하면 됩니다. 다시 여기에 가서 플레이어 쪽으로 돌아와서 public void load를 추가하겠습니다. 그리고 이 load 메서드는 save object를 인자로 받습니다. 그리고 단순히 transform.position을 저장 객체에 저장된 위치로 설정해 줍니다. 그래서 여기는...
AI 자동 생성 콘텐츠
본 콘텐츠는 YouTube Code Monkey (Unity C#)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기