GS2-Formation

パーティ・装備編成機能

所有しているリソースを組み合わせて1つの何かを編成するという仕様は一般的です。 複数のキャラクターを編成してパーティを作ったり、武器・防具といったアイテムを編成して装備するといったものです。

フォーム

キャラクターの装備機能を実現するにあたって、武器・兜・籠手・胴・脚・足といった複数のスロットを用意し 各スロットに適合するアイテムのみを装備できるように設定が可能です。

Form Model では、どのようなスロットが存在するのか、各スロットにはどのようなアイテムを編成できるのかをマスターデータとして定義します。 マスターデータを登録することでマイクロサービスで利用可能なデータや振る舞いを設定できます。

マスターデータの種類には以下があります。

  • MoldModel: Mold の保存枠構成
  • PropertyFormModel: PropertyForm のスロット構成

マスターデータの登録はマネージメントコンソールから登録する他、GitHubからデータを反映したり、GS2-Deployを使ってCIから登録するようなワークフローを組むことが可能です。

スクリプトトリガー

ネームスペースに updateMoldScript updateFormScript updatePropertyFormScript を設定すると、編成データの更新前後でカスタムスクリプトを実行できます。 スクリプトは同期・非同期の実行方式を選択でき、非同期では GS2-Script や Amazon EventBridge を介した外部連携にも対応します。

設定できる主なイベントトリガーとスクリプト設定名は以下の通りです。

  • updateMoldScript(完了通知: updateMoldDone): Mold 更新の前後
  • updateFormScript(完了通知: updateFormDone): Form 更新の前後
  • updatePropertyFormScript(完了通知: updatePropertyFormDone): PropertyForm 更新の前後

トランザクションアクション

GS2-Formation では以下のトランザクションアクションを提供しています。

  • 検証アクション: キャパシティ(保存数量上限)の検証
  • 消費アクション: キャパシティの減算
  • 入手アクション: キャパシティの加算、キャパシティの設定、編成内容(Form / PropertyForm)の設定、編成されているリソースに対する入手アクションの適用

「編成されているリソースに対する入手アクションの適用」を入手アクションとして利用することで、特定のパーティスロットに編成されているキャラクターに対して直接経験値を加算するといった処理が可能になります。また、「キャパシティの加算」を報酬として設定することで、特定のミッション達成時に編成枠を自動的に拡張するといった運用も可能です。

実装例

Mold での編成の永続化

Form の Slot には GS2-Inventory で管理する ItemSet / SimpleItem か、GS2-Dictionary で管理する Entry を登録できます。 それぞれ設定するためには、所有証明署名 を付加する必要があります。

所有証明署名 の取得方法については各サービスの説明を確認してください。

