Unity - 클라이언트 데이터 저장/불러오기, 파일 유효성 검사

2023. 3. 24. 14:03유니티/기능

게임을 만들 때, 필수 요소 중 하나는 저장과 불러오기 입니다

데이터를 저장할 때에는 서버에 저장하는 방식과 클라이언트에 저장하는 방식이 있습니다.

 

클라이언트에 저장하는 방법은 서버에 저장할 때보다 빠르다는 장점이 있습니다.

서버에 연결하는 시간을 기다릴 필요가 없기 때문에 빠릅니다.

 

클라이언트 저장 방식의 단점으로는 유저가 파일을 쉽게 찾아 변조할 수 있는 취약점이 있습니다.

프로그램의 저장 경로를 찾기만 하면 세이브 데이터를 확인 및 수정 할 수 있기 때문입니다.

 

 

아래 목차를 Ctrl + F 로 검색하여 찾아가시면 빠릅니다.

1. 실험용 오브젝트 준비하기

2. 데이터를 담을 클래스 작성하기

3. 데이터 저장/불러오기를 관리할 스크립트 작성하기

3.1. 파일 유효성 검사

4. 실행해보기


1. 실험용 오브젝트 준비하기

간단한 플레이어의 위치를 저장/불러오기 하는 과정을 만들어보겠습니다.

** 저장/불러오기 방법만 궁금하시다면 글의 중간부터 보시면 좋습니다. **

 

화면은 다음과 같이 구성됩니다.

이미지는 플레이어의 이동 위치를 보여줍니다.

텍스트는 정확한 위치 좌표를 표시할 것입니다.

SaveManager 은 빈 오브젝트로, 이후 만들 세이브/로드 클래스를 넣을 것입니다.

 

우선 플레이어를 이동시키기 위해 PlayerController 스크립트를 생성하겠습니다.

 

Player Controller 는 다음과 같이 작성합니다.

이동과 위치 표시를 한 번에 진행하도록 하겠습니다.

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    // 위치를 표시할 텍스트
    public UnityEngine.UI.Text text;

    void Update()
    {
        // 이동 값 받기
        Vector3 vec = new Vector3(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));

        // 위치 이동
        transform.position += vec * Time.deltaTime * 100;

        // 텍스트에 위치 표시하기
        text.text = "Position : " + (Vector2)transform.GetComponent<RectTransform>().localPosition;
    }
}

 

작성한 스크립트를 이미지에 추가해줍니다.

그 후, Player Contoller 의 Text 프로퍼티에

생성해 두었던 텍스트 오브젝트를 드래그하여 놓습니다.

 

프로그램을 실행시키고 이동을 하는지, 위치가 표시되는지 확인해줍니다.


 

2. 데이터를 담을 클래스 작성하기

다음은 데이터를 저장할 형태를 담을 클래스 스크립트를 작성하겠습니다.

 

다음과 같이 "PlayerData" 스크립트를 생성해주겠습니다.

 

스크립트는 다음과 같이 작성합니다.

using UnityEngine;

[System.Serializable]
public class PlayerData
{
    public Vector3 position;
}

 

다른 형태의 데이터를 저장할 때, 배열을 사용해 여러가지 값을 넣어야 할 때가 있습니다. (인벤토리 등)

배열 형태의 데이터를 저장하려면 [System.Serializable] 을 사용해야 합니다.

 

또한, 데이터를 담으려면 외부에서 값을 수정할 수 있어야 하기 때문에

모든 변수에 public 을 적어줍니다.

 


 

3. 데이터 저장/불러오기를 관리할 스크립트 작성하기

다음은 저장/불러오기를 위한 스크립트를 작성하겠습니다.

 

SaveManager 라는 이름으로 새 스크립트를 생성하겠습니다.

 

우선, 기본적인 코드를 작성하겠습니다.

using UnityEngine;
using System.IO;

public class SaveManager : MonoBehaviour
{
    // 플레이어 오브젝트
    public GameObject player;
    
    // 파일 경로
    string path;
    string directoryPath;
    string filePath;

    private void Start()
    {
        // 저장 경로
        path = Application.dataPath;
        directoryPath = path + "/Save";
        filePath = directoryPath + "/Save.json";
        
        // 실제로 게임을 빌드 할 때에는 아래의 경로를 사용 할 것
        // Application.persistentDataPath
        
        // 경로 참고
        // 회사명은 기본값 DefaultCompany
        // PC : "C:/Users/사용자/AppData/LocalLow/회사명/프로젝트/"
        // IOS : ".../Android/data/com.회사명.프로젝트/files/"
    }
    
