2K Views
December 28, 24
スライド概要
渋谷で働くGame Programmer (Unity) at CyberAgent, Inc. | Unity Weekly: http://bit.ly/3i6fRPe | Blog: http://bit.ly/3iheiOE | 最近ポケカはじめました
MasterMemory v3 最速確認会 【年末だよ】Unity お・と・なのLT大会 2024 2024/12/28 @yucchiy_
このLTで話したいこと • MasterMemoryについてのおさらい • MasterMemoryについて • MessagePackとの関係やコード生成について • MasterMemory v3の変更点の紹介 • Source Generator対応について • MasterMemory v3の利用方法の紹介 • 導入方法から簡単な使い方について
自己紹介 向井 祐一郎 • 株式会社 サイバーエージェント / 株式会社アプリ ット • Lead Developer Experience • システム系の基盤開発・プロジェクトの開発支援 SNS・個人活動など • @yucchiy_(X) • Unity Weekly • https://blog.yucchiy.com/tags/unity-weekly/ ボ • UniTips(社内有志で技術書典に執筆)
MasterMemory のおさらい
• マスター ータの管理に主眼を置いたインメモリ ータベース • 省メモリ・高速なデータベースロード・高速な検索の3点に重点を置く • 弊社導入事例: Unity/C# ーム開発における、クライアントでのマスターデータの扱い方 • https://blog.applibot.co.jp/2024/01/19/unity-master-data/ • ータベースのフォーマットは MessagePack • データを読み込んで、メモリ上にデータベースを構築する • 書き出されたデータの読み込みでMessagePackのデータの解析に、 MessagePackのシリアライ が必要になる デ ザ ゲ https://github.com/Cysharp/MasterMemory デ デ MasterMemory とは
デ ータの定義 [MemoryTable("person"), MessagePackObject(true)] public record Person { [PrimaryKey] public int PersonId { get; set; } [SecondaryKey(0), NonUnique] [SecondaryKey(1, keyOrder: 1), NonUnique] public int Age { get; set; } [SecondaryKey(2), NonUnique] [SecondaryKey(1, keyOrder: 0), NonUnique] public Gender Gender { get; set; } public string Name { get; set; } } 属性を用いて各カラムの “扱い” を定義
デ ータベースの検索 データベースの読み込み (byte[] を引数に初期化) var db = new MemoryDatabase( File.ReadAllBytes( Path.Combine(Application.dataPath, "StreamingAssets", "database.bytes") ) ); Person person = db.PersonTable.FindByPersonId(3); RangeView<Person> persons = db.PersonTable.FindByGender(Gender.Male); PrimaryKeyで検索 SecondaryKeyで範囲検索
MessagePackのフォーマッタ
独自型のシリアライ
のために、内部でフォーマッタが必要になる
[MemoryTable(“person”), MessagePackObject(true)]
public record Person
{
[PrimaryKey]
public int PersonId { get; init; }
internal sealed class PersonFormatter :
MessagePack.Formatters.IMessagePackFormatter<Samples.Person>
{
public void Serialize(
ref MessagePack.MessagePackWriter writer,
Samples.Person value,
MessagePack.MessagePackSerializerOptions options)
{
[SecondaryKey(0), NonUnique]
[SecondaryKey(1, keyOrder: 1), NonUnique]
public int Age { get; init; }
var formatterResolver = options.Resolver;
writer.WriteMapHeader(4);
writer.WriteRaw(GetSpan_PersonId());
// …
[SecondaryKey(2), NonUnique]
[SecondaryKey(1, keyOrder: 0), NonUnique]
public Gender Gender { get; init; }
}
public Samples.Person Deserialize(
ref MessagePack.MessagePackReader reader,
MessagePack.MessagePackSerializerOptions options)
{
// …
}
public string Name { get; init; }
}
ズ
}
MessagePackCompiler(mpc) • IL2CPP下のUnityではフォーマッタの事前生成が必要(AOT Code Generation) • mpcを用いて実行前にコード生成しておく dotnet tool install --global MessagePack.Generator // Simple Sample: dotnet mpc -i "..\src\Sandbox.Shared.csproj" -o "MessagePackGenerated.cs" // Use force map simulate DynamicContractlessObjectResolver dotnet mpc -i "..\src\Sandbox.Shared.csproj" -o "MessagePackGenerated.cs" -m
MasterMemory.Generator
定義された
ータを操作するためのロジックを生成するツール
Person person = db.PersonTable.FindByPersonId(3);
RangeView<Person> persons =
db.PersonTable.FindByGender(Gender.Male);
MasterMemory.Generator.exe
-i “C:\UnitySample”
-o “C:\UnitySample\Generated"
-n "UnitySample"
[MemoryTable("person"), MessagePackObject(true)]
public record Person
{
[PrimaryKey]
public int PersonId { get; set; }
[SecondaryKey(0), NonUnique]
[SecondaryKey(1, keyOrder: 1), NonUnique]
public int Age { get; set; }
[SecondaryKey(2), NonUnique]
[SecondaryKey(1, keyOrder: 0), NonUnique]
public Gender Gender { get; set; }
public string Name { get; set; }
デ
}
MasterMemory v3の変更点
前提: MessagePack v3の ップ ートについて • v3(v3.0.3)にて、フォーマッタの事前生成がSource Generator化された • プロジェクトコードのコンパイル時にフォーマッタが自動生成される • 上記の対応に伴い、mpcが不要になった デ ア https://github.com/MessagePack-CSharp/MessagePack-CSharp/releases/tag/v3.0.3
MasterMemory v3の ップ ート • MessagePack v3が取り込まれた • これによりMessagePackのSource Generator対応が取り込まれた • あわせて、MasterMemory.GeneratorがSource Generator化された • MasterMemoryのコード生成に関する実装が、すべてSource Generator化された デ ア https://neue.cc/2024/12/20_mastermemory_v3.html
MasterMemory v3の導入方法
インストール だ NuGet For Unityで「MasterMemory」をインストールする( け!)
asmdef を切る場合の設定 “Override References” をONにし、”Assembly References”にDLLを設定
各アセン
リごとに「MasterMemoryGeneratorOptions」を設定する
using MasterMemory;
v2以前ではコマンドラインのオプションで指定していた箇所
[assembly: MasterMemoryGeneratorOptions(
Namespace = "Samples",
IsReturnNullIfKeyNotFound = false,
PrefixClassName = "Hoge"
)]
namespace System.Runtime.CompilerServices
{
internal sealed class IsExternalInit { }
}
Initプロパティを利用したい場合は定義
ブ
デ
ータベースの設定
MasterMemory v3の使い方
デ ータの定義 v2以前と比べ、利用方法に変更はない [MemoryTable("person"), MessagePackObject(true)] public record Person { [PrimaryKey] public int PersonId { get; init; } [SecondaryKey(0), NonUnique] [SecondaryKey(1, keyOrder: 1), NonUnique] public int Age { get; init; } [SecondaryKey(2), NonUnique] [SecondaryKey(1, keyOrder: 0), NonUnique] public Gender Gender { get; init; } public string Name { get; init; } }
ゲ デ ータベースの書き出し v2以前と比べ、利用方法に変更はない var builder = new DatabaseBuilder(); ームごとに管理されている マスターデータを読み込んで builder.Append(new Person[] { new() { PersonId = 1, Age = 20, Gender = Gender.Male, Name = "John" }, new() { PersonId = 2, Age = 22, Gender = Gender.Female, Name = "Jane" }, new() { PersonId = 3, Age = 24, Gender = Gender.Male, Name = "Jack" }, }); File.WriteAllBytes( Path.Combine(Application.dataPath, "StreamingAssets", "database.bytes"), builder.Build() ); データベースファイルとして書き出す
デ ータベースの検索 v2以前と比べ、利用方法に変更はない var db = new MemoryDatabase( File.ReadAllBytes( Path.Combine(Application.dataPath, "StreamingAssets", "database.bytes") ) ); Person person = db.PersonTable.FindByPersonId(3); RangeView<Person> persons = db.PersonTable.FindByGender(Gender.Male);
余談: コンパイル時のエラー Unity 6で再現する、Unity側のバ とのこと グ https://x.com/neuecc/status/1870994368020296171
まとめ • MasterMemory v3の、Source Generator対応に焦点を当てて紹介しました • Source Generator対応により、導入からコード生成の手間が大幅に省けた • これによって開発体験が大きく向上した • MasterMemory v2からv3について利用方法の大きな変更はない • 下回り(Source Generator対応)がメインになる • 移行コストは低そう(Unityバージョンの要求は高いので、運用タイトルからの移行はチョット高い) → MasterMemory v3、積極的に使っていきたい