Amazon GameSparks(preview)のMessage機能のデータ型がどのようにC#コードで表現されるか試してみた

2022.10.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

前回の記事では、Amazon GameSparksのMessage機能を使ってゲームクライアントとバックエンドサーバ間の双方向通信を実現しました。その際、GameSparksが生成したC#コードをUnityに組み込んで使用しました。このC#コードには、GameSparks上で定義づけたMessageのデータ型を元にしたDTOクラスなどが書かれていました。

今回は、GameSparks上で定義したデータ型がどのようなC#コードとして出力されるのかを具体的に見ていきたいと思います。

例として使用するデータ構造

今回は、位置情報ゲームのプレイヤーデータを扱うことを想定してMessage機能を使っていきます。

プレイヤーデータの構造は以下のようなものとします。

名前 用途 形式
name プレイヤー名 文字列
age 年齢(任意入力) 整数
level 現在のレベル 整数
isPro 有料課金プレイヤー判定 yesかnoの二値
lat 現在の緯度 実数
lon 現在の経度 実数
moveType 移動方式 徒歩、自転車、車のうちの1つ

GameSparksでのデータ型の作成

上記のデータ構造を元に、実際にGameSparks上でデータ型の設定をしていきます。
GameSparksでは、プリミティブ型として以下のものが使用できます。

  • String
  • Integer
  • Boolean
  • Decimal
  • Any

また、以下のような特殊なデータ型も設定することができます。

  • List
  • Map
  • Enum
  • Structure
  • Variant

この記事では、これらのデータ型全てを使用する形で設定を進めていきます。

GameSparksでは、Manage Game-defined shapesという画面でオリジナルのデータ型を登録できます。
これは、プログラミングであらかじめ独自の型定義をするのと同じような機能であり、データ型の登録をしておくことで、それを各Messageのデータ構造を設定する画面で呼び出すことができます。
この機能を使うことで、RequestsやNotifaicationsで同じデータ型を使い回すなどといったことが可能です。また、データ構造の変更が必要になった時、登録していたデータ型を修正するだけで全てのMessageにその内容が自動的に反映されます。

生成されたC#コードを見る

GameSparksでの設定とそれを元に生成されたC#コードを見比べていきましょう。

Structure

GameSparksでの設定

Structure型のフィールドとして、以下の項目を設定

名前 データ型
name String
age Integer (Not required)
level Integer
isPro Boolean
lat Decimal
lon Decimal
moveType MoveType
other Any

C#コード

public sealed class Player
{
    [JsonProperty]
    public string name { get; }
    [JsonProperty]
    public int? age { get; }
    [JsonProperty]
    public int level { get; }
    [JsonProperty]
    public bool isPro { get; }
    [JsonProperty]
    public Decimal lat { get; }
    [JsonProperty]
    public Decimal lon { get; }
    [JsonProperty]
    public MoveType moveType { get; }
    [JsonProperty]
    public dynamic other { get; }

    public Player(
        string name,
        int? age = null,
        int level,
        bool isPro,
        Decimal lat,
        Decimal lon,
        MoveType moveType,
        dynamic other)
    {
        this.name = name;
        this.age = age;
        this.level = level;
        this.isPro = isPro;
        this.lat = lat;
        this.lon = lon;
        this.moveType = moveType;
        this.other = other;
    }

    public override string ToString()
    {
        return string.Concat(
           $"{nameof(name)}: { name }", Environment.NewLine,
           $"{nameof(age)}: { age }");
           $"{nameof(level)}: { level }", Environment.NewLine,
           $"{nameof(isPro)}: { isPro }", Environment.NewLine,
           $"{nameof(lat)}: { lat }", Environment.NewLine,
           $"{nameof(lon)}: { lon }", Environment.NewLine,
           $"{nameof(moveType)}: { moveType }", Environment.NewLine,
           $"{nameof(other)}: { other }", Environment.NewLine,
    }
}

