본문 바로가기
UNITY/유니티게임스쿨

csv Parser

by 램플릿 2024. 9. 30.

 

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

public class CSV_Data_Paser : EditorWindow
{ 
    private string csvFilePath = "";
    private string outputFolder = "Assets/ScriptableObjects";

    [MenuItem("Tools/CSV to ScriptableObject Converter %J")]
    public static void ShowWindow()
    {
        GetWindow<CSV_Data_Paser>("CSV Converter");
    }

    private void OnGUI()
    {
        GUILayout.Label("CSV to ScriptableObject Converter", EditorStyles.boldLabel);

        csvFilePath = EditorGUILayout.TextField("CSV File Path", csvFilePath);
        if (GUILayout.Button("Select CSV File"))
        {
            csvFilePath = EditorUtility.OpenFilePanel("Select CSV File", "", "csv");
        }

        outputFolder = EditorGUILayout.TextField("Output Folder", outputFolder);
        if (GUILayout.Button("Select Output Folder"))
        {
            outputFolder = EditorUtility.SaveFolderPanel("Select Output Folder", "Assets", "");
            outputFolder = outputFolder.Replace(Application.dataPath, "Assets");
        }

        if (GUILayout.Button("Convert CSV to ScriptableObject"))
        {
            ConvertCSVToScriptableObject();
        }
    }

    private void ConvertCSVToScriptableObject()
    {
        if (string.IsNullOrEmpty(csvFilePath) || !File.Exists(csvFilePath))
        {
            Debug.LogError("Invalid CSV file path.");
            return;
        }

        string[] lines = File.ReadAllLines(csvFilePath);
        if (lines.Length < 2)
        {
            Debug.LogError("CSV file is empty or has no data rows.");
            return;
        }

        string[] headers = lines[0].Split(',');
        for (int i = 1; i < lines.Length; i++)
        {
            string[] values = lines[i].Split(',');
            if (values.Length != headers.Length)
            {
                Debug.LogWarning($"Skipping row {i} due to mismatched column count.");
                continue;
            }

            CSVData data = ScriptableObject.CreateInstance<CSVData>();
            data.PopulateData(headers, values);

            string assetPath = $"{outputFolder}/CSVData_{i}.asset";
            AssetDatabase.CreateAsset(data, assetPath);
        }

        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log("CSV conversion completed.");
    }
}

public class CSVData : ScriptableObject
{
    public string[] headers;
    public string[] values;

    public void PopulateData(string[] headers, string[] values)
    {
        this.headers = headers;
        this.values = values;
    }
}

% : ctrl

# : shift

 

 

복수의 데이터를 저장하는 방식으로 위의 방식은 너무 많은 스크립터블오브젝트를 생성한다. 따라서 아래와 같이 변경

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

public class CSV_Data_Paser : EditorWindow
{   
    private string csvFilePath = "";
    private string outputFolder = "Assets/ScriptableObjects";
    private string fileName = "CSVDataContainer";

    [MenuItem("Tools/CSV to ScriptableObject Converter %#C")] // Ctrl+Shift+C (Windows) or Cmd+Shift+C (Mac)
    public static void ShowWindow()
    {
        GetWindow<CSV_Data_Paser>("CSV Converter");
    }

    private void OnGUI()
    {
        GUILayout.Label("CSV to ScriptableObject Converter", EditorStyles.boldLabel);

        csvFilePath = EditorGUILayout.TextField("CSV File Path", csvFilePath);
        if (GUILayout.Button("Select CSV File"))
        {
            csvFilePath = EditorUtility.OpenFilePanel("Select CSV File", "", "csv");
        }

        outputFolder = EditorGUILayout.TextField("Output Folder", outputFolder);
        if (GUILayout.Button("Select Output Folder"))
        {
            outputFolder = EditorUtility.SaveFolderPanel("Select Output Folder", "Assets", "");
            outputFolder = outputFolder.Replace(Application.dataPath, "Assets");
        }

        fileName = EditorGUILayout.TextField("File Name", fileName);

        if (GUILayout.Button("Convert CSV to ScriptableObject"))
        {
            ConvertCSVToScriptableObject();
        }
    }

