최종프로젝트 외부데이터를 어떻게 확장성을 확보할수 있을까? 하며 생각을 하며 튜터님께 질문을 했을때

"고정 데이터인데 이걸 확장성을 볼 필요가있나? " , " 고정 데이터는 해당 class의 데이터의 내부에서 파싱하면된다. "

라고 조언을 주셔서 , 우선 고정데이터를 파싱하는 코딩하여 작업을 하였고 , 나중에 내부 클래스에서 데이터를 파싱하는것을 리팩토링하기로 하였다.

 

CSVController.cs : CSV 데이터를 로드하는 컨트롤러 클래스

using UnityEngine;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;

public class CSVController
{
    static string SPLIT_RE = @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))"; //한 행에 대한 데이터 분리를 위한 세퍼레이트(분리자)
    static string LINE_SPLIT_RE = @"\r\n|\n\r|\n|\r";//가져온 CSV데이터를 엔터단위로 끊기위한 분리자
    static char[] TRIM_CHARS = { '\"' }; //

    public Dictionary<int, ItemData> ItemCSVRead(string file)
    {
        Dictionary<int, ItemData> list = new Dictionary<int, ItemData>();
        TextAsset data = Resources.Load(file) as TextAsset;

        //가져온 텍스트 덩어리를 해당 분리자를 통해 분리한다.
        string[] lines = Regex.Split(data.text, LINE_SPLIT_RE);

        //해당 CSV 데이터에 헤더(카테고리) + 해당 자료형만 입력되있는 상태이므로 List 반환
        if (lines.Length <= 2) return list;


        string[] header = Regex.Split(lines[0], SPLIT_RE);  //해당 CSV 데이터에 헤더(카테고리)입력 -> Dictonary의 키값으로 사용
        string[] typeHeader = Regex.Split(lines[1], SPLIT_RE);   //해당 CSV 데이터에 자료형 입력 -> 해당 자료의 자료형 선정시 사용됨
        for (int i = 2; i < lines.Length; i++)
        {
            string[] values = Regex.Split(lines[i], SPLIT_RE);
            if (values.Length == 0 || values[0] == "") continue; //정보가 없으면 다음 행으로 이동

            ItemData item = new ItemData();
            item.ID = (int)DataTypeCheck(typeHeader[0], values[0]);
            item.Name = (string)DataTypeCheck(typeHeader[1], values[1]);
            item.Type = (string)DataTypeCheck(typeHeader[2], values[2]);
            item.Rairty = (string)DataTypeCheck(typeHeader[3], values[3]);
            item.Descripton = (string)DataTypeCheck(typeHeader[4], values[4]);
            item.Attack = (float)DataTypeCheck(typeHeader[5], values[5]); ;
            item.AttackPercent = (bool)DataTypeCheck(typeHeader[6], values[6]); ;
            item.Defence = (float)DataTypeCheck(typeHeader[7], values[7]);
            item.DefencePercent = (bool)DataTypeCheck(typeHeader[8], values[8]);
            item.Health = (float)DataTypeCheck(typeHeader[9], values[9]);
            item.HealthPercent = (bool)DataTypeCheck(typeHeader[10], values[10]);
            item.CritChance = (float)DataTypeCheck(typeHeader[11], values[11]);
            item.CritChancePercent = (bool)DataTypeCheck(typeHeader[12], values[12]);
            item.CritDamage = (float)DataTypeCheck(typeHeader[13], values[13]);
            item.CritDamagePercent = (bool)DataTypeCheck(typeHeader[14], values[14]);
            item.Effect = (string)DataTypeCheck(typeHeader[15], values[15]);
            item.Cost = (int)DataTypeCheck(typeHeader[16], values[16]);
            item.StackMaxCount = (int)DataTypeCheck(typeHeader[17], values[17]);
            list[item.ID] = item;

        }
        return list;
    }
    public Dictionary<int, EnemyData> EnemyCSVRead(string file)
    {
        Dictionary<int, EnemyData> list = new Dictionary<int, EnemyData>();
        TextAsset data = Resources.Load(file) as TextAsset;

        //가져온 텍스트 덩어리를 해당 분리자를 통해 분리한다.
        string[] lines = Regex.Split(data.text, LINE_SPLIT_RE);

        //해당 CSV 데이터에 헤더(카테고리) + 해당 자료형만 입력되있는 상태이므로 List 반환
        if (lines.Length <= 2) return list;

        string[] header = Regex.Split(lines[0], SPLIT_RE);  //해당 CSV 데이터에 헤더(카테고리)입력 -> Dictonary의 키값으로 사용
        string[] typeHeader = Regex.Split(lines[1], SPLIT_RE);   //해당 CSV 데이터에 자료형 입력 -> 해당 자료의 자료형 선정시 사용됨
        for (int i = 2; i < lines.Length; i++)
        {
            string[] values = Regex.Split(lines[i], SPLIT_RE);
            if (values.Length == 0 || values[0] == "") continue; //정보가 없으면 다음 행으로 이동

            EnemyData enemy = new EnemyData();
            enemy.ID = (int)DataTypeCheck(typeHeader[0], values[0]);
            enemy.Name = (string)DataTypeCheck(typeHeader[1], values[1]);
            enemy.Descripton = (string)DataTypeCheck(typeHeader[2], values[2]);
            enemy.DropItemID = (List<int>)DataTypeCheck(typeHeader[3], values[3]);
            enemy.DropGold = (int)DataTypeCheck(typeHeader[4], values[4]);
            enemy.Attack = (float)DataTypeCheck(typeHeader[5], values[5]); ;
            enemy.AttackSpeed = (float)DataTypeCheck(typeHeader[6], values[6]);
            enemy.Defence = (float)DataTypeCheck(typeHeader[7], values[7]);
            enemy.Health = (float)DataTypeCheck(typeHeader[8], values[8]);
            enemy.Health = (float)DataTypeCheck(typeHeader[9], values[9]);
            enemy.CritChance = (float)DataTypeCheck(typeHeader[10], values[10]);
            enemy.CritDamage = (float)DataTypeCheck(typeHeader[11], values[11]);
            list[enemy.ID] = enemy;

        }
        return list;
    }
    public Dictionary<int, StagaData> StageCSVRead(string file)
    {
        Dictionary<int, StagaData> list = new Dictionary<int, StagaData>();
        TextAsset data = Resources.Load(file) as TextAsset;

        //가져온 텍스트 덩어리를 해당 분리자를 통해 분리한다.
        string[] lines = Regex.Split(data.text, LINE_SPLIT_RE);

        //해당 CSV 데이터에 헤더(카테고리) + 해당 자료형만 입력되있는 상태이므로 List 반환
        if (lines.Length <= 2) return list;

        string[] header = Regex.Split(lines[0], SPLIT_RE);  //해당 CSV 데이터에 헤더(카테고리)입력 -> Dictonary의 키값으로 사용
        string[] typeHeader = Regex.Split(lines[1], SPLIT_RE);   //해당 CSV 데이터에 자료형 입력 -> 해당 자료의 자료형 선정시 사용됨
        for (int i = 2; i < lines.Length; i++)
        {
            string[] values = Regex.Split(lines[i], SPLIT_RE);
            if (values.Length == 0 || values[0] == "") continue; //정보가 없으면 다음 행으로 이동

            StagaData stage = new StagaData();
            stage.ID = (int)DataTypeCheck(typeHeader[0], values[0]);
            stage.ChapterNum = (int)DataTypeCheck(typeHeader[1], values[1]);
            stage.StageNum = (int)DataTypeCheck(typeHeader[2], values[2]);
            stage.CurStageModifier = (float)DataTypeCheck(typeHeader[3], values[3]);
            stage.StageName = (string)DataTypeCheck(typeHeader[4], values[4]);
            stage.SlayEnemyCount = (int)DataTypeCheck(typeHeader[5], values[5]); ;
            stage.SummonEnemyIDList = (List<int>)DataTypeCheck(typeHeader[6], values[6]);
            list[stage.ID] = stage;

        }
        return list;
    }
    private object DataTypeCheck(string type, string value)
    {
        switch (type)
        {
            case "int":
                return int.Parse(value);  //int
            case "float":
                return float.Parse(value); //float
            case "double":
                return double.Parse(value);  //double
            case "bool":
                return bool.Parse(value);  //bool
            case "string":
                return value;  //string
            //default:
            //    throw new Exception($"지원하지 않는 자료형 타입입니다.: {type}");
        }

        if (type.StartsWith("List<"))
        {
            var itemType = type.Substring(5, type.Length - 6);

            //CSV 데이터 셀 서식이 Text이기에 \" 철자를 제거해야된다. 
            value = value.TrimStart(TRIM_CHARS).TrimEnd(TRIM_CHARS).Replace("\\", "");

            switch (itemType)
            {
                case "int":
                    return value.Split(',').Select(int.Parse).ToList();  //List<int>
                case "float":
                    return value.Split(',').Select(float.Parse).ToList(); //List<float>
                case "double":
                    return value.Split(',').Select(double.Parse).ToList(); //List<double>
                case "bool":
                    return value.Split(',').Select(bool.Parse).ToList();  //List<bool>
                case "string":
                    return value.Split(',').Select(v => v.Trim()).ToList();  //List<string>

            }
        }


        return null;
    }

}

 

CSV가 아닌 Json으로 UserData관리를 하기로 하였다.

왜 CSV를 외부데이터로 사용하고있고 쓰기도 할 수 있는데 어째서 Json을 골랐는가?

Json은 데이터가 미리 포멧팅(구조)가 잡혀있고 계층성이기에 직렬화/역직렬화에 유리하기에 선택을 하였다.

UserData에는 사용자 Class의 Inventory,Skill 등등을 쉽고 간단하게 사용할 수 있기에 Json을 사용하였다.

 

JsonController : UserData를 저장/불러오기를 담당하는 컨트롤러

using UnityEngine;
using System.IO;

public class JsonController
{
    public UserData LoadUserData(string path)
    {
        if (File.Exists(path))
        {
            string json = File.ReadAllText(path);
            Debug.Log("UserData loded to: " + path);
            return JsonUtility.FromJson<UserData>(json);
        }
        Debug.LogWarning("UserData file not found!");
        return null;
    }

    public void SaveUserData(UserData userData, string path)
    {
        string jsonUserData = JsonUtility.ToJson(userData, true);
        File.WriteAllText(path, jsonUserData);
        Debug.Log("UserData saved to: " + path);
    }
}

 

 

+ Recent posts