0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unity めんどうくさいデータをひな型自動生成🐤

0
Last updated at Posted at 2026-08-11

header

🎮 Unityを愛する努力 〜コード自動生成で開発体験を変える〜

みなさまはUnityが大好きでしょうか? 私は愛しております。

💡 Unityが選ばれる理由

  • 初心者でも優しいゲームエンジン
  • C#をベースにしているため扱いやすい
  • UEよりもネットに様々な知見や技術ブログが多い

けれども、私は現状不満なところがあります。
みなさんもこれを読んでいるということは、Unityを愛せるけど、不満なところがある人だらけだと思います。

なので、私は少しでも愛せる努力をするためにある事をしました。


📌 目次

  1. Unityのコードの一部自動化(非AI)
  2. SupportChigadio とは
  3. データクラス生成機能
  4. コードでのデータの見方
  5. さいごに

Unityのコードの一部自動化(非AI)

私がUnityで現状不満なのが、コード生成の部分です。

test.cs
using UnityEngine;
 
public class Test : MonoBehavior
{
}

よく使われるコードですが、ではこういうのはどうでしょうか?

testdata.cs
using UnityEngine;
 
public class TestData
{
}
atestdata.cs
using UnityEngine;
 
public class ATestData : TestData
{
}
btestdata.cs
using UnityEngine;
 
public class BTestData : TestData
{
}

どうでしょう? プログラマーならこのめんどうくささはわかると思います。

😩 あるあるな煩わしさ

  • 同じようなスクリプトの生成
  • 同じような書き方なのに手動入力

そこの煩わしさを、できるだけ改善したのが今回私が開発中であるサポートライブラリです。


🧩 SupportChigadio

▶ GitHubリポジトリはこちら

項目 内容
必要ライブラリ Addressable / UniTask
ステータス 開発中(一部自動生成でエラーが出るファイルがあり修正中)
目的 開発環境での煩わしさをできるだけフリーにすること

その中で、今回は一部の機能をご紹介します。


🗂️ データクラス生成

独自のデータ型をUIで作り自動スクリプト生成できる機能です。
プログラマー以外でも、プランナーなど他の人でも視覚的にデータを入力・編集できるメリットがあります。

🚀 セットアップ手順

  1. GitHub からリポジトリを取得
  2. 中にある app_normal.bat をクリックして起動
  3. ブラウザで http://localhost:8000/ にアクセス

起動画面

📋 ClassDataID(データ型)

左のスライドバーから ClassDataID を選択すれば、このような画面になります。
ここで、簡易的にデータクラスをコードではなくUIで設定できます。

ClassDataID画面1
ClassDataID画面2
ClassDataID画面3

各データの変数の設定や、データ入力も対応しています。

✅ 操作の流れ

  1. 編集が終われば 保存ボタン を押す
  2. C#生成ボタン を押す
  3. 最初のGridまで戻り、全バイナリ生成 / ヘッダー生成 / TableID生成 のボタンをすべて押す

すると、app_normal.bat が置いてあるフォルダパスの一つ上のパスに data というフォルダが生成されます。
その中の class_data_id フォルダに自動生成されます。

🔧 自動生成される実際のコード(例:PlaceMap)

Row定義

placemaprow.cs
using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
using GameCore.Tables.ID;
using GameCore.Enums;
 
namespace GameCore.Tables
{
    public class PlaceMapRow : BaseClassDataRow
    {
        [SerializeField]
        protected PlaceMapTableID table_id;
        public PlaceMapTableID TableID { get => table_id;}
        [SerializeField]
        protected string name;
        public string Name { get => name; } // 名前
        [SerializeField]
        protected bool use;
        public bool Use { get => use; } // 使用フラグ
        [SerializeField]
        protected Dictionary<GameCore.Enums.SeatPlaceID, GameCore.Classes.PlaceData> place_map = new Dictionary<GameCore.Enums.SeatPlaceID, GameCore.Classes.PlaceData>();
        public Dictionary<GameCore.Enums.SeatPlaceID, GameCore.Classes.PlaceData> Place_map { get => place_map; } // プレスデータ(辞書)
 
        public override void Read(int id,BinaryReader reader)
        {
            table_id = (PlaceMapTableID)id;
                    int len_name = reader.ReadInt32();
                    name = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(len_name));
            use = reader.ReadBoolean();
            place_map = new Dictionary<GameCore.Enums.SeatPlaceID, GameCore.Classes.PlaceData>();
            int place_map_count = reader.ReadInt32();
            for (int place_map_i = 0; place_map_i < place_map_count; place_map_i++) {
                GameCore.Enums.SeatPlaceID place_map_key;
                place_map_key = (GameCore.Enums.SeatPlaceID)Enum.ToObject(typeof(GameCore.Enums.SeatPlaceID), reader.ReadInt32());
                GameCore.Classes.PlaceData place_map_val;
                place_map_val = new GameCore.Classes.PlaceData();
                place_map_val.Read(reader);
                place_map[place_map_key] = place_map_val;
            }
        }
    }

Table定義

PlaceMapTable.cs
using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
using GameCore.Tables.ID;
using GameCore.Enums;
 
namespace GameCore.Tables
{
    public class PlaceMapTable : BaseClassDataID<PlaceMapTableID, PlaceMapRow>
    {
        static PlaceMapTable()
        {
            RowIndex = new PlaceMapRowIndex();
            TableId = TableID.PlaceMap;
            RegisterReferenceLoader(); // 依存先プリロード用に自分自身を登録
        }
 