    private void ConvertCSVToScriptableObject()
    {
        if (string.IsNullOrEmpty(csvFilePath) || !File.Exists(csvFilePath))
        {
            Debug.LogError("Invalid CSV file path.");
            return;
        }

        string[] lines = File.ReadAllLines(csvFilePath);
        if (lines.Length < 2)
        {
            Debug.LogError("CSV file is empty or has no data rows.");
            return;
        }

        CSVDataContainer container = ScriptableObject.CreateInstance<CSVDataContainer>();
        container.headers = lines[0].Split(',');
        container.dataRows = new List<CSVDataRow>();

        for (int i = 1; i < lines.Length; i++)
        {
            string[] values = lines[i].Split(',');
            if (values.Length != container.headers.Length)
            {
                Debug.LogWarning($"Skipping row {i} due to mismatched column count.");
                continue;
            }

            CSVDataRow row = new CSVDataRow();
            row.values = values;
            container.dataRows.Add(row);
        }

        string assetPath = $"{outputFolder}/{fileName}.asset";
        AssetDatabase.CreateAsset(container, assetPath);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log($"CSV conversion completed. ScriptableObject saved at {assetPath}");
    }
}

[System.Serializable]
public class CSVDataRow
{
    public string[] values;
}

public class CSVDataContainer : ScriptableObject
{
    public string[] headers;
    public List<CSVDataRow> dataRows;

    public string GetValue(int rowIndex, string headerName)
    {
        if (rowIndex < 0 || rowIndex >= dataRows.Count)
        {
            Debug.LogError($"Row index {rowIndex} is out of range.");
            return null;
        }

        int columnIndex = System.Array.IndexOf(headers, headerName);
        if (columnIndex == -1)
        {
            Debug.LogError($"Header '{headerName}' not found.");
            return null;
        }

        return dataRows[rowIndex].values[columnIndex];
    }
}

 

 

에셋이 변경되면 자동으로 스크립터블 오브젝트를 생성해주는 기능

유니티의 에셋포스트프로세서 기능을 이용.

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

public class CSVToScriptableObjectConverter : AssetPostprocessor
{
    private static readonly string csvFolderPath = "Assets/CSV";
    private static readonly string outputFolderPath = "Assets/ScriptableObjects";

    private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        Debug.Log("CSV files converted");
        
        foreach (string assetPath in importedAssets)
        {
            if (Path.GetExtension(assetPath).ToLower() == ".csv" && assetPath.StartsWith(csvFolderPath))
            {
                ConvertCSVToScriptableObject(assetPath);
            }
        }
    }

    private static void ConvertCSVToScriptableObject(string csvPath)
    {
        string fileName = Path.GetFileNameWithoutExtension(csvPath);
        string outputPath = $"{outputFolderPath}/{fileName}.asset";

        if (!Directory.Exists(outputFolderPath))
        {
            Directory.CreateDirectory(outputFolderPath);
        }

        CSVDataContainer2 container = AssetDatabase.LoadAssetAtPath<CSVDataContainer2>(outputPath);
        if (container == null)
        {
            container = ScriptableObject.CreateInstance<CSVDataContainer2>();
            AssetDatabase.CreateAsset(container, outputPath);
        }

        string[] lines = File.ReadAllLines(csvPath);
        if (lines.Length < 2)
        {
            Debug.LogError($"CSV file {csvPath} is empty or has no data rows.");
            return;
        }

        container.headers = lines[0].Split(',');
        container.dataRows = new List<CSVDataRow2>();

        for (int i = 1; i < lines.Length; i++)
        {
            string[] values = lines[i].Split(',');
            if (values.Length != container.headers.Length)
            {
                Debug.LogWarning($"Skipping row {i} in {csvPath} due to mismatched column count.");
                continue;
            }

            CSVDataRow2 row = new CSVDataRow2();
            row.values = values;
            container.dataRows.Add(row);
        }

        EditorUtility.SetDirty(container);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log($"CSV conversion completed for {csvPath}. ScriptableObject saved at {outputPath}");
    }
}

