Unity 파일 저장과 불러오기 (Serialization)

2019. 7. 16. 02:55프로그래밍/Unity [공부]

 

 Unity에서 파일을 저장하는 방법은 여러 가지가 있는데 이번에 제가 사용한 방법은 BinaryFormatter를 이용한 직렬화(Serialization)입니다. 직렬화는 객체의 내용을 바이트 단위로 변환하여 송수신할 수 있게 만들어주는 것을 의미합니다. 직렬화의 장점으로는 바이너리로 저장되기 때문에 안전하고 속도가 매우 빠르다는 것입니다.

 

 

Unity - Manual: Script Serialization

Script Serialization Serialization is the automatic process of transforming data structures or object states into a format that Unity can store and reconstruct later. Some of Unity’s built-in features use serialization; features such as saving and loading,

docs.unity3d.com

 

Unity에서 직렬화를 하기 위해서는 아래의 몇 가지 규칙이 필요합니다.

  • public 또는 [Serialization] 속성을 가지고 있어야 합니다.
  • static이 아니어야 합니다.
  • const가 아니어야 합니다.
  • readonly가 아니어야 합니다.
  • 직렬화할 수 있는 fieldtype이어야 합니다.

 

직렬화할 수 있는 fieldtype 이란

  • [Serializable] 속성을 가지고 있는 사용자 정의 비추상 클래스
  • [Serializable] 속성을 가지고 있는 사용자 정의 구조체
  • UnityEngine.Object 에서 파생한 오브젝트 참조
  • 기본 데이터 타입 (int, float, double, bool, string 등)
  • 직렬화 가능한 필드를 가진 배열
  • 직렬화 가능한 필드를 가진 List

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class PlayerData
{
    public float[] pos;

    public PlayerData(Player _player)
    {
        pos = new float[3];

        pos[0] = _player.transform.position.x;
        pos[1] = _player.transform.position.y;
        pos[2] = _player.transform.position.z;
    }
}

 우선 플레이어의 위치를 저장할 수 있는 클래스를 하나 만들었습니다. 직렬화를 해야 하므로 [Serializable] 속성을 붙여줬습니다. 위치를 저장하는데 Vector3가 아닌 float[ ]을 사용한 이유는 Vector3는 직렬화된 구조체가 아니므로 사용하고 싶다면 사용자 정의 구조체로 재정의하여 사용해야 합니다.

 

 여기서 MonoBehaviour의 상속이 되어있으면 에러가 발생합니다. 이 문제 찾느라 고생했습니다...ㅠㅠ

MonoBehaviour는 직렬화가 되지 않는다고 합니다.

 

using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public static class SaveSystem
{
    public static void Save(PlayerData _data, string filePath)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        FileStream stream = new FileStream(filePath, FileMode.Create);

        formatter.Serialize(stream, _data);
        stream.Close();
    }

    public static PlayerData Load(string filePath)
    {
        if (File.Exists(filePath))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            FileStream stream = new FileStream(filePath, FileMode.Open);

            PlayerData data = formatter.Deserialize(stream) as PlayerData;

            stream.Close();

            return data;
        }
        else
        {
            Debug.LogError("Save file not found in" + filePath);
            return null;
        }
    }
}

 다음은 저장과 불러오기 기능을 만든 클래스입니다. 

System.IOSystem.Runtime.Serialization.Formatters.Binary 네임스페이스를 선언해줘야 합니다.

 

 저장은 FileStreamFileMode.Create와 BinaryFormatter를 이용해 지정된 경로에 파일을 하나 만들어 준 후 데이터를 바이트 배열로 변환해서 저장해줍니다.

 

 불러오기는 FileStream의 FileMode.Open을 이용해 데이터를 받아와 마찬가지로 BinaryFormatter를 이용해 데이터를 변환시켜준 후 형 변환을 해줍니다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    private PlayerData data;

    private string filePath = string.Empty;

    void Awake()
    {
        filePath = Application.dataPath + "/Uk.bin";
    }

    public void Save()
    {
        data = new PlayerData(this);
        SaveSystem.Save(data, filePath);
    }

    public void Load()
    {
        data = SaveSystem.Load(filePath);
        this.transform.position = new Vector3(data.pos[0], data.pos[1], data.pos[2]);
    }
}

마지막으로 플레이어의 위치를 저장, 불러오는 부분입니다.

 

 

 

<적용 영상>

 저장과 불러오기를 통해 게임의 퀄리티를 더 높일 수 있다는 생각에 행복한 마음으로 공부를 한 것 같습니다. 우선 이번에 공부한 내용을 바탕으로 늑대와 토끼 게임에 적용해 볼 생각입니다.

 

 현재 작성한 코드는 여러 객체의 위치를 저장하기에는 무리가 있습니다. 하지만 List에 직렬화시킨 클래스를 저장하는 방법을 사용하면 여러 명의 위치를 저장할 수 있으므로 게임에 적용할 때는 이 방법을 사용할 것입니다.