GameSparks上では、構造体としてPlayerを定義した形です。このようにGame-defined shapesとしてデータ型を登録することで、各Messageに流用できます。
一方で、C#コードではGameSparksの構造体の設定を元に典型的なDTOクラスが作成されています。

次に、この構造体の各フィールドについて細かく見ていきましょう。

一般的な型

GameSparksでの設定

名前 データ型
name String
age Integer (Not required)
level Integer
isPro Boolean
lat Decimal
lon Decimal

C#コード

string name
int? age
int level
bool isPro
Decimal lat
Decimal lon

文字列、整数、ブール、小数とGameSparks上での設定がそのままC#コードに反映されている形です。
任意項目として設定したageだけは、C#でnull許容を意味する?が付いています。

Any

GameSparksでの設定

名前 データ型
other Any

C#コード

public dynamic other

otherというフィールドは元々のデータ構造にはありませんでしたが、Anyの解説のために付け足しました。

AnyについてGameSparks上のドキュメントで解説している箇所は見当たりませんが、様々な言語に存在するAny型と同じく動的な型という意味で解釈して良いようです。
出力されたコードのように、C#では動的な型はdynamic型として表現します。
型による保護が無くなるため、遅延評価など特殊な用途以外での使用は推奨されません。

Enum

GameSparksでの設定

名前 データ型
MoveType Enum walk, bicycle, car

C#コード

public enum MoveType
{
   walk,
   bicycle,
   car        
}

GameSparks上で値の種類を登録することで、それが自動的にC#コードのEnumに反映されます。

List

Player StructureをListとして扱うようにデータ型を登録することもできます。

GameSparksでの設定

名前 データ型 値の型
PlayerList List Player

C#コード

List<Player> playerList

複数のPlayerデータをListにまとめることで、Playerの一覧を表示したり集計したりという用途に使えそうです。

Map

ListだけでなくMapとして扱うこともできます。

GameSparksでの設定

名前 Keyの型 データ型 値の型
PlayerMap String(固定) Map Player

C#コード

Dictionary<string, Player> playerMap

C#ではMapはDictionaryと表現します。
また、Keyの型はGameSpark上でStringに固定されているため、他の型を設定することはできません。
任意の文字列KeyとPlayerデータを関連付けて保持したい場合に便利でしょう。

Variant

Anyと同じく元々のデータ構造から外れますが、解説のためGameSparks上に設定しました。
Variantは少し特殊なデータ型です。公式の解説では、C言語のUnion型(共用体)に近いものと書かれています。

A variant data type. This shape is similar in nature to a C union data type.

GameSparksでの設定

Variant型のフィールドとして、以下の項目を設定

名前 データ型
aaa String
bbb Integer

C#コード

public sealed class test
{
    [JsonProperty]
    public string aaa { get; }
    [JsonProperty]
    public int bbb { get; }

    private test(
        string aaa = null,
        int bbb = null)
    {
        this.aaa = aaa;
        this.bbb = bbb;
    }

    public static test Fromaaa(string aaa) {
        return new test(aaa: aaa);
    }
    public static test Frombbb(int bbb) {
        return new test(bbb: bbb);
    }

    public override string ToString()
    {
        return string.Concat(
           $"{nameof(aaa)}: { aaa }", Environment.NewLine,
           $"{nameof(bbb)}: { bbb }");
    }
}

コンストラクタがprivate化されており、フィールドのどれか1つだけに値をセットしてインスタンス生成をすることを矯正されています。

こちらについては、私の方ではUnityでどのような用途に使用できるのか分かりませんでしたが、一般的にunion型は1つのメモリ領域を複数の型が共有するために使用するようです。

まとめ

GameSparks上で設定できる全てのデータ型について、C#コードを生成した際にどのようなコードになるかを見てきました。

基本的に、GameSparksでの設定内容がそのままC#コードを通してUnityに反映されるようになっています。
こういった情報を把握しておくことで、Unityでどのようなデータを取り扱うことになるか理解することができるでしょう。