내일배움캠프 Unity 60일차 TIL - 팀 9와 4분의 3 - 개발일지

Wooooo·2024년 1월 19일
0

내일배움캠프Unity

목록 보기
62/94

[오늘의 키워드]

  1. 딕셔너리 직렬화
  2. 맵 디자인을 할 수 있는 씬 제작

1. [딕셔너리 직렬화]

Map Edit 씬에서 생성한 딕셔너리 정보를 JSON으로 저장해야했는데, 딕셔너리는 바로 직렬화가 안되서 직렬화가 가능한 다른 객체를 거쳐서 저장할 수 있게 했다.

SerializableDictionary 클래스

using System;
using System.Collections.Generic;
using System.Linq;

[Serializable]
public class SerializableDictionary<TKey, TValue>
{
    public List<TKey> keys;
    public List<TValue> values;

    public SerializableDictionary() { }

    public SerializableDictionary(Dictionary<TKey, TValue> dict) => FromDictionary(dict);

    public Dictionary<TKey, TValue> ToDictionary()
    {
        var dict = new Dictionary<TKey, TValue>();
        for (int i = 0; i < keys.Count; i++)
            dict.TryAdd(keys[i], values[i]);
        return dict;
    }

    public void FromDictionary(Dictionary<TKey, TValue> dict)
    {
        keys = dict.Keys.ToList();
        values = dict.Values.ToList();
    }
}

간단하게 keys, values 리스트를 갖는 직렬화 가능한 딕셔너리 클래스를 만들었다.
리스트는 자동으로 직렬화가 가능한 자료형이기 때문에 바로 떠오른 방법이었다.

FromDictionary와 ToDictionary 메서드로 두 자료형을 왔다갔다 할 수 있게 해봤다.

Dictionary <-> JSON 변환 확장 메서드

using System.Collections.Generic;
using UnityEngine;

public static class Extensions
{
    public static string DictionaryToJson<TKey, TValue>(this Dictionary<TKey, TValue> dict)
    {
        var serializableDict = new SerializableDictionary<TKey, TValue>(dict);
        return JsonUtility.ToJson(serializableDict);
    }

    public static string DictionaryToJson<TKey, TValue>(this Dictionary<TKey, TValue> dict, bool pretty)
    {
        var serializableDict = new SerializableDictionary<TKey, TValue>(dict);
        return JsonUtility.ToJson(serializableDict, pretty);
    }

    public static Dictionary<TKey, TValue> DictionaryFromJson<TKey, TValue>(this string jsonStr)
    {
        var serializableDict = JsonUtility.FromJson<SerializableDictionary<TKey, TValue>>(jsonStr);
        return serializableDict.ToDictionary();
    }
}

확장 메서드를 이용해서 두 객체를 바로바로 직렬화 할 수 있게 해봤다.

실제 사용) Dictionary to JSON

    private void SaveDataFile()
    {
        File.WriteAllText(_dataPath, _voxelMap.DictionaryToJson());
        FileInfo fileInfo = new FileInfo(_dataPath);
        Debug.Log($"Save File Bytes : {fileInfo.Length} byte");
    }

실제 사용) JSON to Dictionary

    public IEnumerator ReadMapDataFile(TextAsset json)
    {
        var originData = json.text.DictionaryFromJson<Vector3Int, MapData>();
        foreach (var data in originData)
        {
            var type = WorldData.GetType(data.Value.type)[data.Value.typeIndex];
            _voxelMap.TryAdd(data.Key, (type, data.Value.forward));
        }
        yield return null;
    }

2. [맵 디자인을 할 수 있는 씬 제작]

컴포넌트 하나와 유니티 프리미티브 오브젝트인 Cube를 이용해 Voxel 맵을 제작할 수 있도록 해봤다.

씬에 MapEditDataSource 컴포넌트가 붙은 프리미티브 Cube를 배치하고, 컴포넌트에서 속성을 지정해준 다음, Play Mode로 들어가면 VoxelMapDataGernerator 컴포넌트가 MapEditDataSource들을 모두 읽어서 VoxelMap 데이터를 만든다.

VoxelMapDataGernerator.cs

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

public class VoxelMapDataGenerator : MonoBehaviour
{
    private Dictionary<Vector3Int, MapData> _voxelMap = new();
    private MapEditDataSource[] _dataSources;
    [SerializeField] private TextAsset _mapData;
    [SerializeField] private string _dataPath = @"Assets/04. Resources/MapData.json";
    private int _blockCount = 0;

    public void Start()
    {
        GetDataSources();
        GenerateMapData();
        SaveDataFile();
        Debug.Log($"Progress Time : {Time.realtimeSinceStartup}");
    }

    private void GetDataSources()
    {
        _dataSources = FindObjectsOfType<MapEditDataSource>();
        Debug.Log($"Get Sources Count : {_dataSources.Length}");
    }

    private void GenerateMapData()
    {
        foreach (var source in _dataSources)
        {
            foreach (var (pos, info) in source.GetSourceData())
            {
                if (_voxelMap.TryAdd(pos, info))
                    _blockCount++;
            }
        }

        Debug.Log($"Create Blocks data Count : {_blockCount}");
    }

    private void SaveDataFile()
    {
        File.WriteAllText(_dataPath, _voxelMap.DictionaryToJson());

        // 이 코드는 기존 내용이 지워지지 않음,,
        //using (FileStream fs = new(_dataPath, FileMode.OpenOrCreate, FileAccess.Write))
        //{
        //    using (StreamWriter sw = new StreamWriter(fs))
        //    {
        //        sw.Write(_voxelMap.DictionaryToJson());
        //        sw.Close();
        //    }
        //    fs.Close();
        //}

        FileInfo fileInfo = new FileInfo(_dataPath);
        Debug.Log($"Save File Bytes : {fileInfo.Length} byte");
    }
}

데이터를 생성하면, MapEditDataSource는 총 몇 개 읽어왔는지,
만들어진 Block의 갯수는 몇 개인지,
저장된 파일은 몇 byte인지,
실행 시간은 얼마나 걸렸는지 로그가 찍히도록 해봤다.

profile
game developer

0개의 댓글