[System.Serializable]
public class CSVDataRow2
{
    public string[] values;
}

public class CSVDataContainer2 : ScriptableObject
{
    public string[] headers;
    public List<CSVDataRow2> dataRows;

    public string GetValue(int rowIndex, string headerName)
    {
        if (rowIndex < 0 || rowIndex >= dataRows.Count)
        {
            Debug.LogError($"Row index {rowIndex} is out of range.");
            return null;
        }

        int columnIndex = System.Array.IndexOf(headers, headerName);
        if (columnIndex == -1)
        {
            Debug.LogError($"Header '{headerName}' not found.");
            return null;
        }

        return dataRows[rowIndex].values[columnIndex];
    }
}

 

 

명시적인 구조체로 만들어주기

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

public class CSVToScriptableObjectConverter : AssetPostprocessor
{
    private static readonly string csvFolderPath = "Assets/CSV";
    private static readonly string outputFolderPath = "Assets/ScriptableObjects";

    private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        Debug.Log("CSV files converted");
        
        foreach (string assetPath in importedAssets)
        {
            if (Path.GetExtension(assetPath).ToLower() == ".csv" && assetPath.StartsWith(csvFolderPath))
            {
                ConvertCSVToScriptableObject(assetPath);
            }
        }
    }

    private static void ConvertCSVToScriptableObject(string csvPath)
    {
        string fileName = Path.GetFileNameWithoutExtension(csvPath);
        string outputPath = $"{outputFolderPath}/{fileName}.asset";

        if (!Directory.Exists(outputFolderPath))
        {
            Directory.CreateDirectory(outputFolderPath);
        }

        CSVDataContainer2 container = AssetDatabase.LoadAssetAtPath<CSVDataContainer2>(outputPath);
        if (container == null)
        {
            container = ScriptableObject.CreateInstance<CSVDataContainer2>();
            AssetDatabase.CreateAsset(container, outputPath);
        }

        string[] lines = File.ReadAllLines(csvPath);
        if (lines.Length < 2)
        {
            Debug.LogError($"CSV file {csvPath} is empty or has no data rows.");
            return;
        }

        container.headers = lines[0].Split(',');
        container.dataRows = new List<CSVDataRow2>();

        for (int i = 1; i < lines.Length; i++)
        {
            string[] values = lines[i].Split(',');
            if (values.Length != container.headers.Length)
            {
                Debug.LogWarning($"Skipping row {i} in {csvPath} due to mismatched column count.");
                continue;
            }

            CSVDataRow2 row = new CSVDataRow2();
            row.index = Int32.Parse(values[0]);
            row.hp = Int32.Parse(values[1]);
            row.mp = Int32.Parse(values[2]);
            row.attackPower = Int32.Parse(values[3]);
            container.dataRows.Add(row);
        }

        EditorUtility.SetDirty(container);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log($"CSV conversion completed for {csvPath}. ScriptableObject saved at {outputPath}");
    }
}

[System.Serializable]
public class CSVDataRow2
{
    public int index;
    public int hp;
    public int mp;
    public int attackPower;
}

public class CSVDataContainer2 : ScriptableObject
{
    public string[] headers;
    public List<CSVDataRow2> dataRows;
}

'UNITY > 유니티게임스쿨' 카테고리의 다른 글

오브젝트 풀링  (0) 2024.09.30
Unity 클라이언트와 서버 통신하기  (0) 2024.09.25
프로토버퍼  (0) 2024.09.23
쉐이더  (0) 2024.07.31
포스트 프로세싱  (0) 2024.07.30