propertyType の種類

  • gs2_inventory – GS2-Inventory で管理する ItemSet を装着
  • gs2_simple_inventory – GS2-Inventory で管理する SimpleItem を装着
  • gs2_dictionary – GS2-Dictionary で管理する Entry を装着
    var result = await gs2.Formation.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Mold(
        moldName: "mold-0001"
    ).Form(
        index: 0
    ).SetFormAsync(
        slots: new [] {
            new Gs2.Unity.Gs2Formation.Model.EzSlotWithSignature
            {
                Name = "slot-0001",
                PropertyType = "gs2_dictionary",
                Body = "body",
                Signature = "signature",
            },
        },
        keyId: "key-0001"
    );
    const auto Domain = Gs2->Formation->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Mold(
        "mold-0001" // moldName
    )->Form(
        0 // index
    );
    const auto Future = Domain->SetForm(
        []
        {
            const auto v = MakeShared<TArray<TSharedPtr<Gs2::Formation::Model::FSlotWithSignature>>>();
            v->Add({'name': 'slot-0001', 'propertyType': 'gs2_dictionary', 'body': 'body', 'signature': 'signature'});
            return v;
        }(),
        "key-0001"
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
    const auto Result = Future2->GetTask().Result();

Mold の キャパシティの引き上げ

キャパシティ(保存数量上限)の引き上げはゲームエンジン用の SDK では処理できません。

GS2-Exchange で課金通貨との交換などの報酬としてキャパシティを引き上げるようにしてください。

Mold での編成内容の一覧を取得

    var items = await gs2.Formation.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Mold(
        moldName: "mold-0001"
    ).FormsAsync(
    ).ToListAsync();
    const auto Domain = Gs2->Formation->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Mold(
        "mold-0001" // moldName
    );
    const auto It = Domain->Forms(
    );
    TArray<Gs2::UE5::Formation::Model::FEzMoldPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

Mold での編成内容を取得

    var item = await gs2.Formation.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Mold(
        moldName: "mold-0001"
    ).Form(
        index: 0
    ).ModelAsync();
    const auto Domain = Gs2->Formation->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Mold(
        "mold-0001" // moldName
    )->Form(
        0 // index
    );
    const auto item = Domain.Model();

Property Form での編成の永続化

Form の Slot には GS2-Inventory で管理する ItemSet か、GS2-Dictionary で管理する Entry を登録できます。 それぞれ設定するためには、所有証明署名 を付加する必要があります。

所有証明署名 の取得方法については各サービスの説明を確認してください。

    var result = await gs2.Formation.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).PropertyForm(
        formModelName: "form-0001",
        propertyId: "property-0001"
    ).SetPropertyFormAsync(
        slots: new [] {
            new Gs2.Unity.Gs2Formation.Model.EzSlotWithSignature
            {
                Name = "slot-0001",
                PropertyType = "gs2_dictionary",
                Body = "body",
                Signature = "signature",
            },
        },
        keyId: "key-0001"
    );
    const auto Domain = Gs2->Formation->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->PropertyForm(
        "form-0001", // formModelName
        "property-0001" // propertyId
    );
    const auto Future = Domain->SetPropertyForm(
        []
        {
            const auto v = MakeShared<TArray<TSharedPtr<Gs2::Formation::Model::FSlotWithSignature>>>();
            v->Add({'name': 'slot-0001', 'propertyType': 'gs2_dictionary', 'body': 'body', 'signature': 'signature'});
            return v;
        }(),
        "key-0001"
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
    const auto Result = Future2->GetTask().Result();

Property Form での編成内容を取得

    var item = await gs2.Formation.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).PropertyForm(
        formModelName: "form-0001",
        propertyId: "property-0001"
    ).ModelAsync();
    const auto Domain = Gs2->Formation->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->PropertyForm(
        "form-0001", // formModelName
        "property-0001" // propertyId
    );
    const auto item = Domain.Model();

編成リソースの管理と注意事項

プロパティの指定方法

フォームのスロットに編成するプロパティはプロパティIDで指定します。 スロットモデルの設定でプロパティIDの正規表現を指定することで、そのスロットに編成できる値を限定することができます。

所有証明署名と編成後の注意

GS2-Inventory で管理するアイテムをスロットに設定する場合、GS2-Inventory が発行する署名付きアイテムセット(所有証明署名)を使って編成対象に指定できます。 署名付きアイテムセットが保証する「所有していること」は編成時点での保証であり、編成後にそのアイテムを売却・消費してもフォームのデータはそのままになります。 このため、GS2-Inventory でアイテムを消費・売却する場合は、事前に GS2-Formation の編成で使用されていないかをクライアントまたはスクリプトで確認する必要があります。

ItemSet.referenceOf による編成中アイテムの削除防止

スタンダードインベントリ(GS2-Inventory)を使用している場合は、ItemSetreferenceOf フィールドを利用することで、編成中のアイテムが誤って消費・削除されることを防ぐことができます。

フォームのスロットにアイテムをセットする際に、そのスロットを識別する文字列(GRN など)を ItemSet.referenceOf に設定します。 スタンダードインベントリのインベントリモデル設定には「referenceOf に値が存在する場合は消費を禁止する」オプションがあります。 このオプションを有効にすると、ItemSet.referenceOf が空でないアイテムは消費・売却できなくなるため、編成中のアイテムに対してシステムレベルで削除の制限をかけることができます。

詳細なリファレンス