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を愛する努力 〜Matrix対応編〜

0
Last updated at Posted at 2026-08-16

🎮 Unityを愛する努力 〜Matrix対応編〜

前回、SupportChigadio のデータクラス生成機能(ClassDataID)についての記事を書きました。

記事の最後で「対応予定の機能」として挙げていた Matrix が形になってきたので、今回はその続編としてご紹介します。

この記事の対象読者

  • 前回の記事でSupportChigadioのClassDataIDまでは触った人
  • 「row×col」のような二次元の関連データをUnityで扱いたい人

📌 目次

  1. Matrixとは何か
  2. セットアップ・操作の流れ
  3. 自動生成される実際のコード
  4. 地味だけど一番作り込んだところ:部分ロード
  5. コードでの呼び出し方
  6. さいごに

🧩 Matrixとは何か

ClassDataIDは「1つのIDに対して1行のデータ」というシンプルな構造でした。

一方でゲームを作っていると、こういうデータが欲しくなることがよくあります。

  • キャラA × キャラB のニックネーム(あだ名は相手によって変わる)
  • キャラ × トークタイプ の組み合わせで変わる会話パターン
  • 座席 × 時間帯 で変わる混雑度、みたいな二次元テーブル
    つまり 「行(row)」と「列(col)」の組み合わせで1つのデータが決まる、二次元のテーブルです。これを表現するために追加したのがMatrix機能です。

イメージとしてはこんな感じの表です。

row \ col TestB_01 TestB_02
TestA_01 "おいキャラC" 呼び "よぉ" 呼び
TestA_02 "先輩" 呼び -

rowとcolは、それぞれ既存のEnumやClassDataID(前回紹介した機能)を自由に組み合わせて指定できます。row側とcol側が同じ型(例えばキャラID同士)でも、違う型(キャラID×トークタイプID)でも対応しています。


🚀 セットアップ・操作の流れ

基本的な操作感は前回のClassDataIDと同じです。

🧩 SupportChigadio

▶ GitHubリポジトリはこちら

項目 内容
必要ライブラリ Addressable / UniTask
ステータス 開発中(一部自動生成でエラーが出るファイルがあり修正中)
目的 開発環境での煩わしさをできるだけフリーにすること
  1. 左のスライドバーから ClassDataMatrixID を選択
  2. Matrix名(例:TestMatrix)を決めて、row側の型col側の型を選択(Enum / ClassDataIDどちらもOK)
  3. フィールド(データの中身)を定義
  4. 表形式のUIで、row×colの各セルにデータを入力
  5. 保存 → C#生成 ボタン
  6. 最初のGridまで戻り、Matrix用の全バイナリ生成 / ヘッダー生成 ボタンを押す

C#生成を押すと、data/class_data_matrix_id フォルダの中に、Matrix名ごとのフォルダが作られ、その中に必要なスクリプトが一式自動生成されます。

スクリーンショット 2026-08-17 001247.png

スクリーンショット 2026-08-17 001229.png

スクリーンショット 2026-08-17 001306.png


🔧 自動生成される実際のコード

例として、row=キャラID、col=キャラID の NickNameChara というMatrixを作った場合を見てみます。

Row定義

TestMatrixMatrixRow.cs
using System.IO;
using System;
using System.Collections.Generic;
using UnityEngine;
using GameCore.Enums;
using GameCore.Tables.ID;

namespace GameCore.Tables {
    public class TestMatrixMatrixRow : BaseClassDataMatrixRow {
        [SerializeField]
        protected TestATableID rowId_;
        public TestATableID RowId { get => rowId_; }
        [SerializeField]
        protected TestBTableID colId_;
        public TestBTableID ColId { get => colId_; }

        [SerializeField]
        protected int value;
        public int Value { get => value; } // Default Value
        public override void Read(int rowId, int colId, BinaryReader reader) {
            rowId_ = (TestATableID)rowId;
            colId_ = (TestBTableID)colId;
            value = reader.ReadInt32();
        }
    }
}

自分がどのrow・colに属しているセルなのかを、RowId / ColId として自分自身が持っているので、セル単体を取り出したあとでも「誰から見た誰の呼び方か」が分かるようになっています。

Table定義(抜粋)

TestMatrixMatrixTable.cs
using System.IO;
using GameCore.Tables.ID;
using GameCore.Enums;
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;