        public override void Read(BinaryReader reader)
        {
            PlaceMapTable.Table.Clear();
            int rowCount = reader.ReadInt32();
            int colCount = reader.ReadInt32();
            var colNames = new string[colCount];
            var colTypes = new string[colCount];
            for(int i=0; i<colCount; i++) {
                int len = reader.ReadInt32();
                colNames[i] = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(len));
                len = reader.ReadInt32();
                colTypes[i] = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(len));
            }
            RowIndex.Read(reader, true); // 行インデックスブロックを読み進めつつキャッシュしておく(高速な連続読みはそのまま維持)
            for(int r=0; r<rowCount; r++) {
                var enumVal = (PlaceMapTableID)Enum.ToObject(typeof(PlaceMapTableID), reader.ReadInt32());
                var row = new PlaceMapRow();
                row.Read(r + 1,reader);
                Table[enumVal] = row;
            }
        }
    }
}

TableID Enum

PlaceMapTableID.cs
using System;
 
namespace GameCore.Tables.ID
{
    public enum PlaceMapTableID
    {
        None = 0,
        PlaceMap_CafeMap = 1,
        PlaceMap_01 = 2,
        PlaceMap_02 = 3,
        PlaceMap_03 = 4,
        PlaceMap_04 = 5,
        PlaceMap_05 = 6,
        PlaceMap_06 = 7,
        PlaceMap_07 = 8,
        PlaceMap_08 = 9,
        PlaceMap_09 = 10,
        PlaceMap_10 = 11,
        PlaceMap_11 = 12,
        PlaceMap_12 = 13,
        PlaceMap_13 = 14,
        PlaceMap_14 = 15,
        PlaceMap_15 = 16,
        PlaceMap_16 = 17,
        PlaceMap_17 = 18,
        PlaceMap_18 = 19,
        PlaceMap_19 = 20,
        PlaceMap_20 = 21,
        PlaceMap_21 = 22,
        PlaceMap_22 = 23,
        PlaceMap_23 = 24,
        PlaceMap_24 = 25,
        PlaceMap_25 = 26,
        PlaceMap_26 = 27,
        PlaceMap_27 = 28,
        PlaceMap_28 = 29,
        PlaceMap_29 = 30,
        Max
    }
}

これがすべて自動生成されます。
また、class_data_id フォルダの中に all_class_data.bytes があるので、これをAddressableに登録して終了です!

あとはデータを呼び出す前に、このbytesの中を読み取り事前にデータをロードする方法は、

Load.cs
/// これをどこかで呼び出す
await LoadAsyncCore();
/// Coreという名前は、最初の一枚目の画像でPlaceMapはCoreと設定しているため

💻 コードでのデータの見方

では、読み込んだデータをどうコードで呼び出すか?
こちらも呼び出し方は自動で作られます。

まず、自動で作られるコード(今回の例:PlaceMap)ですと

PlaceMapTableIDExsample.cs
using System;
using UnityEngine;
using GameCore.Tables;
using GameCore.Tables.ID;
using System.Collections.Generic;
namespace GameCore.Tables
{
    public static class PlaceMapIDExtensions
    {
        public static PlaceMapRow GetRow(this PlaceMapTableID id)
        {
            if (PlaceMapTable.Table.TryGetValue(id, out var row))
            {
                return row;
            }
            else
            {
                return null; // または throw new KeyNotFoundException()
            }
        }
        public static int ToInt(this PlaceMapTableID id)
        {
            return (int)id;
        }
        
        public static int ToIndex(this PlaceMapTableID id)
        {
            return (int)id - 1;
        }
        public static PlaceMapTableID ToPlaceMapTableID(this int id)
        {
            return (PlaceMapTableID)id;
        }
        public static void ForID(Action<PlaceMapTableID> action)
        {
            if (action == null) throw new ArgumentNullException(nameof(action));
            for (EnumIDIter<PlaceMapTableID> id = PlaceMapTableID.PlaceMap_CafeMap; id < PlaceMapTableID.Max; id++)
            {
                action(id);
            }
        }
        public static List<PlaceMapTableID> FindAll(Func<PlaceMapTableID, bool> predicate)
        {
            if (predicate == null) throw new ArgumentNullException(nameof(predicate));
            var results = new List<PlaceMapTableID>();
            for (EnumIDIter<PlaceMapTableID> id = PlaceMapTableID.PlaceMap_CafeMap; id < PlaceMapTableID.Max; id++)
            {
                PlaceMapTableID value = id;
                if (!Enum.IsDefined(typeof(PlaceMapTableID), value))continue; // 無効な値はスキップ
                if (predicate(value))results.Add(value);
            }
            
            return results;
        }
        
        public static PlaceMapTableID Find(Func<PlaceMapTableID, bool> predicate)
        {
            if (predicate == null) throw new ArgumentNullException(nameof(predicate));
            for (EnumIDIter<PlaceMapTableID> id = PlaceMapTableID.PlaceMap_CafeMap; id < PlaceMapTableID.Max; id++)
            {
                PlaceMapTableID value = id;
                if (!Enum.IsDefined(typeof(PlaceMapTableID), value))continue; // 無効な値はスキップ
                if (predicate(value))return value;
            }
            
            return PlaceMapTableID.None; // デフォルト値(必要に応じて変更)
        }
    }
}

これも自動生成されます。
なので、先ほどの事前読み込み(Load)をしたあとに——

test.cs
var row = PlaceMapTableID.PlaceMap_CafeMap.GetRow();
var name = row.Name; ///一枚目の画像のとおり、カフェが入る

これだけです。
自分からコードを手動ではなく、ある程度の自動生成でのコード。
これにより、より開発速度が上がると思います。


🔮 さいごに

ここまで書きましたが、まだまだSupportChigadioは他の自動生成に対応しております。

🛠️ 対応予定の機能

  • Matrixの対応
  • Stateなど

エラーがまだ出ている場所もありますが、これからも開発を進めたいと思います。

⭐ 気になった方はぜひGitHubをチェックしてみてください → SupportChigadio

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?