[Unity] CSV → ScriptableObject への変換処理を作った話

Unity

以前、こんな記事を書きました。

この記事の結論として、CSVとScriptableObjectを併用して管理するよーって話になったんですが、その場合、CSVファイルの中身をScriptableObjectへ変換するパーサークラスが必要になります。

前回の記事では、省略しちゃったんですが
(需要があるのかわからないけど!)今回はそこを少し掘り下げようかなと思います。

変換処理、作ってみました

MagicDataImporter.cs(前回のMagicDataに変換)

// UnityEditorでCSVからMagicDataへインポートするツール
// Append / Overwrite のモード切替付き

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

public class MagicDataImporter : EditorWindow
{
    private string csvPath = "";
    private MagicData targetMagicData;

    private enum ImportMode { Append, Overwrite }
    private ImportMode importMode = ImportMode.Append;

    [MenuItem("Tools/Import Magic Data from CSV")]
    static void ShowWindow()
    {
        var window = GetWindow<MagicDataImporter>();
        window.titleContent = new GUIContent("Magic Data Importer");
        window.Show();
    }

    void OnGUI()
    {
        GUILayout.Label("Magic Data Importer", EditorStyles.boldLabel);

        importMode = (ImportMode)EditorGUILayout.EnumPopup("インポートモード", importMode);

        if (GUILayout.Button("CSVファイルを選択"))
        {
            csvPath = EditorUtility.OpenFilePanel("Select CSV File", "", "csv");
        }

        EditorGUILayout.LabelField("CSVファイルパス:", csvPath);

        targetMagicData = (MagicData)EditorGUILayout.ObjectField(
            "MagicDataアセット",
            targetMagicData,
            typeof(MagicData),
            false
        );

        if (GUILayout.Button("インポート") && !string.IsNullOrEmpty(csvPath) && targetMagicData != null)
        {
            if (EditorUtility.DisplayDialog(
                "確認",
                importMode == ImportMode.Overwrite ?
                    "現在のデータをすべて削除して上書きします。よろしいですか?" :
                    "既存のデータに追加します。続行しますか?",
                "はい", "キャンセル"))
            {
                ImportCSV();
            }
        }
    }

    void ImportCSV()
    {
        var lines = File.ReadAllLines(csvPath);
        if (lines.Length < 2)
        {
            EditorUtility.DisplayDialog("エラー", "CSVに十分なデータがありません", "OK");
            return;
        }

        if (importMode == ImportMode.Overwrite)
        {
            targetMagicData.magicList.Clear();
        }

        int successCount = 0;
        List<string> errorLogs = new List<string>();

        for (int i = 1; i < lines.Length; i++)
        {
            string[] row = lines[i].Split(',');

            if (row.Length < 5)
            {
                errorLogs.Add($"行{i + 1}: 列数が不足しています");
                continue;
            }

            if (!int.TryParse(row[2], out int power) || !int.TryParse(row[3], out int manaCost))
            {
                errorLogs.Add($"行{i + 1}: 数値が不正です(PowerまたはManaCost)");
                continue;
            }

            if (!System.Enum.TryParse(row[4], out ElementType element))
            {
                errorLogs.Add($"行{i + 1}: 無効な属性: {row[4]}");
                continue;
            }

            var magic = new MagicElement
            {
                magicName = row[0],
                description = row[1],
                power = power,
                manaCost = manaCost,
                element = element
            };

            targetMagicData.magicList.Add(magic);
            successCount++;
        }

        EditorUtility.SetDirty(targetMagicData);
        AssetDatabase.SaveAssets();

        string result = $"{successCount} 件の魔法データを{(importMode == ImportMode.Append ? "追加" : "上書き")}しました。\n" +
                        $"現在の総数: {targetMagicData.magicList.Count} 件";

        if (errorLogs.Count > 0)
        {
            result += "\n\nエラー一覧:\n" + string.Join("\n", errorLogs);
        }

        EditorUtility.DisplayDialog("インポート完了", result, "OK");
    }
}

とりあえず前回のScriptableObjectの例で作った MagicData への変換クラスを作ってみました。
基本的にはCSVデータを一行ずつ読み込んで、データクラスの形にして渡しているだけです。
よくあるCSV Parserと似たような形になりましたね。

ちょっと補足するとしたら、一応エディタ拡張でウィンドウで設定が確認できるようにしているとかでしょうか?
データ追加、上書きも設定次第で変更できるイメージです。

このコードをコピペして、メニューの “Tools/Import Magic Data from CSV” をクリックすると、以下のようなウィンドウが表示されます。

ちなみにcsvの形式はこんな感じのイメージです。

CSVData_MagicData.csv

magicName,description,power,manaCost,element
ファイアボール,火の玉を投げる魔法,50,10,Fire
ウォーターボール,水の玉を投げる魔法,50,10,Water
ヒール,HPを回復する魔法,0,15,Light

MagicDataは前回の記事のサンプルコードをそのまま使ってます。

MagicData.cs(ScriptableObjectの定義)

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

[Serializable]
public class MagicElement
{
    public string magicName;
    public string description;
    public int power;
    public int manaCost;
    public ElementType element;
}

public enum ElementType
{
    Fire,
    Water,
    Wind,
    Earth,
    Light,
    Dark
}

[CreateAssetMenu(fileName = "MagicData", menuName = "Game/MagicData")]
public class MagicData : ScriptableObject
{
    public List<MagicElement> magicList = new List<MagicElement>();
}

最後に

正直、今回の内容はそのまま使うって言うよりも、「そういう管理をしている人がいるんだなぁ〜」くらいに思ってもらえたら十分かなと思います。
そもそもの話、CSVからScriptableObjectにして使うのが一般的かどうかもわかってないので…
(データクラスをいちいち作るよりは手間が省けるかな?くらい)

もし、試してみようかなー、こういうことやりたかったんだよーって方がいたら、ぜひ参考に!
あくまでMagicDataに合わせたサンプルコードなので、使いたい形に合わせて変更しながら作って頂ければなと思います。

コメント

タイトルとURLをコピーしました