namespace GameCore.Tables {
    public class TestMatrixMatrixTable : BaseClassDataMatrixID<TestATableID, TestBTableID, TestMatrixMatrixRow> {
        static TestMatrixMatrixTable()
        {
            RowIndex = new TestMatrixMatrixRowIndex();
            TableId = MatrixTableID.TestMatrix;
            MatrixTableRegistry.Loaders[TableId] = (header, reader) => header.GetData<TestMatrixMatrixTable>(MatrixTableID.TestMatrix, reader);
            MatrixTableRegistry.Unloaders[TableId] = () => { Table.Clear(); s_cellIndexCache.Clear(); };
        }
 
        public override void Read(BinaryReader reader) {
            // rowKeys / colKeys の読み込み → 行インデックス → 各セル、の順に読み進める
            ...
        }
 
        // 以下、LoadSingleAsync / LoadRowAsync / LoadColumnAsync など
        // セル・行・列単位でロードするためのメソッド群が自動で生えてくる
    }
}

ClassDataIDのときは Table : Dictionary<TableID, Row> という一次元の辞書でしたが、Matrixは Dictionary<TRow, Dictionary<TCol, Row>> という二段構えの辞書になっています。


🕳 地味だけど一番作り込んだところ:部分ロード

Matrixは仕組み上、row×colの組み合わせが多いとデータ量が大きくなりがちです。「使う予定のないセルまで全部メモリに載せる」のはもったいないので、必要な行・列・セルだけをピンポイントで読み込めるようにしています。

やっていることはシンプルで、バイナリファイルを書き出す時点で

  • テーブル全体の中の「この行はどこから始まるか」という行インデックス
  • 各行ブロックの中に埋め込んだ「この列はどこから始まるか」というセルインデックス
    をあらかじめ記録しておき、読み込み側はそのオフセット情報を頼りに、必要な場所だけ Seek して読む、という形です。
Matrixのバイナリ構造(イメージ)
├─ rowKeys / colKeys 一覧
├─ 行インデックス(rowId, offset, size)× 行数
└─ 行ブロック × 行数
     ├─ セルインデックス(colId, offset, size)× 列数
     └─ 各セルの実データ(列順に連結)

これのおかげで、「テーブル丸ごとロード」をしなくても、いきなり1セルだけ読みに行くAPIを呼んでも普通に動きます。初回アクセス時にそのテーブルの行インデックスだけその場で読んでキャッシュし、2回目以降はそのキャッシュを使い回す、という遅延ロードの仕組みになっています。


💻 コードでの呼び出し方

呼び出し方も、ClassDataID同様に自動生成されたユーティリティから呼ぶだけです。MatrixTableIdUtils.cs にMatrix名ごとの専用メソッドがまとめて生成されます。

test.cs
// セル単体だけロード
LoadSingleNickNameChara(TestATableID.TestA_01, TestBTableID.TestB_01);
 
// 行全体をロード(colは指定しない)
LoadSingleRowNickNameChara(TestATableID.TestA_01);
 
// 列全体をロード(rowは指定しない)
LoadSingleColumnNickNameChara(TestBTableID.TestB_01);
 
// 複数セルをまとめてロード
LoadSingleNickNameChara(new[] {
    (TestATableID.TestA_01, TestBTableID.TestB_01),
    (TestATableID.TestA_02, TestBTableID.TestB_02),
});

引数の組み合わせ(単体/配列/行のみ/列のみ)によって、複数のオーバーロードが自動で生成されるので、状況に応じて一番シンプルな書き方を選べます。

データを取り出すときは、ClassDataIDの GetRow() と同じ感覚で、Matrix専用の拡張メソッドを使います。

test.cs
var cell = TestATableID.TestA_01.GetCell(TestBTableID.TestB_01);
var nickName = cell.NickName;
 
// そのセルが存在するかだけ先に知りたい場合
if (TestATableID.TestA_01.HasCell(TestBTableID.TestB_01))
{
    // ...
}
 
// 行全体、列全体をまとめて取りたい場合
var row = TestATableID.TestA_01.GetNickNameCharaMatrixRow(); // Dictionary<colType, Row>
var col = TestBTableID.TestB_01.GetNickNameCharaMatrixCol(); // Dictionary<rowType, Row>

ちなみに GetRow という名前は、ClassDataID側の拡張メソッドと名前がかぶってあいまい参照エラーになるため、Matrix側は Get{Matrix名}MatrixRow / Get{Matrix名}MatrixCol という専用の名前にしています。


🔮 さいごに

前回の記事で「対応予定」としていたMatrixが、ひとまず一通り形になりました。

  • row×colの二次元データをUIから編集できる
  • ClassDataIDと同じ感覚で自動生成コードから呼び出せる
  • 必要なところだけピンポイントでロードできる部分ロード対応
    という感じで、単純な一覧データだけでなく、キャラ同士の関係性のような「組み合わせもの」もSupportChigadioで扱えるようになってきました。

引き続き、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?