💾
GodotでMessagePack for C# を使ってみる
MessagePackはJsonやprotobufよりも速いらしいのでマルチプレイのゲームを作る場合やゲームデータのセーブなどに使えます。
GodotEngineでMessagePackを行うにはMessagePack for C#を使うのが良いかと思います。
gdscriptでmesagepackができるプラグインが今のところ見つからなかったので、ここはC#を使います。
ただ、Godot名前空間にあるVector2
,Vector3
などの型は対応していないのでCustomFormmatterを使ってシリアライズ/デシリアライズできるようにする必要があります。
CustomFormatterを定義
試しにVector2
型のCustomFormatterを作ってみました
Formatters.cs
public sealed class Vector2Formatter : global::MessagePack.Formatters.IMessagePackFormatter<global::Godot.Vector2>
{
public global::Godot.Vector2 Deserialize(ref MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options)
{
if (reader.IsNil)
{
throw new InvalidOperationException("typecode is null, struct not supported");
}
var length = reader.ReadArrayHeader();
var x = default(float);
var y = default(float);
var z = default(float);
for (int i = 0; i < length; i++)
{
var key = i;
switch (key)
{
case 0:
x = reader.ReadSingle();
break;
case 1:
y = reader.ReadSingle();
break;
default:
reader.Skip();
break;
}
}
var result = new global::Godot.Vector2(x, y);
return result;
}
public void Serialize(ref MessagePackWriter writer, global::Godot.Vector2 value, global::MessagePack.MessagePackSerializerOptions options)
{
writer.WriteArrayHeader(2);
writer.Write(value.X);
writer.Write(value.Y);
}
}
このCustomFormatterを使ってCustomResolverを作ります。
using MessagePack.Formatters;
using MessagePack.Resolvers;
public sealed class GodotResolver : IFormatterResolver
{
public static readonly IFormatterResolver Instance = new GodotResolver();
private static readonly IMessagePackFormatter[] Formatters = new IMessagePackFormatter[]
{
// ここにCustomFromatterを登録する
new Vector2Formatter(),
new Vector3Formatter(),
};
private static readonly IFormatterResolver[] Resolvers = new IFormatterResolver[]
{
StandardResolver.Instance,
};
private GodotResolver(){}
public IMessagePackFormatter<T> GetFormatter<T>()
{
foreach (var formatter in Formatters)
{
if (formatter is IMessagePackFormatter<T> typedFormatter)
{
return typedFormatter;
}
}
foreach (var resolver in Resolvers)
{
var formatter = resolver.GetFormatter<T>();
if (formatter != null)
{
return formatter;
}
}
return null;
}
}
シリアライズオプションにこのResolverを登録します
public override void _Ready()
{
var resolver = MessagePack.Resolvers.CompositeResolver.Create(GodotResolver.Instance);
var options = MessagePackSerializerOptions.Standard.WithResolver(resolver);
MessagePackSerializer.DefaultOptions = options;
}
これでgodotでMessagePackを使う準備はできました。試しにクラスを定義してシリアライズして見ます。
using Godot;
using MessagePack;
[MessagePackObject]
public readonly struct PlayerData
{
[Key(0)]
public readonly string name;
[Key(1)]
public readonly int age;
[Key(2)]
public readonly Vector2 position;
public PlayerData(string name, int age, Vector2 position)
{
this.name = name;
this.age = age;
this.position = position;
}
}
public override void _Ready()
{
var resolver = MessagePack.Resolvers.CompositeResolver.Create(GodotResolver.Instance);
var options = MessagePackSerializerOptions.Standard.WithResolver(resolver);
MessagePackSerializer.DefaultOptions = options;
var data = new PlayerData(
name: "alice",
age: 23,
position: new Vector2(1.0f,2.0f));
var bytes = Serialize(data);
}
public byte[] Serialize(PlayerData data)
{
var bin = MessagePackSerializer.Serialize(data);
var json = MessagePackSerializer.ConvertToJson(bin);
GD.Print(json); //json形式でlogに出力してみる
return bin;
}
string
やint
に加えてVector2
型もしっかりシリアライズできています。
まとめ
CustomFormatterを自前で用意するのは面倒ですが、かなり簡単だったので誰でも実装できると思います。ゲームを作る上でセーブシステムなどは必要ですし、C#が使えるGodotならC#のライブラリが利用できるので、いろいろと便利ですね。
ちなみにVisualStudioを使っていてコメントなどに日本語が含まれているとバグるので、気を付けましょう。https://qiita.com/hyahoitaro/items/802f58595f4f971510f7
Discussion