初めに
最近 MasterMemoryのv3が出たので触っていきます。今回はゲームのマスター(Google Sheet)を想定してダウンロードや使用についても行っていきます。
以下にて記事の内容のリポジトリを公開しています。
csvは Assets/StreamingAssets/masterdata-dev1.csv に保存されています。
開発環境
- Unity6000.0.38f1
導入
まずは NuGetForUnityを導入します。
Package Managerに URLから導入します。

次に NuGet For UnityのWindowを開いて MasterMemory を検索します。

また init を使用する場合は、以下のコードを定義しておく必要があります。
namespace System.Runtime.CompilerServices { internal static class IsExternalInit { } }
マスターデータの作成
以下の記事を参考に マスターデータ取得用の GET のエンドポイント作成します。
今回は マスターデータを作成して、CSVデータをローカルに保存しているものとします。
以下のようなマスターデータを定義します
id name nameJP rotationCenterPlanetId radius gravity orbitalSpeed lightIntensity lightOuterRadius int string string int float float float float float id 惑星名 惑星名-日本語 公転の中心天体 惑星の半径 重力 公転速度(-の場合は反対回転) 天体のライトの強さ ライトの外側の半径 1 Earth 地球 1 1 1250 0.5 0.8 3 2 Mercury 水星 1 0.5 1450 0.5 0.8 3 3 Venus 金星 1 0.85 1200 0.5 0.8 3 4 Sun 太陽 1 0.5 1350 0.5 2 3 5 Moon 月 1 0.5 1600 0.5 0.5 3
このマスターデータのcsvを 以下のパスに保存しておきます。
Assets/StreamingAssets/masterdata-dev1.csv
実際のプロジェクトでは CSVの情報から MetaDatabase を使ってバイナリーに変換したり、マスターデータに一度入れた後にビルドしてバイナリーに保存したりなどの運用になりますが、今回はその辺の話はスキップします。
CSVをMasterMemoryのデータに変換
まずはマスターデータのデータベースに応じたクラスを作成します。
[MemoryTable("planet"), MessagePackObject(true)] public record PlanetMaster { [PrimaryKey] public int Id { get; init; } public string Name { get; init; } public string NameJP { get; init; } public int RotationCenterPlanetId { get; init; } public float Radius { get; init; } public float Gravity { get; init; } public float OrbitalSpeed { get; init; } public float LightIntensity { get; init; } public float LightOuterRadius { get; init; } }
検索に使用するkeyには、[PrimaryKey] を付けておきます。
次のローカルにあるCSVからMasterMemoryにロードします。
// CSV ファイルをテキストとして読み込み string csvText = await File.ReadAllTextAsync(FilePath, _cts.Token); // CSV から MasterMemory 用の MemoryDatabase を構築 MemoryDatabase memoryDatabase = MasterMemoryLoader.BuildDatabaseFromCSV(csvText);
MasterMemoryからデータを検索
使用時には以下のように指定したkeyから検索をすることができます
var allPlanets = memoryDatabase.PlanetMasterTable; Debug.Log($"データの数:{allPlanets.Count}"); var firstPlanet = allPlanets.FindById(1); Debug.Log($"id:{firstPlanet.Id}, name:{firstPlanet.Name}, nameJP:{firstPlanet.NameJP}");
実行すると以下のようなログが表示されます。

自動生成ファイルについて
v3から MasterMemoryのデータを操作するためのフォーマッタは、Source Generatorによって自動生成されるようになりました。
実際には今回の場合は以下のようなコードが生成されています
// <auto-generated /> #pragma warning disable #nullable enable using MM; using MasterMemory.Validation; using MasterMemory; using MessagePack; using System.Collections.Generic; using System; using MM.Tables; namespace MM { public sealed class MemoryDatabase : MemoryDatabaseBase { public PlanetMasterTable PlanetMasterTable { get; private set; } = default!; public MemoryDatabase( PlanetMasterTable PlanetMasterTable ) { this.PlanetMasterTable = PlanetMasterTable; } public MemoryDatabase(byte[] databaseBinary, bool internString = true, MessagePack.IFormatterResolver? formatterResolver = null, int maxDegreeOfParallelism = 1) : base(databaseBinary, internString, formatterResolver, maxDegreeOfParallelism) { } protected override void Init(Dictionary<string, (int offset, int count)> header, System.ReadOnlyMemory<byte> databaseBinary, MessagePack.MessagePackSerializerOptions options, int maxDegreeOfParallelism) { if (maxDegreeOfParallelism == 1) { InitSequential(header, databaseBinary, options, maxDegreeOfParallelism); } else { InitParallel(header, databaseBinary, options, maxDegreeOfParallelism); } } void InitSequential(Dictionary<string, (int offset, int count)> header, System.ReadOnlyMemory<byte> databaseBinary, MessagePack.MessagePackSerializerOptions options, int maxDegreeOfParallelism) { this.PlanetMasterTable = ExtractTableData<PlanetMaster, PlanetMasterTable>(header, databaseBinary, options, xs => new PlanetMasterTable(xs)); } void InitParallel(Dictionary<string, (int offset, int count)> header, System.ReadOnlyMemory<byte> databaseBinary, MessagePack.MessagePackSerializerOptions options, int maxDegreeOfParallelism) { var extracts = new Action[] { () => this.PlanetMasterTable = ExtractTableData<PlanetMaster, PlanetMasterTable>(header, databaseBinary, options, xs => new PlanetMasterTable(xs)), }; System.Threading.Tasks.Parallel.Invoke(new System.Threading.Tasks.ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }, extracts); } public ImmutableBuilder ToImmutableBuilder() { return new ImmutableBuilder(this); } public DatabaseBuilder ToDatabaseBuilder() { var builder = new DatabaseBuilder(); builder.Append(this.PlanetMasterTable.GetRawDataUnsafe()); return builder; } public DatabaseBuilder ToDatabaseBuilder(MessagePack.IFormatterResolver resolver) { var builder = new DatabaseBuilder(resolver); builder.Append(this.PlanetMasterTable.GetRawDataUnsafe()); return builder; } #if !DISABLE_MASTERMEMORY_VALIDATOR public ValidateResult Validate() { var result = new ValidateResult(); var database = new ValidationDatabase(new object[] { PlanetMasterTable, }); ((ITableUniqueValidate)PlanetMasterTable).ValidateUnique(result); ValidateTable(PlanetMasterTable.All, database, "Id", PlanetMasterTable.PrimaryKeySelector, result); return result; } #endif static MasterMemory.Meta.MetaDatabase? metaTable; public static object? GetTable(MemoryDatabase db, string tableName) { switch (tableName) { case "planet": return db.PlanetMasterTable; default: return null; } } #if !DISABLE_MASTERMEMORY_METADATABASE public static MasterMemory.Meta.MetaDatabase GetMetaDatabase() { if (metaTable != null) return metaTable; var dict = new Dictionary<string, MasterMemory.Meta.MetaTable>(); dict.Add("planet", MM.Tables.PlanetMasterTable.CreateMetaTable()); metaTable = new MasterMemory.Meta.MetaDatabase(dict); return metaTable; } #endif } }