    private void Update()
    {
        // R 버튼으로 저장
        if (Input.GetKey(KeyCode.R))
        {
            Save();
        }

        // T 버튼으로 불러오기
        if (Input.GetKey(KeyCode.T))
        {
            Load();
        }
    }

    public void Save()
    {
        print("Save");
    }

    public void Load()
    {
        print("Load");
    }
    
    public void CheckFile()
    {
    }
}

 

 

세이브는 다음과 같이 작성합니다.

세이브 파일의 유효성 검사는 아래 설명을 참고하시길 바랍니다.

public void Save()
{
    print("Save");

    // 데이터를 저장할 클래스를 새로 생성한다.
    PlayerData data = new();

    // 데이터에 필요한 정보를 입력한다.
    data.position = player.transform.position;

    // JsonUtility 를 사용해 데이터를 json 으로 변환한다.
    string json = JsonUtility.ToJson(data);


    // Directory, File 클래스를 사용하기 위해서는 using System.IO; 네임스페이스 필요
    // 유효성 검사. 세이브 파일이 없으면 새로 파일을 생성한다.
    CheckFile();

    // 파일 저장하기
    // filePath : 저장 경로
    File.WriteAllText(filePath, json);
}

 

로드는 다음과 같이 작성합니다.

public void Load()
{
    print("Load");

    // Directory, File 클래스를 사용하기 위해서는 using System.IO; 네임스페이스 필요
    // 유효성 검사. 세이브 파일이 없으면 새로 생성한다.
    CheckFile();

    // 파일 불러오기
    // filePath : 저장 경로
    string json = File.ReadAllText(filePath);


    // JsonUtility 를 사용해 읽어들인 파일을 사용할 형태의 클래스 데이터로 변환한다.
    PlayerData data = JsonUtility.FromJson<PlayerData>(json);

    if (data != null)
        // 변환된 데이터를 필요한 곳에 적용시킨다.
        player.transform.position = data.position;
}

 

 


3.1. 파일 유효성 검사

파일 유효성 검사는 중요한 부분이기 때문에 따로 문단을 나누어 작성하겠습니다.

 

처음 게임을 받게 되면 유저의 클라이언트에는 세이브 파일이 없습니다.

이 때, 세이브 파일을 읽거나 쓰려고 시도를 하면 에러가 발생합니다.

 

따라서 먼저 세이브 폴더, 파일이 있는지 확인 한 다음,

대상이 없으면 생성을 해주는 코드를 만들겠습니다.

 

SaveManager.cs 의 CheckFile() 함수에 이어서 작성하도록 하겠습니다.

using System.IO;

public void CheckFile()
{
    // Directory, File 클래스를 사용하기 위해서는 using System.IO; 네임스페이스 필요
    
    // 유효성 검사에 필요한 경로는 대상이 되는 폴더(파일)의 이름까지 포함한다.
    // 경로 끝의 이름을 가진 대상이 있다면 true, 없다면 false 를 반환한다.

    // 폴더(파일) 생성 시, 경로 끝의 이름을 가진 대상을 생성한다.
    // 생성한 파일은 자동으로 열려있는 상태여서, 닫아주는 코드까지 작성해야 한다.

    // 폴더 유효성 검사
    if (!Directory.Exists(directoryPath))
        // 폴더가 없다면 새로 생성한다.
        Directory.CreateDirectory(directoryPath);

    // 생성한 파일을 담을 변수
    FileStream file = null;

    // 파일 유효성 검사
    if (!File.Exists(filePath))
        // 파일이 없다면 새로 생성한다.
        file = File.Create(filePath);

    // 파일이 생성되었다면 자동으로 열려있다.
    // 파일을 편집하기 위해서는 닫아주어야한다.
    if (file != null)
        file.Close();
}

 

더 좋은 방법은 아래의 포스팅을 참고해주시길 바랍니다.

https://dyunace.tistory.com/11

 

Unity - 폴더, 파일 탐색 및 생성

대부분 게임을 시작하면, 자동으로 유저의 정보를 저장하는 세이브 데이터가 자동으로 생성됩니다. 이럴 때에는 저장할 경로를 찾아 세이브 폴더와 파일을 생성하는 코드를 필요로 합니다. 이

dyunace.tistory.com

 

 

이제 스크립트를 SaveManager 오브젝트에 추가시켜주고

Player 프로퍼티에 생성해두었던 이미지를 끌어다 놓으시면 됩니다.

 


4. 실행해보기

이제 프로그램을 실행시켜봅시다.

WASD 로 이동한 뒤, R을 누르면 저장, T를 누르면 불러오기가 됩니다.

 

만약, 세이브 폴더나 세이브 파일이 없다면 자동으로 생성됩니다.

 

결과 영상

https://youtu.be/dDdDdxDUYJQ