NAV Navbar
Unity UE4 Cocos2d-x
 

Game Server Services

Game Server Services(GS2) とはモバイルゲーム開発に特化したバックエンドサーバサービス(BaaS)です。

ゲーム開発に必要なネットワーク機能をコンポーネント化してサービスとして提供します。 ゲーム内から必要な一部のコンポーネント単位で利用することができるよう設計されており、手軽に・手頃な価格で・高性能なサーバ機能を利用できます。

利用料金

一般的な利用料金

サービス内に特別な記載がない限り、以下の料金でサービスを利用できます。

無料枠

毎月2万円(100万API呼び出し相当) の無料枠が存在し、無料枠を超過するまではクレジットカードの登録なくご利用いただけます。 クレジットカードの登録がない状態で無料枠を使い切ると、サービスの提供が強制的に停止されます。

処理性能

GS2 はゲームサーバを性能を気にすることなく利用いただけるよう設計されています。 リリース前のベンチマークで秒間10万回のアクセスを処理できることを確認しています。

しかし、すべてのAPIを性能を気にする必要なく提供することは困難であり、 一部のAPIには性能を保証するラインを別途設けている場合があります。 個別の性能保証が定義されている場合は各コンポーネントで定義しています。

はじめかた

はじめに

この章では、Unity のプロジェクトを作成して GS2 の機能を利用するために必要な 1. GS2のウェブサイト側の手順と 2. Unity上のSDK組み込み

までをまとめています。 ネットワークを利用したアカウント管理やアイテム課金、プッシュ通知などをUnityのゲームに初めて組み込む人はじっくりと、他のサービスを触ったことがある人は飛ばしながら確認してください。

ウェブサイト上でGS2 アカウントの作成

まず、 GS2 のサイト からGS2開発用アカウントの登録を行ってください。 GS2開発用アカウント登録は、ゲームの開発管理者が行います。

現時点でGS2のサイト側での各種設定は、1プロジェクトあたり1開発者という制限があります。(この制限は将来撤廃予定です) なので開発管理者は、あなたのチームの中で一番メインのプログラマを充てることが望ましいです。

ウェブサイト上でGS2プロジェクトの作成

GS2 のアカウントを作成後、最初にするべきことは GS2サービス上で対象となるゲーム(以下 プロジェクト と呼びます)を作成することです。

プロジェクト とはゲームタイトルと 1:1 となる概念です。 GS2 のあらゆるリソースは プロジェクト に関連付けて保持され、他の プロジェクト のデータには基本的に干渉できません。

もちろん、新規のゲームタイトルを作り始める時に最初にGS2のプロジェクト設定をしても良いですし、すでに開発中のタイトルにGS2を追加することもできます。

プロジェクト名は 英数字大文字小文字とハイフン、アンダーバー で構成することが出来ます。(例:MySuperCool_Stg_Game-001) 日本語のプロジェクト名は設定できません。

クレデンシャルの発行

GS2 の API を利用するには認証情報(以下 クレデンシャル 、と呼びます)が必要です。

クレデンシャルユーザ の認証に用いる情報で、 GS2 のサービス利用に関わる権限はこの ユーザ 単位で管理されます。 権限の詳細は セキュリティポリシー に定義され、これを ユーザ に割り当てて使用します。

権限管理にまつわるリソースは、以下の階層構造で管理されています。

セキュリティポリシーの例

{
  "Version": "2016-04-01",
  "Statements": [
    {
      "Effect": "Allow",
      "Actions": [
        "Gs2Account:CreateAccount"
      ],
      "Resources": [
        "*"
      ]
    }
  ]
}

個々の API の呼出可否は セキュリティポリシー に定義されます。 セキュリティポリシー は JSON形式で記述します。

GS2 では開発者向けに2種類の セキュリティポリシー を事前に定義して提供しています。 あらゆるAPIを呼び出せる AdministratorAccess と、ゲーム内に組み込んで使用する想定の ApplicationAccess です。 後者はチート行為につながるような操作ができるAPIを呼び出すことができないよう設定されています。

セキュリティポリシー の詳細は別途 セキュリティポリシーのフォーマット を参照してください。

GS2-Deploy による設定

クレデンシャルの発行に必要な設定は GS2-Identifier の管理画面からも個別に登録できますが、 GS2-Deploy を利用した設定が便利です。

GS2-Deploy は GS2 上で継続的デリバリーを実現するためのサービスです。 GS2に対して設定したい項目を テンプレート として yaml 形式で記述し、 GS2-Deploy に登録することで設定を反映できます。

その際に作成することになるエンティティが スタック です。 テンプレート を指定した スタック を作成することで、 テンプレート に記述した設定が反映されます。 また、 スタック に対して変更した テンプレート を再適用した場合も、変更点を自動的に検出し、作成が必要なリソースは作成、更新が必要なリソースは更新、削除が必要なリソースは削除します。

これによって開発者は、いらなくなったリソースを削除するなどのオペレーションを行うことなく、求める状況を宣言的に テンプレート に記述するだけでよくなります。

では、さっそく テンプレート を使って GS2-Deploy でスタックを作成してみましょう。

クレデンシャルを発行する GS2-Deploy テンプレートの例

GS2TemplateFormatVersion: "2019-05-01"
Description: GS2 SDK identifier template Version 2019-07-10

Globals:
  Alias:
    ApplicationUserName: application

Resources:
  IdentifierApplicationUser:
    Type: GS2::Identifier::User
    Properties:
      Name: ${ApplicationUserName}

  IdentifierApplicationUserAttachPolicy:
    Type: GS2::Identifier::AttachSecurityPolicy
    Properties:
      UserName: ${ApplicationUserName}
      SecurityPolicyId: grn:gs2::system:identifier:securityPolicy:ApplicationAccess
    DependsOn:
      - IdentifierApplicationUser

  IdentifierApplicationIdentifier:
    Type: GS2::Identifier::Identifier
    Properties:
      UserName: ${ApplicationUserName}
    DependsOn:
      - IdentifierApplicationUser

Outputs:
  ApplicationClientId: !GetAttr IdentifierApplicationIdentifier.Item.ClientId
  ApplicationClientSecret: !GetAttr IdentifierApplicationIdentifier.ClientSecret

隣に示したのは、 ApplicationAccess 権限を設定したユーザを作成する テンプレート のサンプルです。 ApplicationUserName: application の部分を変更すると、GS2-Identifier に作成されるユーザ名を変更できます。

テンプレートをダウンロード

この テンプレート をファイルに保存し、 GS2-Deploy の スタックの新規作成テンプレートデータ に指定してスタックを作成します。

作成したスタックに登録された設定の反映状態は、スタックの 実行状態 の項目で確認できます。 数秒〜数分後には CREATE_COMPLETE になり、 クレデンシャル の設定が完了しているはずです。

反映完了後に アウトプット タブを選択すると、クレデンシャルの クライアントIDクライアントシークレット が表示されます。 この値は後ほど SDK に設定しますので、確認できる場所を覚えておいてください。

GS2-SDK for Game Engine のダウンロードと導入

GS2-SDK for Game Engine から GS2-SDK for Unity もしくは GS2-SDK for Game Engine を入手できます。 お使いの環境にあったパッケージをダウンロードし、示された手順に従って導入してください。

これでゲームエンジンから GS2 を利用するための準備が整いました。

アカウント管理機能の実装サンプル

テンプレート

GS2-Account の利用を開始するためのテンプレート例

GS2TemplateFormatVersion: "2019-05-01"
Description: GS2-Account initialize template Version 2010-06-26

Globals:
  Alias:
    AccountNamespaceName: game-0001
    KeyNamespaceAccountAuthentication: account-encryption-key-namespace
    KeyAccountAuthentication: account-encryption-key

Resources:
  KeyNamespaceAccountAuthentication:
    Type: GS2::Key::Namespace
    Properties:
      Name: ${KeyNamespaceAccountAuthentication}

  KeyAccountAuthentication:
    Type: GS2::Key::Key
    Properties:
      NamespaceName: ${KeyNamespaceAccountAuthentication}
      Name: ${KeyAccountAuthentication}
    DependsOn:
      - KeyNamespaceAccountAuthentication

  AccountNamespace:
    Type: GS2::Account::Namespace
    Properties:
      Name: ${AccountNamespaceName}

Outputs:
  AccountNamespaceName: !GetAttr AccountNamespace.Item.Name
  KeyAccountAuthenticationKeyId: !GetAttr KeyAccountAuthentication.Item.KeyId

まず、サンプルコードから利用する GS2-Account の設定を行います。 GS2-Deploy の スタックの新規作成 で、以下のテンプレートを テンプレートデータ に指定してスタックを作成します。

テンプレートをダウンロード

サンプルコード

サンプルコード

using System;
using System.Collections;
using System.Collections.Generic;
using Gs2.Core;
using Gs2.Unity.Gs2Account.Model;
using Gs2.Unity.Gs2Account.Result;
using Gs2.Unity.Util;
using UnityEngine;

public class Gs2AccountSample2 : MonoBehaviour
{
    // GS2-Identifier で発行したクライアントID
    public string clientId;

    // GS2-Identifier で発行したクライアントシークレット
    public string clientSecret;

    // アカウントを作成する GS2-Account のネームスペース名
    public string accountNamespaceName;

    // アカウントの認証結果に付与する署名を計算するのに使用する暗号鍵
    public string accountEncryptionKeyId;

    void Start()
    {
        StartCoroutine(CreateAndLoginAction());
    }

    public IEnumerator CreateAndLoginAction()
    {
        // GS2 SDK のクライアントを初期化

        Debug.Log("GS2 SDK のクライアントを初期化");

        var profile = new Gs2.Unity.Util.Profile(
            clientId: clientId,
            clientSecret: clientSecret,
            reopener: new Gs2BasicReopener()
        );

        {
            AsyncResult<object> asyncResult = null;

            var current = profile.Initialize(
                r => { asyncResult = r; }
            );

            yield return current;

            // コルーチンの実行が終了した時点で、コールバックは必ず呼ばれています

            // クライアントの初期化に失敗した場合は終了
            if (asyncResult.Error != null)
            {
                OnError(asyncResult.Error);
                yield break;
            }
        }

        var gs2 = new Gs2.Unity.Client(profile);

        // アカウントを新規作成

        Debug.Log("アカウントを新規作成");

        EzAccount account = null;

        {
            AsyncResult<EzCreateResult> asyncResult = null;

            var current = gs2.Account.Create(
                r => { asyncResult = r; },
                accountNamespaceName
            );

            yield return current;

            // コルーチンの実行が終了した時点で、コールバックは必ず呼ばれています

            // アカウントが作成できなかった場合は終了
            if (asyncResult.Error != null)
            {
                OnError(asyncResult.Error);
                yield break;
            }

            // 作成したアカウント情報を取得
            account = asyncResult.Result.Item;
        }

        // ログイン

        Debug.Log("ログイン");

        GameSession session = null;

        {
            AsyncResult<GameSession> asyncResult = null;
            var current = profile.Login(
               authenticator: new Gs2AccountAuthenticator(
                   session: profile.Gs2Session,
                   accountNamespaceName: accountNamespaceName,
                   keyId: accountEncryptionKeyId,
                   userId: account.UserId,
                   password: account.Password
               ),
               r => { asyncResult = r; }
           );

            yield return current;

            // コルーチンの実行が終了した時点で、コールバックは必ず呼ばれています

            // ゲームセッションオブジェクトが作成できなかった場合は終了
            if (asyncResult.Error != null)
            {
                OnError(asyncResult.Error);
                yield break;
            }

            // ログイン状態を表すゲームセッションオブジェクトを取得
            session = asyncResult.Result;
        }

        // 引き継ぎ情報の一覧を取得

        Debug.Log("引き継ぎ情報の一覧を取得");

        {
            AsyncResult<EzListTakeOverSettingsResult> asyncResult = null;

            var current = gs2.Account.ListTakeOverSettings(
                r => { asyncResult = r; },
                session,
                accountNamespaceName
            );

            yield return current;

            // コルーチンの実行が終了した時点で、コールバックは必ず呼ばれています

            // APIの呼び出しが完了したら通知されるコールバック
            if (asyncResult.Error != null)
            {
                OnError(asyncResult.Error);
                yield break;
            }

            List<EzTakeOver> items = asyncResult.Result.Items;
            foreach (var item in items)
            {
                // 引き継ぎに関する情報が取得される
            }
        }

        // GS2 SDK の終了処理

        Debug.Log("GS2 SDK の終了処理");

        {
            // ゲームを終了するときなどに呼び出してください。
            // 頻繁に呼び出すことは想定していません。
            var current = profile.Finalize();

            yield return current;
        }
    }

    private void OnError(Exception e)
    {
        Debug.Log(e.ToString());
    }
}
// MyActor.hpp

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include <gs2/ez/Gs2Ez.hpp>
#include <memory>
#include "MyActor.generated.h"

UCLASS()
class GS2SDKAPP_API AMyActor : public AActor
{
    GENERATED_BODY()

public: 
    // Sets default values for this actor's properties
    AMyActor();

    // GS2-Identifier で発行したクライアントID
    UPROPERTY(EditAnywhere, Category = "GS2")
    FString ClientId;

    // GS2-Identifier で発行したクライアントシークレット
    UPROPERTY(EditAnywhere, Category = "GS2")
    FString ClientSecret;

    // アカウントを作成する GS2-Account のネームスペース名
    UPROPERTY(EditAnywhere, Category = "GS2")
    FString AccountNamespaceName;

    // アカウントの認証結果に付与する署名を計算するのに使用する暗号鍵
    UPROPERTY(EditAnywhere, Category = "GS2")
    FString AccountEncryptionKeyId;

private:
    std::shared_ptr<gs2::ez::Profile> ProfilePtr;
    std::shared_ptr<gs2::ez::Client> ClientPtr;

    gs2::ez::GameSession GameSession;
    gs2::ez::account::EzAccount EzAccount;

    void ProfileInitialize();
    void AccountCreate();
    void ProfileLogin();
    void AccountListTakeOverSettings();
    void ProfileFinalize();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public: 
    // Called every frame
    virtual void Tick(float DeltaTime) override;
};
// MyActor.cpp

#include "MyActor.h"
#include "Engine.h"

// Sets default values
AMyActor::AMyActor()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
    Super::BeginPlay();

    ProfileInitialize();
}

void AMyActor::ProfileInitialize()
{
    // GS2 SDK のクライアントを初期化

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "profile.initialize");

    ProfilePtr = std::make_shared<gs2::ez::Profile>(
        TCHAR_TO_ANSI(*ClientId),
        TCHAR_TO_ANSI(*ClientSecret),
        gs2::ez::Gs2BasicReopener()
        );

    ClientPtr = std::make_shared<gs2::ez::Client>(
        *ProfilePtr
        );

    ProfilePtr->initialize(
        [this](gs2::ez::Profile::AsyncInitializeResult r)
        {
            if (r.getError())
            {
                // クライアントの初期化に失敗した場合は終了
                GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "profile.initialize failed.");
            }
            else
            {
                AccountCreate();
            }
        }
    );
}

void AMyActor::AccountCreate()
{
    // アカウントを新規作成

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "account.create");

    ClientPtr->account.create(
        [this](gs2::ez::account::AsyncEzCreateResult r)
        {
            if (r.getError())
            {
                // アカウントが作成できなかった場合は終了
                GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "account.create failed.");
            }
            else
            {
                // 作成したアカウント情報を取得
                EzAccount = r.getResult()->getItem();

                ProfileLogin();
            }
        },
        TCHAR_TO_ANSI(*AccountNamespaceName)
    );
}

void AMyActor::ProfileLogin()
{
    // ログイン

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "profile.login");

    ProfilePtr->login(
        [this](gs2::ez::Profile::AsyncLoginResult r)
        {
            if (r.getError())
            {
                // ゲームセッションオブジェクトが作成できなかった場合は終了
                GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "profile.login failed.");
            }
            else
            {
                // ログイン状態を表すゲームセッションオブジェクトを取得
                GameSession = *r.getResult();

                AccountListTakeOverSettings();
            }
        },
        gs2::ez::Gs2AccountAuthenticator(
            ProfilePtr->getGs2Session(),
            TCHAR_TO_ANSI(*AccountNamespaceName),
            TCHAR_TO_ANSI(*AccountEncryptionKeyId),
            EzAccount.getUserId(),
            EzAccount.getPassword()
        )
    );
}

void AMyActor::AccountListTakeOverSettings()
{
    // 引き継ぎ情報の一覧を取得

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "account.listTakeOverSettings");

    ClientPtr->account.listTakeOverSettings(
        [this](gs2::ez::account::AsyncEzListTakeOverSettingsResult r)
        {
            if (r.getError())
            {
                // 引き継ぎ情報の一覧が取得できなかった場合は終了
                GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "account.listTakeOverSettings failed.");
            }
            else
            {
                auto& items = r.getResult()->getItems();
                for (auto i = 0; i < items.getCount(); ++i)
                {
                    // 引き継ぎに関する情報が取得される
                }

                ProfileFinalize();
            }
        },
        GameSession,
        TCHAR_TO_ANSI(*AccountNamespaceName)
    );
}

void AMyActor::ProfileFinalize()
{
    // GS2 SDK の終了処理

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "profile.finalize");

    // ゲームを終了するときなどに呼び出してください。
    // 頻繁に呼び出すことは想定していません。
    ProfilePtr->finalize(
        [this]()
        {
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, "successfull.");
        }
    );
}

// Called every frame
void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}
// HelloWorldScene.hpp

/****************************************************************************
 このファイルは Cocos2d-x のパッケージに含まれる初期コードに
 Game Server Services 株式会社が改変を加えたものです。
 以下の表示は、その初期コードに関するものです。
/****************************************************************************
/****************************************************************************
 Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.

 http://www.cocos2d-x.org

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#include <gs2/ez/Gs2Ez.hpp>

class HelloWorld : public cocos2d::Scene
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();

    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);

    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);

private:
    std::shared_ptr<gs2::ez::Profile> _pProfile;
    std::shared_ptr<gs2::ez::Client> _pClient;

    gs2::ez::GameSession _gameSession;
    gs2::ez::account::EzAccount _ezAccount;

    void profileInitialize();
    void accountCreate();
    void profileLogin();
    void accountListTakeOverSettings();
    void profileFinalize();

    void myLog(const char message[]);
};

#endif // __HELLOWORLD_SCENE_H__
// HelloWorldScene.cpp

/****************************************************************************
 このファイルは Cocos2d-x のパッケージに含まれる初期コードに
 Game Server Services 株式会社が改変を加えたものです。
 以下の表示は、その初期コードに関するものです。
/****************************************************************************
/****************************************************************************
 Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.

 http://www.cocos2d-x.org

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/

#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    return HelloWorld::create();
}

// Print useful error message instead of segfaulting when files are not there.
static void problemLoading(const char* filename)
{
    printf("Error while loading: %s\n", filename);
    printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in HelloWorldScene.cpp\n");
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Scene::init() )
    {
        return false;
    }

    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));

    if (closeItem == nullptr ||
        closeItem->getContentSize().width <= 0 ||
        closeItem->getContentSize().height <= 0)
    {
        problemLoading("'CloseNormal.png' and 'CloseSelected.png'");
    }
    else
    {
        float x = origin.x + visibleSize.width - closeItem->getContentSize().width/2;
        float y = origin.y + closeItem->getContentSize().height/2;
        closeItem->setPosition(Vec2(x,y));
    }

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    /////////////////////////////
    // 3. add your codes below...

    // add a label shows "Hello World"
    // create and initialize a label

    auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
    if (label == nullptr)
    {
        problemLoading("'fonts/Marker Felt.ttf'");
    }
    else
    {
        // position the label on the center of the screen
        label->setPosition(Vec2(origin.x + visibleSize.width/2,
                                origin.y + visibleSize.height - label->getContentSize().height));

        // add the label as a child to this layer
        this->addChild(label, 1);
    }

    // add "HelloWorld" splash screen"
    auto sprite = Sprite::create("HelloWorld.png");
    if (sprite == nullptr)
    {
        problemLoading("'HelloWorld.png'");
    }
    else
    {
        // position the sprite on the center of the screen
        sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

        // add the sprite as a child to this layer
        this->addChild(sprite, 0);
    }

    profileInitialize();

    return true;
}


void HelloWorld::menuCloseCallback(Ref* pSender)
{
    //Close the cocos2d-x game scene and quit the application
    Director::getInstance()->end();

    /*To navigate back to native iOS screen(if present) without quitting the application  ,do not use Director::getInstance()->end() as given above,instead trigger a custom event created in RootViewController.mm as below*/

    //EventCustom customEndEvent("game_scene_close_event");
    //_eventDispatcher->dispatchEvent(&customEndEvent);


}

namespace
{
    const char ClientId[] = "事前に発行されたクライアント ID を記載してください";
    const char ClientSecret[] = "事前に発行されたクライアントシークレットを記載してください";
    const char AccountNamespaceName[] = "事前に作成したネームスペース名を記載してください";
    const char AccountEncryptionKeyId[] = "事前に作成した暗号化キーの ID を記載してください";
}

void HelloWorld::profileInitialize()
{
    // GS2 SDK のクライアントを初期化

    myLog("profile.initialize");

    _pProfile = std::make_shared<gs2::ez::Profile>(
        ClientId,
        ClientSecret,
        gs2::ez::Gs2BasicReopener()
    );

    _pClient = std::make_shared<gs2::ez::Client>(
        *_pProfile
    );

    _pProfile->initialize(
        [this](gs2::ez::Profile::AsyncInitializeResult r)
        {
            if (r.getError())
            {
                // クライアントの初期化に失敗した場合は終了
                myLog("profile.initialize failed.");
            }
            else
            {
                accountCreate();
            }
        }
    );
}

void HelloWorld::accountCreate()
{
    // アカウントを新規作成

    myLog("account.create");

    _pClient->account.create(
        [this](gs2::ez::account::AsyncEzCreateResult r)
        {
            if (r.getError())
            {
                // アカウントが作成できなかった場合は終了
                myLog("account.create failed.");
            }
            else
            {
                // 作成したアカウント情報を取得
                _ezAccount = r.getResult()->getItem();

                profileLogin();
            }
        },
        AccountNamespaceName
    );
}

void HelloWorld::profileLogin()
{
    // ログイン

    myLog("profile.login");

    _pProfile->login(
        [this](gs2::ez::Profile::AsyncLoginResult r)
        {
            if (r.getError())
            {
                // ゲームセッションオブジェクトが作成できなかった場合は終了
                myLog("profile.login failed.");
            }
            else
            {
                // ログイン状態を表すゲームセッションオブジェクトを取得
                _gameSession = *r.getResult();

                accountListTakeOverSettings();
            }
        },
        gs2::ez::Gs2AccountAuthenticator(
                _pProfile->getGs2Session(),
                AccountNamespaceName,
                AccountEncryptionKeyId,
                _ezAccount.getUserId(),
                _ezAccount.getPassword()
        )
    );
}

void HelloWorld::accountListTakeOverSettings()
{
    // 引き継ぎ情報の一覧を取得

    myLog("account.listTakeOverSettings");

    _pClient->account.listTakeOverSettings(
        [this](gs2::ez::account::AsyncEzListTakeOverSettingsResult r)
        {
            if (r.getError())
            {
                // 引き継ぎ情報の一覧が取得できなかった場合は終了
                myLog("account.listTakeOverSettings failed.");
            }
            else
            {
                auto& items = r.getResult()->getItems();
                for (auto i = 0; i < items.getCount(); ++i)
                {
                    // 引き継ぎに関する情報が取得される
                }

                profileFinalize();
            }
        },
        _gameSession,
        AccountNamespaceName
    );
}

void HelloWorld::profileFinalize()
{
    // GS2 SDK の終了処理

    myLog("profile.finalize");

    // ゲームを終了するときなどに呼び出してください。
    // 頻繁に呼び出すことは想定していません。
    _pProfile->finalize(
        [this]()
        {
            myLog("successfull.");
        }
    );
}

void HelloWorld::myLog(const char message[])
{
    auto visibleSize = Director::getInstance()->getVisibleSize();
    auto origin = Director::getInstance()->getVisibleOrigin();

    static auto y = origin.y + visibleSize.height;

    auto label = Label::createWithTTF(message, "fonts/Marker Felt.ttf", 12);
    if (label != nullptr)
    {
        y -= label->getContentSize().height;
        label->setPosition(Vec2(origin.x + label->getContentSize().width/2, y));
        this->addChild(label, 1);
    }
}

まずはゲームオブジェクト全体のコードサンプルをコード欄に示します。

解説

SDKの初期化

SDKの初期化

var profile = new Gs2.Unity.Util.Profile(
    clientId: clientId,
    clientSecret: clientSecret,
    reopener: new Gs2BasicReopener()
);

{
    AsyncResult<object> asyncResult = null;

    var current = profile.Initialize(
        r => { asyncResult = r; }
    );

    yield return current;

    // コルーチンの実行が終了した時点で、コールバックは必ず呼ばれています

    // クライアントの初期化に失敗した場合は終了
    if (asyncResult.Error != null)
    {
        OnError(asyncResult.Error);
        yield break;
    }
}

var gs2 = new Gs2.Unity.Client(profile);
ProfilePtr = std::make_shared<gs2::ez::Profile>(
    TCHAR_TO_ANSI(*ClientId),
    TCHAR_TO_ANSI(*ClientSecret),
    gs2::ez::Gs2BasicReopener()
);

ClientPtr = std::make_shared<gs2::ez::Client>(
    *ProfilePtr
);

ProfilePtr->initialize(
    [this](gs2::ez::Profile::AsyncInitializeResult r)
    {
        if (r.getError())
        {
            // クライアントの初期化に失敗した場合は終了
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "profile.initialize failed.");
        }
        else
        {
            AccountCreate();
        }
    }
);
_pProfile = std::make_shared<gs2::ez::Profile>(
    ClientId,
    ClientSecret,
    gs2::ez::Gs2BasicReopener()
);

_pClient = std::make_shared<gs2::ez::Client>(
    *_pProfile
);

_pProfile->initialize(
    [this](gs2::ez::Profile::AsyncInitializeResult r)
    {
        if (r.getError())
        {
            // クライアントの初期化に失敗した場合は終了
            myLog("profile.initialize failed.");
        }
        else
        {
            accountCreate();
        }
    }
);

こちらは GS2 にアクセスするための設定をしています。 clientId clientSecret にはクレデンシャルを設定します。 具体的な設定値は、先程 GS2-Deploy のクレデンシャルを作成するテンプレートを使ってスタックを作成し、 ステータスが CREATE_COMPLETE になっていることを確認した上で、アウトプットタブで確認できる ApplicationClientId ApplicationClientSecret の内容を設定してください。

Account Namespace Name KeyAccountAuthenticationKeyId も同様にアカウント管理導入テンプレートのアウトプットタブの内容を設定してください。

reopener にはアクセストークンをリフレッシュするためのハンドラーを設定します。 特にこだわりがなければ Gs2BasicReopener を設定しておくことで、GS2 が推奨するリフレッシュロジックが適用されます。

アカウントの作成

アカウントの作成

EzAccount account = null;

{
    AsyncResult<EzCreateResult> asyncResult = null;

    var current = gs2.Account.Create(
        r => { asyncResult = r; },
        accountNamespaceName
    );

    yield return current;

    // コルーチンの実行が終了した時点で、コールバックは必ず呼ばれています

    // アカウントが作成できなかった場合は終了
    if (asyncResult.Error != null)
    {
        OnError(asyncResult.Error);
        yield break;
    }

    // 作成したアカウント情報を取得
    account = asyncResult.Result.Item;
}
ClientPtr->account.create(
    [this](gs2::ez::account::AsyncEzCreateResult r)
    {
        if (r.getError())
        {
            // アカウントが作成できなかった場合は終了
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "account.create failed.");
        }
        else
        {
            // 作成したアカウント情報を取得
            EzAccount = r.getResult()->getItem();

            ProfileLogin();
        }
    },
    TCHAR_TO_ANSI(*AccountNamespaceName)
);
_pClient->account.create(
    [this](gs2::ez::account::AsyncEzCreateResult r)
    {
        if (r.getError())
        {
            // アカウントが作成できなかった場合は終了
            myLog("account.create failed.");
        }
        else
        {
            // 作成したアカウント情報を取得
            _ezAccount = r.getResult()->getItem();

            profileLogin();
        }
    },
    AccountNamespaceName
);

ここではゲームプレイヤーを識別するためのアカウント情報の作成を行っています。 accountNamespaceName にはアカウント情報を記録するネームスペースを指定します。

テンプレートの アウトプット で確認できる AccountNamespaceName の値を設定します。

Create メソッドのコールバックは、アカウントの作成処理が完了したときに呼び出されます。 エラーが発生した場合は r.Error に例外オブジェクトが格納されており、成功したときには r.Result に結果が格納されています。

なお、このアカウントの作成は本来、アプリケーションの初回起動時に1回だけ行うべきものです。 実際のアプリケーションではこの応答の内容をローカルストレージに保存し、2回目以降はストレージから取得した値を使用してください。

ログイン

ログイン

GameSession session = null;

{
    AsyncResult<GameSession> asyncResult = null;

    var current = profile.Login(
        authenticator: new Gs2AccountAuthenticator(
            session: profile.Gs2Session,
            accountNamespaceName: accountNamespaceName,
            keyId: accountEncryptionKeyId,
            userId: account.UserId,
            password: account.Password
        ),
        r => { asyncResult = r; }
    );

    yield return current;

    // コルーチンの実行が終了した時点で、コールバックは必ず呼ばれています

    // ゲームセッションオブジェクトが作成できなかった場合は終了
    if (asyncResult.Error != null)
    {
        OnError(asyncResult.Error);
        yield break;
    }

    // ログイン状態を表すゲームセッションオブジェクトを取得
    session = asyncResult.Result;
}
ProfilePtr->login(
    [this](gs2::ez::Profile::AsyncLoginResult r)
    {
        if (r.getError())
        {
            // ゲームセッションオブジェクトが作成できなかった場合は終了
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "profile.login failed.");
        }
        else
        {
            // ログイン状態を表すゲームセッションオブジェクトを取得
            GameSession = *r.getResult();

            AccountListTakeOverSettings();
        }
    },
    gs2::ez::Gs2AccountAuthenticator(
        ProfilePtr->getGs2Session(),
        TCHAR_TO_ANSI(*AccountNamespaceName),
        TCHAR_TO_ANSI(*AccountEncryptionKeyId),
        EzAccount.getUserId(),
        EzAccount.getPassword()
    )
);
_pProfile->login(
    [this](gs2::ez::Profile::AsyncLoginResult r)
    {
        if (r.getError())
        {
            // ゲームセッションオブジェクトが作成できなかった場合は終了
            myLog("profile.login failed.");
        }
        else
        {
            // ログイン状態を表すゲームセッションオブジェクトを取得
            _gameSession = *r.getResult();

            accountListTakeOverSettings();
        }
    },
    gs2::ez::Gs2AccountAuthenticator(
            _pProfile->getGs2Session(),
            AccountNamespaceName,
            AccountEncryptionKeyId,
            _ezAccount.getUserId(),
            _ezAccount.getPassword()
    )
);

つづいて GS2 へのログイン処理です。 accountNamespaceName には作成したアカウントが存在するネームスペースを指定し、 keyId にはアカウントの認証結果に付与する署名を計算するのに使用する暗号鍵を指定します。 更に、 userId password には先程作成したアカウントのユーザIDとパスワードを指定します。

ログイン処理が完了するとコールバックが呼び出され、先ほどと同じように エラーが発生した場合は r.Error に例外オブジェクトが格納されており、成功したときには r.Result に結果が格納されています。

r.Result にはログイン状態を表現するための GameSession オブジェクトが返ってきます。 以降 GS2 の API を呼び出すときにログイン状態でなければ呼び出せないAPIの引数には GameSession を渡して使用することとなります。

ログイン状態でなければ呼び出せないAPIの呼び出し

ログイン状態でなければ呼び出せないAPIの呼び出し

{
    AsyncResult<EzListTakeOverSettingsResult> asyncResult = null;

    var current = gs2.Account.ListTakeOverSettings(
        r => { asyncResult = r; },
        session,
        accountNamespaceName
    );

    yield return current;

    // コルーチンの実行が終了した時点で、コールバックは必ず呼ばれています

    // APIの呼び出しが完了したら通知されるコールバック
    if (asyncResult.Error != null)
    {
        OnError(asyncResult.Error);
        yield break;
    }

    List<EzTakeOver> items = asyncResult.Result.Items;
    foreach (var item in items)
    {
        // 引き継ぎに関する情報が取得される
    }
}
ClientPtr->account.listTakeOverSettings(
    [this](gs2::ez::account::AsyncEzListTakeOverSettingsResult r)
    {
        if (r.getError())
        {
            // 引き継ぎ情報の一覧が取得できなかった場合は終了
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "account.listTakeOverSettings failed.");
        }
        else
        {
            auto& items = r.getResult()->getItems();
            for (auto i = 0; i < items.getCount(); ++i)
            {
                // 引き継ぎに関する情報が取得される
            }

            ProfileFinalize();
        }
    },
    GameSession,
    TCHAR_TO_ANSI(*AccountNamespaceName)
);
_pClient->account.listTakeOverSettings(
    [this](gs2::ez::account::AsyncEzListTakeOverSettingsResult r)
    {
        if (r.getError())
        {
            // 引き継ぎ情報の一覧が取得できなかった場合は終了
            myLog("account.listTakeOverSettings failed.");
        }
        else
        {
            auto& items = r.getResult()->getItems();
            for (auto i = 0; i < items.getCount(); ++i)
            {
                // 引き継ぎに関する情報が取得される
            }

            profileFinalize();
        }
    },
    _gameSession,
    AccountNamespaceName
);

ログイン状態でなければ使用できないAPIの例として、引き継ぎ設定の一覧取得APIを示しています。 session に GameSession を渡して、ログイン中のゲームプレイヤーが設定している引き継ぎ設定の一覧を取得できます。

マスターデータの管理

用語の定義

用語 意味
モデルマスター 管理画面から更新するゲームプレイヤーごとに変わらないデータ
モデル ゲーム内から使用するゲームプレイヤーごとに変わらないデータ
プロパティ モデルをもとに作成したゲームプレイヤーごとに異なるデータ

アカウント管理では出てきませんでしたが、ゲームの構成要素の中には所持品やクエストなどゲームプレイヤーごとには変わらないデータがあります。 このようなデータをGS2では モデル と呼んでいます。そして、モデルをもとにゲームプレイヤーの所持するデータになったものを プロパティ と呼んでいます。

一般的にマスターデータと捉えられるのは GS2 においては モデル にあたるかとおもいますが、GS2 においては モデルマスター という概念が存在します。 モデル モデルマスター の違いは、データの内容としては基本的には一緒ですが、実際にゲームから使われる状態にあるかどうか。という違いがあります。

つまり、GS2 の管理画面上で編集できるデータは モデルマスター であり、それを実際にゲーム内から使用できる状態に変換すると モデル に変わります。 この変換工程が必要な理由は、 モデルマスター に対する変更を一括してゲーム内に反映するためです。 この工程がない場合、管理画面で少しずつデータを更新していく過程でゲーム内に半端なデータが反映されてしまうことになります。

マスターデータの作成

GS2-Deploy のテンプレートでのマスターデータ管理例

GS2TemplateFormatVersion: "2019-05-01"
Description: GS2 master data template Version 2010-06-26

Globals:
  Alias:
    NamespaceName: inventory

Resources:
  Namespace:
    Type: GS2::Inventory::Namespace
    Properties:
      Name: ${NamespaceName}

  NamespaceSettings:
    Type: GS2::Inventory::CurrentItemModelMaster
    Properties:
      NamespaceName: ${NamespaceName}
      Settings:  # ここから下のデータが本来はJSONで指定する部分ですが、yaml として記述して反映できます
        version: 2019-02-05
        inventoryModels:
          - name: item
            metadata: ITEM
            initialCapacity: 40
            maxCapacity: 60
            itemModels:
              - name: item-0001
                metadata: ITEM_0001
                maxCount: 99
                sortValue: 1
              - name: item-0002
                metadata: ITEM_0002
                maxCount: 99
                sortValue: 2
              - name: item-0003
                metadata: ITEM_0003
                maxCount: 99
                sortValue: 3
          - name: character
            metadata: CHARACTER
            initialCapacity: 30
            maxCapacity: 50
            itemModels:
             - name: character-0001
               metadata: CHARACTER_0001
               maxCount: 1
               sortValue: 1

    DependsOn:
      - Namespace

この変換工程は、すべての モデルマスター を一旦 JSON 形式のファイルにエクスポートし、そのJSONファイルをアップロードすることで一括して モデル として反映する仕組みになっています。 GS2 の管理画面で モデルマスター を操作して JSON 形式のファイルにエクスポートして利用しても構いませんが、Excel や独自の管理ツールを作成し GS2 上に モデルマスター を一切登録せずに モデル にデータを反映することもできます。

また、GS2-Deploy のテンプレート内でマスターデータを管理することもできます。この場合 git などの バージョン管理ツール で取り扱いやすくなりますので、こちらも検討してみてください。

運営上都合のいい方法でマスターデータを管理してください。

GS2サービスの拡張

GS2-Script を使って GS2-Inventory にアイテムを付与する例

-- 引数からモデル情報を取得
namespace_name = args['namespaceName']
user_id = args['userId']
inventory_name = args['inventoryName']
item_name = args['itemName']
acquire_count = args['acquireCount']
expires_at = args['expiresAt']

inventory_client = gs2('inventory')

-- リクエストを発行
result = inventory_client.acquire_item_set_by_user_id({
    namespace_name=namespace_name,
    user_id=user_id,
    inventory_name=inventory_name,
    item_name=item_name,
    acquire_count=acquire_count,
    expires_at=expires_at,
})

-- レスポンスを応答
if(result['isError']) then
    fail(result['statusCode'], result['errorMessage'])
end

result = result['result']

GS2 は一般的な用途においては標準機能でゲームシステムを構築できるよう設計しています。 しかし、ゲーム内のユースケースによっては GS2 の標準機能では実現できないことが出てくるかもしれません。

そのようなケースに対応できるよう GS2 では Lua によるスクリプトによる機能拡張に対応しています。 GS2-Account であれば、アカウントの新規作成時 アカウントの認証時 引き継ぎ設定登録時 引き継ぎ実行時 といったタイミングで GS2-Script に登録したスクリプトを実行できるようにしています。 これによって、 引き継ぎ設定をしたときに報酬をプレゼントボックスに届けたい というようなニーズに応えられるようにしています。 何度も引き継ぎ登録して報酬を受け取り放題になるのはゲームシステムに破綻をきたすので、1回だけ付与できるようにしたい場合は GS2-Limit の提供する回数制限機能と組み合わせて実装するなど、スクリプトの中で工夫を凝らしてください。

サービス紹介・利用料金

GS2-Account

ユーザを識別するためのアカウント管理機能を提供するサービスです。

デバイスの機種変更時にデータを引き継げるようにするために メールアドレス+パスワード や ソーシャルアカウントを利用したデータの引き継ぎ機能に対応しています。

GS2-Account のデータ構造は以下です

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

引き継ぎ情報に関して、同一ユーザの情報の登録・更新・削除は1秒間に3回以内に抑えるようにしてください。 異なるユーザであればこの制限はありません。

基本的な使い方

ネットワーク機能を実装するゲームにはアカウント管理は必須です。 なぜなら、個人を特定しないと所持品の管理や、フレンドの管理といったネットワーク機能の多くの要素を実現できないからです。

アカウントに関して、GS2 では2種類の方針を用意しています。

アカウント管理の仕組み

GS2 では、アカウントの管理機能を実現するために GS2-Account というサービスを提供しています。

匿名アカウント

匿名アカウントの発行実装例(Unity)

yield return gs2.Account.Create(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.AccountId); // string ゲームプレイヤーアカウント
            Debug.Log(r.Result.Item.UserId); // string アカウントID
            Debug.Log(r.Result.Item.Password); // string パスワード
            Debug.Log(r.Result.Item.TimeOffset); // integer 現在時刻に対する補正値(現在時刻を起点とした秒数)
            Debug.Log(r.Result.Item.CreatedAt); // long 作成日時
        }
    },
    namespaceName   //  ネームスペース名
);

最初に GS2-Account が発行するアカウントは 匿名アカウント と呼ばれるカテゴリのアカウントです。 匿名アカウントとは 利用者を特定することはできますが、ゲームプレイヤーが誰なのかを特定する情報をもたないアカウントです。

「なぜ、そんなアカウントを発行するのか」というと、ゲームを開始するときにいきなりメールアドレスを聞いたりするのは、離脱率を上げてしまうからです。 そのため、利用者を特定するのに十分な要素として、GS2-Account はアカウントの新規作成APIを呼び出すとランダムなIDとパスワードを発行します。 アクセス元のクライアントは発行されたIDとパスワードをローカルストレージに保存し、ログイン処理にはその情報を利用します。

引き継ぎ設定

引き継ぎ設定登録の実装例(Unity)

yield return gs2.Account.AddTakeOverSetting(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.TakeOverId); // string 引き継ぎ設定
            Debug.Log(r.Result.Item.UserId); // string ユーザーID
            Debug.Log(r.Result.Item.Type); // integer スロット番号
            Debug.Log(r.Result.Item.UserIdentifier); // string 引き継ぎ用ユーザーID
            Debug.Log(r.Result.Item.Password); // string パスワード
            Debug.Log(r.Result.Item.CreatedAt); // long 作成日時
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  ネームスペース名
    type,   //  スロット番号
    userIdentifier,   //  引き継ぎ用ユーザーID
    password   //  パスワード
);

引き継ぎ実行の実装例(Unity)

yield return gs2.Account.DoTakeOver(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.AccountId); // string ゲームプレイヤーアカウント
            Debug.Log(r.Result.Item.UserId); // string アカウントID
            Debug.Log(r.Result.Item.Password); // string パスワード
            Debug.Log(r.Result.Item.TimeOffset); // integer 現在時刻に対する補正値(現在時刻を起点とした秒数)
            Debug.Log(r.Result.Item.CreatedAt); // long 作成日時
        }
    },
    namespaceName,   //  ネームスペース名
    type,   //  スロット番号
    userIdentifier,   //  引き継ぎ用ユーザーID
    password   //  パスワード
);

匿名アカウントは登録シーケンスが不要というメリットがありますが、当然デメリットもあります。それは、ゲームプレイヤー自身も自分のIDとパスワードを把握していないということです。

そこで、GS2-Account では 引き継ぎ設定 という機能を用意しています。 引き継ぎ設定とは、匿名アカウントをよりゲームプレイヤーが認識しやすいID/パスワードとして表現するためのもので 一つの匿名アカウントに対して複数の引き継ぎ設定を行えるようになっています。

たとえば、スロット1 には メールアドレス任意のパスワード を設定できるようにし、 スロット2 には SNS の アカウント名 などの情報を登録することになります。 あとは、機種変更を行ったり、異なるデバイスで同じアカウントにログインしたいと思ったときには 引き継ぎ設定で登録したID/パスワード情報を入力することで、再度匿名アカウントのログイン情報を取得できます。

異なるデバイスからのログインを止めたい

GS2-Gateway で同時ログインを許可しない例(Unity)

yield return gs2.Gateway.SetUserId(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.ConnectionId); // string コネクションID
            Debug.Log(r.Result.Item.OwnerId); // string オーナーID
            Debug.Log(r.Result.Item.NamespaceName); // string ネームスペース名
            Debug.Log(r.Result.Item.UserId); // string ユーザーID
            Debug.Log(r.Result.Item.CreatedAt); // long 作成日時
            Debug.Log(r.Result.Item.UpdatedAt); // long 最終更新日時
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  ネームスペース名
    false   //  同時に異なるクライアントからの接続を許容するか
);

GS2-Account は引き継ぎを実行した際に匿名アカウントのパスワードを変更する機能があります。 この機能を利用すると、引き継ぎ実行前のデバイスからのログインをできなくすることができます。

しかし、より厳密にログイン制限を行いたい場合はこの方法では不十分です。 たとえば、セーブデータをまるっと異なるデバイスにコピーした場合に、複数のデバイスが正しいID/パスワードを持った状態を作り出すことができます。 このようなケースに対応したければ、 GS2-Gateway の同時ログインを制限する機能を利用するべきです。

GS2-Gateway は WebSocket を使って GS2 のサーバと接続し、サーバで発生したイベント たとえば「フレンド申請が来た」「新しいプレゼントがプレゼントボクスに届いた」「マッチメイキングが完了した」といった通知を受け取るための仕組みです。

この機能の性質上、WebSocket の通信セッションに対してログイン中のユーザIDを紐付ける必要があり この紐付け処理を実行する際に「すでに該当ユーザIDがログイン中の場合は接続を許可しない」ということが実現できます。

自前のアカウント管理システムと連携して GS2 の認証処理を行う

GS2-Account で署名付きアカウント情報を取得する実装例(Unity)

yield return gs2.Account.Authentication(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.AccountId); // string ゲームプレイヤーアカウント
            Debug.Log(r.Result.Item.UserId); // string アカウントID
            Debug.Log(r.Result.Item.Password); // string パスワード
            Debug.Log(r.Result.Item.TimeOffset); // integer 現在時刻に対する補正値(現在時刻を起点とした秒数)
            Debug.Log(r.Result.Item.CreatedAt); // long 作成日時
            Debug.Log(r.Result.Body); // string 署名対象のアカウント情報
            Debug.Log(r.Result.Signature); // string 署名
        }
    },
    namespaceName,   //  ネームスペース名
    userId,   //  アカウントID
    keyId,   //  認証トークンの暗号化に使用する暗号鍵 のGRN
    password   //  パスワード
);

GS2-Auth で署名付きアカウント情報をからアクセストークンを取得する実装例(Unity)

yield return gs2.Auth.Login(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Token); // string アクセストークン
            Debug.Log(r.Result.UserId); // string ユーザーID
            Debug.Log(r.Result.Expire); // long 有効期限
        }
    },
    userId,   //  ユーザーID
    keyId,   //  署名の作成に使用した暗号鍵 のGRN
    body,   //  アカウント認証情報の署名対象
    signature   //  署名
);

gs2-python-sdk を用いてアクセストークンを取得する例

from gs2_auth_client.request import LoginRequest
from gs2_auth_client.result import LoginResult

result = client.login(
    LoginRequest()\
        .with_user_id('user-0001')
)
# エラー発生時には例外が発生
# result が成功結果が格納

すでに自社でアカウント認証基盤を持っている。というケースにも GS2 は対応しています。 GS2-Account のログイン処理は実は2ステップで実装されています。

  1. GS2-Account に匿名アカウントのユーザID/パスワードを送信して、パスワード検証を通過した場合に署名付きのアカウント情報を応答
  2. GS2-Auth に 署名付きアカウント情報 を送信し、署名検証に通過した場合に ユーザID のアクセストークンを発行

自社のアカウント認証基盤のような信頼できるアクセス元からGS2のAPIを呼び出す場合は、 署名検証の部分をスキップしてもリスクにはなりません。

そのため、以下のステップでアクセストークンの取得が可能です。

  1. アカウント認証基盤で アカウントの検証を実施
  2. 検証をパスした場合に GS2-Auth にユーザIDを指定してアクセストークンを作成

あとは、アカウント認証基盤の戻り値に アクセストークン を含めていただき、クライアントは受け取ったアクセストークンを使って GS2 の機能を利用できます。

アクセストークンの有効期限

アクセストークンには有効期限が設定されています。 そのため、取得したアクセストークンを永続化して利用することは推奨できません。

アクセストークンの有効期限は アクセストークン取得時の戻り値に含まれています。 有効期限を迎える前に再度アクセストークンの取得処理を行う必要があります。

GS2-Auth

GS2-Auth

GS2-Auth は GS2 内でゲームプレイヤーを特定するための”アクセストークン”を発行するサービスです。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

TBD

GS2-Chat

テキストベースのチャット機能を提供します。

メッセージデータはルームごとに管理され、過去24時間分のログが保持されます。 ルームにはパスワードを設定でき、設定した場合はメッセージの取得や投稿リクエストにパスワードが必要となります。 ルームにはユーザIDのホワイトリストを設定でき、設定した場合はホワイトリストに含まれるゲームプレイヤーにしかメッセージの取得や投稿ができなくなります。

メッセージにはカテゴリを設定でき、クライアント側でペイロードのパース方法を切り替えることができます。 たとえば、カテゴリ0 はテキストがペイロードにそのまま含まれる。カテゴリ1 はペイロードにスタンプのIDが含まれる。といった使い方ができます。

ルームを購読すれば、ルームに対して新しい投稿があったときにサーバからプッシュ通知を受け取ることができます。 このとき、通知を受けたいカテゴリを設定できますので、メッセージの種類や重要度別にカテゴリを分ければ、ゲームプレイヤーが通知を受け取るメッセージの種類を設定できるようになりユーザ体験を向上させることができます。 また、購読の設定には モバイルプッシュ通知に転送するか という設定もありますので、うまく活用してください。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

1つのルームに対して、1秒間に3回までしか投稿できません。 1ルームに参加できる人数を極端に多くする、一斉に投稿を促すような実装を避けてください。 1つのルームに対して購読できる最大人数は1000人です。

基本的な使い方

TBD

GS2-Datastore

任意のバイナリデータをサーバに保存する機能を提供します。 アップロードされたデータはアクセス権限管理(ACL - Access Control List)が適用されます。

の3種類のACLが設定できます。

アップロードされたデータは自動的にバックアップが作成され、 データを更新・削除したとしても30日以内であれば復元が可能です。 アップロードされたデータには固有のID(UUID v4)と世代IDが割り当てられ、 データを更新・削除すると世代IDが更新されます。 データをダウンロードする際には世代IDを指定して、具体的なファイルを特定します。

データを更新するのと同時に他プレイヤーがデータにアクセスしようとしたときに 意図せず新しいバージョンのデータを取得しないようにダウンロードリクエストには世代IDを含めることができます。 世代IDをダウンロードリクエストに付加することで、確実にリストアップした時点でのデータをダウンロードすることを保証することができます。

ただし、いつまでも世代の古いデータにアクセスできることは望まないゲームもあるかもしれません。 そのため、データの所有者以外は、更新後60分以内かつ1つ前の世代に限り古い世代のデータをダウンロードを許可するオプションが存在します。

本機能を使用してセーブデータの管理をする際には、不正行為に対する耐性を考えてください。 他のGS2のサービスではゲームの仕様に合わせて加算・減算を自由に行えないようコントロールされています。 しかし、ローカルのデーブデータを信頼することはチート行為の原因となりえます。 GS2の機能が提供されている場合はそちらを優先して使用し、 UGCコンテンツの交換や、コンフィグの設定値など改ざんされても影響が軽微な要素のみを管理するようにしてください。

データ構造

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

通常のAPI利用料金に加えて

- データの保存容量 1GBあたり 10円 - データのダウンロード容量 1GBあたり 30円

制限・制約

特に制限はありません。

基本的な使い方

ゲームによってはUGCコンテンツ(レースゲームのゴーストデータやステージのエディットデータ)を他のプレイヤーと共有したいことがあるかもしれません。 このようなときに使用できるのがこのデータストア機能です。

アクセス権限

データストアは任意のバイナリデータをサーバにアップロードできます。 アップロードしたデータにはアクセス権限を設定できます。 アクセス権限にはいくつかの方針があり、データごとに設定ができます。

パブリック

ランキングのスコアにゴーストデータを添付したいような場合に使用します。 スコアのメタデータとして、ゴーストデータのIDを格納しておくことで、ランキングを取得した際に、そのスコアを達成したレースのゴーストをデータストアから取得できるような設計ができます。

プロテクト

特定の相手にのみ公開したい情報。例えば「チャットルームに写真をアップロードして、ルームの参加者にのみアクセス権限を付与したい」というようなケースで使用できます。

プライベート

クラウドセーブのような機能を実現したい場合に使用できます。

データのアップロード

データのアップロードは3つのステップで構成されます。

アップロード用のURLを取得

ファイルをアップロードする前にアップロード用のURLを取得します。 ここで得られたURLに対して HTTP クライアントで PUT リクエストとしてアップロードすることになります。

アップロード用のURLを取得例(Unity)

yield return gs2.Datastore.PrepareUpload(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.DataObjectId); // string データオブジェクト
            Debug.Log(r.Result.Item.Name); // string データの名前
            Debug.Log(r.Result.Item.UserId); // string ユーザーID
            Debug.Log(r.Result.Item.Scope); // string ファイルのアクセス権
            Debug.Log(r.Result.Item.AllowUserIds); // list[string] 公開するユーザIDリスト
            Debug.Log(r.Result.Item.Status); // string 状態
            Debug.Log(r.Result.Item.Generation); // string データの世代
            Debug.Log(r.Result.Item.CreatedAt); // long 作成日時
            Debug.Log(r.Result.Item.UpdatedAt); // long 最終更新日時
            Debug.Log(r.Result.UploadUrl); // string アップロード処理の実行に使用するURL
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  ネームスペース名
    scope,   //  ファイルのアクセス権
    allowUserIds   //  公開するユーザIDリスト
);
データをアップロード

得られた URL に対してアップロードを実行します。

ヘルパー関数

GS2 SDK for Unity ではアップロードプロセスを一つのAPIにまとめたヘルパー関数が用意されています。

ヘルパー関数の利用例(Unity)

yield return gs2.Datastore.Upload(
    r =>
    {
        if (r.Error != null) throw r.Error;
        result = r.Result;
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  ネームスペース名
    scope,   //  ファイルのアクセス権
    allowUserIds,   //  公開するユーザIDリスト
    binary   // byte配列 のバイナリデータ
);

データの世代管理

GS2-Datastore は更新・または削除されたデータに関しても、過去30日間のデータを保持します。 これによって、ライフサイクルによってデータが実際に消去されるまではリストアができるようになっていたり、データを取得するのとほぼ同時にデータが更新されたとしても、意図した世代のデータを取得することができるようになっています。

データのダウンロード

データのダウンロードは2つのステップで構成されます。

料金について

GS2-Datastore の料金モデルは通常のAPIリクエストの料金とは別に保存しているデータ容量に対しても課金されます。 この保存している容量というのは過去の世代のデータに対しても適用されます。

TBD

GS2-Deploy

サービスのデプロイを補助するサービスです。

ゲームが必要とするリソースを記述したテンプレートを適用することで、GS2の各サービスのリソースを作成できるサービスです。 GS2-Deploy を使ったリソース管理を行うことで、開発環境と製品環境で設定を揃えたり、開発中に開発者ごとの環境を作成したりするのが容易となります。 また、イベントを開催するときにも、イベントに必要な要素をテンプレート化して登録し、終わったらそれを削除すれば、イベント開催前の状態に容易に戻すことができます。

GS2-Deploy のデータ構造は以下です

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

TBD

GS2-Distributor

プロパティの増加処理を中継し、対象の所持数量の上限に達していた場合に GS2-Inbox に転送する機能を提供します。 この機能を利用することで、所持数量の上限に到達したときの複雑な分岐処理を無くすことができ、意図しないデータ消失のリスクを回避できます。

その他に、GS2内で一貫性のある処理を実現するための仕組みであるスタンプシートの実行処理にも使用します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一ユーザに対するリソース配送は1秒間に1回におさえてください。 異なるユーザに対してはこの制限はありません。

基本的な使い方

TBD

GS2-Exchange

アイテムの売却価格や、レアリティが異なる進化素材間の交換レートなど、常設される交換処理を実現します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

特に制限はありません。

基本的な使い方

TBD

GS2-Experience

キャラクターやスキルのレベル、親愛度など、経験値を積み重ねてランクアップしていく仕組みの実現に使用します。 ランクキャップ機能も有しており、キャラクターやスキルなど個別の要素ごとに上限管理を行うことができます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一プロパティに対する経験値付与は1秒間に3回までにおさえてください。 異なるユーザや同一ユーザでも異なるプロパティに対する経験値付与であればこの制限はありません。

基本的な使い方

キャラクターやアイテム、スキルなどの成長を管理するためのサービスとして、 GS2 には GS2-Experience が用意されています。

GS2-Experience が提供するのは、主に以下の機能です。

以下の機能については、 GS2-Experience は関知しません。

ただし、 GS2-Stamina など別の GS2 のサービスと連携することで、 GS2-Experience のランクに連動してスタミナの最大値を拡張させたりすることができます。

経験値モデルの登録

経験値モデルは JSON 形式のマスターデータとして登録します。 マスターデータのフォーマットは こちら のリンクに資料があります。

マスターデータは、マネージメントコンソールで要素ごとにデータを登録し、その結果をエクスポートすることで用意することもできます。 エクスポートしたマスターデータを明示的に登録するまで GS2-Experience の挙動には反映されないことに注意してください。

経験値モデルの設定値

経験値モデルはランク体系、たとえばキャラクターレベルやスキルレベルといった成長要素の種類を表現するものです。 『どのランクアップ閾値テーブルを使用するか』や『デフォルトのランクキャップ』、『ランクキャップの引き上げ限界』といった設定が可能です。

ランクアップ閾値テーブル

ランクアップ閾値テーブルはランクアップの閾値となる経験値の配列です。 たとえば、[100, 200, 300] と設定した場合、

経験値の値 ランク
0~99 1
100~199 2
200~299 3
300 4

となり、 300 を超えて経験値を獲得しようとしても、超えた分の経験値は切り捨てられます。

ランクキャップ

ランクキャップ は、到達可能なランクの最大値です。 たとえば、ランクアップ閾値テーブルに指定されている値が [100, 200, 300] で、ランク4までの設定があったとしても、ランクキャップが 2 に設定されている場合、到達できるランクは 2 までとなり、また 100 を超えて経験値を入手できなくなります。 現在のランクキャップの値は、経験値や現在のランクの値同様に個々のキャラクターやスキルなどごとに持つことができます。 ランクキャップは経験値モデルごとの ランクキャップの引き上げ限界 まで引き上げることができ、ランクキャップを引き上げることによって、入手できる経験値の最大量も同時に拡張されます。

経験値の入手

現在獲得している経験値は、経験値モデルとプロパティIDの組み合わせごとに保持されます。

そのため、経験値の入手処理を実行する際にも、獲得する経験値量のほかに、経験値モデルとプロパティIDの組み合わせを指定する必要があります。 プロパティIDとは経験値を付与する対象の識別子で、 GS2-Experience への事前の登録なく任意の値を指定できます。

たとえば、GS2-Inventory で管理している所持キャラクターに対して経験値を付与したい場合は、GS2-Inventory の『所持品ID』をそのままプロパティIDに指定できます。 ゲームプレイヤー自身に経験値を付与したい場合は、GS2-Account の『ユーザID』をそのまま指定できます。

また、所持キャラクターに複数の経験値を持たせたい場合、たとえば『戦闘スキル』と『信頼度』のようにランク体系が異なるのであれば、異なる経験値モデルを指定することで、 GS2-Inventory の『所持品ID』1つに対して複数の経験値を持たせることができます。 一方、『剣スキル』と『弓スキル』のように、武器スキルという同じランク体系のなかで複数の経験値を持たせたい場合は、同様の設定を持つ経験値モデルを複数用意する方法もありますが、 GS2-Inventory の『所持品ID』のうしろに -sword や -bow などの文字列を追加してプロパティIDを区別する方法もあります。

経験値の入手とセキュリティ

経験値の入手はセンシティブな操作です。 経験値の入手に関してクライアントから任意の操作を受け付けることは、不正に経験値を増殖させ、ゲーム性を大きく損なうことになりかねません。

そこで、GS2 ではスタンプシートの報酬としてしか経験値の入手を行えないようにしています。 スタンプシートの報酬の内容はサーバに定義を持ち、また報酬を得る前に対価の支払いやゲーム内目標の達成をおこなったことをスタンプシートの仕組みが保証するため、経験値の不正な増殖を防げます。 したがって、経験値の入手には、ゲーム内ストアでの購入や、クエストの報酬として入手するような報酬設計が必要です。

現在の経験値の取得

経験値を取得する方法は2種類あります

特定のキャラクターのプロパティページを表示したい場合、後者を使用するほうが効率がいいでしょう。

経験値の取得コード例(Unity)

yield return gs2.Experience.GetStatus(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.ExperienceName); // string 経験値の種類の名前
            Debug.Log(r.Result.Item.PropertyId); // string プロパティID
            Debug.Log(r.Result.Item.ExperienceValue); // long 累計獲得経験値
            Debug.Log(r.Result.Item.RankValue); // long 現在のランク
            Debug.Log(r.Result.Item.RankCapValue); // long 現在のランクキャップ
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  ネームスペース名
    experienceName,   //  経験値の種類の名前
    propertyId,   //  プロパティID
);

パーティに編成されたキャラクターへの経験値付与

GS2 では複数のリソースを編成するためのサービスとして GS2-Formation が用意されています。 GS2-Formation を利用すると、個別に指定をおこなうことなく、編成されたリソースの全体に報酬として経験値を付与することができます。 これによって、パーティに編成されているキャラクターなどに対して、一括で簡単に経験値を付与することができます。

ステータスの正当性保証

ランクに応じて何らかの機能を開放するなどのユースケースで、クライアントが送信してきたランク値が正当なのかを検証したくなることがあります。 GS2-Experience はそのようなニーズに応えるために、署名(※)付きのステータスを応答する機能があります。

この機能を利用することで、サーバ間通信を行わず、クライアントが送信してきたステータスの値を信用していいかを判断することができるようになります。 繰り返しステータスを使用した操作を行う場合、署名付きのステータスをクライアントからサーバに渡すことで、通信回数の削減や通信時間の削減ができます。

ただし、署名付きステータスは取得時の値でしかありませんので、ステータスに変化があった場合はクライアント側で再取得を行ったうえでサーバに渡す必要があります。 署名付きステータスには応答時刻が付与されており、 GS2 のサービスでは、取得から1時間以上経過した署名付きステータスは使用できなくなります。

※ 署名とは暗号技術の一種で、とあるデータが改ざんされていないかを検出するために使用できる値です。

GS2-Formation

装備やパーティ編成機能を実現します。

編成情報を記録する フォーム(Form) と、編成した フォーム を保存する 保存したフォーム(Mold) があります。 パーティ編成を例にすると、キャラクターを フォーム に設定し、パーティを編成します。 編成したパーティ情報を複数記録する場合は 保存したフォーム を使って実現できます。

保存したフォーム に保存できる数にはキャパシティを設定でき、キャパシティの値は引き上げることができます。

フォーム に編成するプロパティはプロパティIDで指定することになりますが、プロパティの正規表現で値を限定することができます。 また、GS2-Inventory のリソースを設定する場合は、GS2-Inventory が発行する 署名付きアイテムセット を使って編成対象に指定できます。 署名付きアイテムセット とは、該当のアイテムを所有していることを保証する署名付きプロパティ情報で、こちらを使う場合はクライアントから直接編成することを許します。

この 所有していることを保証する とは編成時点での保証であり、編成後にプロパティを手放した場合のフォローはありません。 GS2-Inventory でプロパティを消費したり売却したりする場合は、事前に GS2-Formation で編成に使用されていないかの判定はクライアントまたはスクリプトで実装する必要があります。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

特に制限はありません。

基本的な使い方

TBD

GS2-Friend

ゲーム内の友達関係を実現するときに使用します。

リクエストを出して、承認されて初めて関係の構築されるフレンドと、 相手の承認手続きなしに構築できるフォローがあります。

ゲームプレイヤーのメタデータとしてプロフィール情報を持たせることができます。 プロフィール情報は 公開 フォロワーに公開 フレンドに公開 のスコープがあり、スコープに応じて他プレイヤーに公開されます。

フレンド・フォロワー数の上限はそれぞれ1万人ですが、連携するサービスによっては各サービスのポリシーに従って追加の制限が加わる可能性があります。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一ゲームプレイヤーによるプロフィールの更新は1秒間に3回までにおさえてください。 異なるユーザや同一ユーザでも異なるプロパティに対する経験値付与であればこの制限はありません。

1人のプレイヤーにフレンドリクエストが集中する仕様は避けてください。 1人のプレイヤーがフレンドリクエストを受け付けられるのは1秒間に3回までです。

基本的な使い方

TBD

GS2-Gateway

GS2-Gateway は WebSocket を経由して GS2 にアクセスするためのプロキシ機能を提供します。 GS2-Gateway を使用して GS2 のサーバを利用することで、常時接続しながら通信できるようになります。これによって、サーバーとの通信速度が改善することが期待できます。

また、常時接続できることから、 サーバーサイドからのプッシュ通知に対応 できます。 このプッシュ通知機能があると GS2-Mission で ミッションのタスク を達成したときや、 GS2-Matchmaking で ギャザリング の参加者の増減があった時などに通知を受け取ることができるようになります。

プッシュ通知対象のデバイスがオフラインだった場合、 Firebase (https://firebase.google.com/) を使用したモバイルプッシュ通知に転送する仕組みも提供します。 この機能を使用すれば、マッチメイキングが完了したときにプレイヤーがオフラインだった場合はモバイルプッシュ通知を出してゲームに戻ってくるようプレイヤーを誘導することができます。

GS2-Gateway で常時接続用のセッションを構築する際にゲームプレイヤーの識別を行えるよう GS2-Auth で発行した アクセストークン で認証処理を行うことになりますが、 すでに同一ユーザのセッションが存在する場合は、アクセスを拒否することができます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

TBD

GS2-Inbox

プレゼントつきのメッセージ機能を提供するサービスです。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

TBD

GS2-Inventory

所持品の管理機能を提供します。

アイテムを入れるカバンのような存在をインベントリと呼びます。 インベントリにはキャパシティを設定でき、キャパシティは後で拡張することができます。 課金通貨を使ったり、クエストの報酬でキャパシティを拡張することができます。

アイテムには有効期限を設定できます。 有効期限の切れたアイテムは使用できなくなり、アイテムを使用する際には明示的に有効期限を指定しなければ有効期限の近いアイテムから消費されます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一プロパティに対する所持数量の増減処理は1秒間に3回までにおさえてください。 異なるプロパティでもインベントリにとって初めて入手する、インベントリのアイテムを使い切る操作の場合は、インベントリ内の操作で1秒間に3回までにおさえてください。 異なるプロパティに対する所持数量の増減であればこの制限はありません。

基本的な使い方

GS2 では所持品を管理するための仕組みとして GS2-Inventory というサービスを提供しています。 所持品と見なせるゲーム内要素にはさまざまな種類がありますが、GS2-Inventory はそれら全てを取り扱えるよう設計しています。 たとえば、以下のようなものです。

GS2-Inventory は所持数量のみを管理し、使用した際の効果の実装には GS2-Exchange などを用いてリソースの交換設計(たとえば、スタミナドリンクを対価としてスタミナ値を回復する報酬を得るというような)を行うことになります。

インベントリ・アイテムセット

インベントリの取得例(Unity)

yield return gs2.Inventory.GetInventory(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.InventoryId); // string インベントリ
            Debug.Log(r.Result.Item.InventoryName); // string インベントリモデル名
            Debug.Log(r.Result.Item.CurrentInventoryCapacityUsage); // integer 現在のインベントリのキャパシティ使用量
            Debug.Log(r.Result.Item.CurrentInventoryMaxCapacity); // integer 現在のインベントリの最大キャパシティ
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  カテゴリー名
    inventoryName   //  インベントリの種類名
);

GS2-Inventory では所持品を管理する単位として、 アイテムセットインベントリ という概念があります。 アイテムセット は、「スタミナドリンク×1個」や「傷薬×30個」といった、アイテムの種類とその個数の組み合わせです。 また、 インベントリ はアイテムセットを格納する鞄のようなものです。 実際にゲームで使用する際には、キャラクター・消費財・通貨 など、UIで一覧表示させたいカテゴリごとにインベントリを分割して利用するのが効果的です。

所持しているアイテムの取得例(Unity)

yield return gs2.Inventory.ListItems(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Items); // list[ItemSet] 有効期限ごとのアイテム所持数量のリスト
            Debug.Log(r.Result.NextPageToken); // string リストの続きを取得するためのページトークン
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  カテゴリー名
    inventoryName,   //  インベントリの種類名
    pageToken,   //  データの取得を開始する位置を指定するトークン(オプション値)
    limit   //  データの取得件数(オプション値)
);

所持品枠

インベントリは所持品枠を持ち、アイテムセット1個につき1個の所持品枠を占有します。 所持品枠の数は有限であり、これを超えてアイテムセットを所持することはできません。 所持品枠の数はゲームプレイヤーごとに拡張できるようになっており、課金通貨やリソースを使用して拡張できるよう設計できます。

同一アイテムの複数所持

GS2-Inventory では、アイテムの種類ごとに、1つのアイテムセットに格納可能な最大個数を設定できます。 キャラクターや装備品のように、一つ一つを区別する必要があるアイテムではこの最大個数を 1 に設定します。 一方、消費物や強化素材のように、たくさん所持することが前提となっているアイテムでは 99 や 999 などに設定します。

また、アイテムの種類ごとに、そのアイテムのアイテムセットを複数作成できるかを設定することができます。 この設定を有効にすることで、複数の所持品枠を占有することで、1アイテムセット当たりの最大個数を超えてアイテムを所持できるようになります。

アイテムの種類の特徴に応じて、たとえば以下のような設定が考えられます。

アイテムの種類 1つのアイテムセットに格納可能な最大個数 同一種類のアイテムセットを複数作成できるか
キャラクターや装備品など 1 OK
消費物や強化素材 99 OK
ゲーム内通貨 999999999 NG

有効期限

所持品には有効期限を設定できます。有効期限の切れた所持品は勝手に削除されます。 イベント期間中にのみ使用できるアイテムログインボーナスとして当日のみ使用できるスタミナドリンクを配布 というような使い方ができます。

種類が同じでも、有効期限の異なるアイテムは別のアイテムセットが作成されます。 その際、同一種類で複数のアイテムセットの作成ができないように設定されたアイテムの場合は入手ができない可能性があります。

インベントリモデル・アイテムモデルの登録

インベントリモデル・アイテムモデルは JSON 形式のマスターデータとして登録します。

マスターデータは、マネージメントコンソールで要素ごとにデータを登録し、その結果をエクスポートすることで用意することもできます。 エクスポートしたマスターデータを明示的に登録するまで GS2-Inventory の挙動には反映されないことに注意してください。

アイテムの入手

アイテムセットに入りきらなかったり、インベントリの所持品枠が足りなかったりして全量を入手できなかった場合は、入りきる分だけがインベントリに格納され、あふれた個数が応答されます。 また、あふれたアイテムは、ネームスペースに設定したスクリプトや、 GS2-Distributor を利用することで、プレゼントボックスへ転送することができます。

必ず新しいアイテムセットを作るように指定した場合、新しいアイテムセットがインベントリに入りきらなければ、既存のアイテムセットに空きがあっても、あふれたものとして扱われます。

アイテムを格納するアイテムセットを指定した場合、そのアイテムセットに入りきらなければ、ほかのアイテムセットやインベントリに空きがあっても、あふれたものとして扱われます。 また、指定したアイテムセットの内容と、入手しようとしたアイテムの種類や期限が一致しない場合はエラーになります。

いずれの指定もない場合、アイテムは、インベントリの所持枠の占有がもっともすくなくなるように格納されます。

アイテムの入手とセキュリティ

アイテムの入手はセンシティブな操作です。 アイテムの入手に関してクライアントから任意の操作を受け付けることは、不正にアイテムを増殖させ、ゲーム性を大きく損なうことになりかねません。

そこで、GS2 ではスタンプシートの報酬としてしかアイテムの入手を行えないようにしています。 スタンプシートの報酬の内容はサーバに定義を持ち、また報酬を得る前に対価の支払いやゲーム内目標の達成をおこなったことをスタンプシートの仕組みが保証するため、アイテムの不正な増殖を防げます。 したがって、アイテムの入手には、ゲーム内ストアでの購入や、クエストの報酬として入手するような報酬設計が必要です。

アイテムの消費

GS2の思想としては、スタンプシートの対価としてアイテムの消費は行われるべきです。 しかし、ローカルリソースに対して報酬を与えられるようにGS2では消費に関してはAPIを用意しています。

アイテム消費の例(Unity)

yield return gs2.Inventory.Consume(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Items); // list[ItemSet] 消費後の有効期限ごとのアイテム所持数量のリスト
            Debug.Log(r.Result.ItemModel.Name); // string アイテムモデルの種類名
            Debug.Log(r.Result.ItemModel.Metadata); // string アイテムモデルの種類のメタデータ
            Debug.Log(r.Result.ItemModel.StackingLimit); // long スタック可能な最大数量
            Debug.Log(r.Result.ItemModel.AllowMultipleStacks); // boolean スタック可能な最大数量を超えた時複数枠にアイテムを保管することを許すか
            Debug.Log(r.Result.ItemModel.SortValue); // integer 表示順番
            Debug.Log(r.Result.Inventory.InventoryId); // string インベントリ
            Debug.Log(r.Result.Inventory.InventoryName); // string インベントリモデル名
            Debug.Log(r.Result.Inventory.CurrentInventoryCapacityUsage); // integer 現在のインベントリのキャパシティ使用量
            Debug.Log(r.Result.Inventory.CurrentInventoryMaxCapacity); // integer 現在のインベントリの最大キャパシティ
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  カテゴリー名
    inventoryName,   //  インベントリの名前
    itemName,   //  アイテムマスターの名前
    consumeCount   //  消費する量
);

アイテムを消費するアイテムセットを指定した場合、指定したアイテムセットに格納された個数で足りなければ、ほかのアイテムセットに余分なアイテムがあったとしても、個数不足として扱われます。

アイテムを消費するアイテムセットを指定しなかった場合、複数のアイテムセットをまたいでアイテムを消費することができます。 またその際、期限が早いアイテムセットから優先的に使用されます。

GS2-JobQueue

ジョブキュー機能を提供するサービスです。

ジョブキューはFIFOを保証したキューで、結果整合処理に使用できます。 結果整合処理とはAPIが応答した時点では処理の完了が保証されず、しばらくの時間の経過をもって”結果的に意図した状態になる”仕組みです。

GS2 では報酬付与の多くの部分を結果整合で処理します。 これは、報酬を付与する際に報酬を管理するサーバーがダウンしていると、芋づる式にいろいろなサービスが利用できなくなることを避けるためです。 報酬付与の処理をジョブキューに登録し、ジョブを実行する際に対象のサーバーが利用できない状態だったとしても、ジョブのリトライで結果整合を実現します。 リトライ回数には最大値を指定でき、最大値に達しても成功しなかった場合はデッドレタージョブという失敗したジョブが蓄積するモデルに格納されます。 デッドレタージョブを使って、処理の失敗したジョブに関して、個別対応を行えるようになっています。

GS2-JobQueue のジョブは勝手には実行されず、明示的に実行APIを呼び出す必要があります。 これにはジョブの戻り値をクライアント側で把握できるようにする目的があります。 例えば、GS2-Quest上でクエストをクリアしたあと、ジョブキューに報酬を付与するジョブが登録され、 クライアントはジョブキューを終端まで実行することでクエストによって得られた報酬付与処理を明示的に実行することができます。(結果整合の遅延処理の完了を待つことが可能) なおかつ、そのジョブの戻り値を参照することで得られた報酬をUIに反映することができます。

ジョブの登録・実行は1秒間に3回までしか行なえません。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一ユーザに対するキューに対するジョブ登録は1秒間に3回までにおさえてください。1回のジョブ登録で最大10個までジョブを登録できますので工夫をしてください。 ジョブの実行は1秒間に1回におさえてください。 異なるユーザに対する処理であればこの制限はありません。

基本的な使い方

TBD

GS2-Limit

回数制限処理を実現するためのサービス

このサービスは単独で使うのではなく 他のサービスで回数制限機能を実現します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一カウンターに対する操作は1秒間に3回までにおさえてください。 異なるユーザや、同一ユーザでも異なるカウンターに対する処理であればこの制限はありません。

基本的な使い方

TBD

GS2-Lock

排他処理を実現するためのサービスです。

GS2内のAPIによるリソースの操作は、そのままでは排他されません。 スタンプシートによってリソースの増減を行うときは、自動的にこのサービスが利用され、対象リソースのIDをキーとしてロックを取得します。 スタンプシートを経由せずにリソースの増減を行う場合、明示的に GS2-Lock を使用してロック操作を行う必要があります。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一ミューテックスに対する操作は1秒間に3回までにおさえてください。 異なるミューテックスに対する処理であればこの制限はありません。

基本的な使い方

TBD

GS2-Log

ログの集約・集計を実現するためのサービスです。

GS2に対するアクセスログや、スタンプシートによるリソース交換のログを検索・集計できるようになります。 ログの保管先は3種類用意されています。

GS2 でログを保管したときにのみ集計機能を利用できます。

どのマイクロサービスのログを集約するかを設定するには各サービスのネームスペースの設定値に GS2-Logs のネームスペースを設定しておこないます。 GS2でログを保管 / Google BigQuery でログを保管 を使用する場合は、ログの保存期間を設定できます。

データ構造

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

制限・制約

GS2-Logs を使うにあたって注意する点はありません。

基本的な使い方

TBD

GS2-Lottery

抽選処理を実現するサービスです。

抽選処理は排出確率テーブルを元に行われます。 排出確率テーブルには、景品ごとの排出される重みを設定します。 例えば、景品Aに3を設定し、景品Bに1を設定した場合、景品Aの排出確率は75%、景品Bは排出確率は25%となります。

また、景品として他の排出確率テーブルを設定することもできます。 あらかじめSSR、SR、Rなどの同一レアリティごとに排出確率テーブルを用意しておき、それらを景品とした排出確率テーブルをつくることで、レアリティごとの排出確率を容易に設定できます。

抽選処理には2つのモードがあり、抽選の種類で設定できます。 1つ目は毎回変わらない確率で一様に抽選処理を行うモード、2つ目はボックスガチャモードです。 ボックスガチャとは、抽選処理を行うごとに景品を箱から取り出していき、最初に箱に入れた数と同回数抽選を行うと、必ずすべての景品が手に入るという方式です。 モードにボックスガチャを設定した場合は、排出テーブルに設定した重みの数だけ箱に景品が入った状態から抽選が始まります。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

TBD

GS2-Matchmaking

対戦相手や協力相手を実現するサービスです。

マッチメイキングの流れは以下です。

ホストがギャザリングの作成 ↓ 参加者が条件にみあうギャザリングを検索 ↓ 規定人数が集まったらマッチメイキング完了

GS2-Matchmaking はこの流れのうち 参加者が条件にみあうギャザリングを検索 に関してできるだけ多くのニーズをみたせるよう設計されています。 ここでは、ユースケースを交えつつ、その設計例を解説します。

ゲーム内には、カジュアルプレイヤー向けと、コアゲーマー向けの2種類のゲームモードがあります。 これらのプレイヤーを混ぜることなくマッチメイキング処理を行いたいとします。

ギャザリングを作成する際にも、ギャザリングを検索する際にも、最大5つの属性値を指定できます。 ギャザリングを作成する際には募集するプレイヤーが持つ属性値の範囲を指定し、 ギャザリングを検索する際には、自身の属性値の値を指定して処理依頼をします。

つまり、ホストがギャザリングを作成する際に 属性値1 に募集するゲームモードを指定し、 検索する側も自分が参加したいゲームモードを 属性値1 に指定することで、希望するゲームモードが同じプレイヤー同士がマッチメイキングされます。

ホストはギャザリングを作成する際に 属性値2 に募集するレベルの最小値と最大値を指定します。 検索する側は自分のレベルを 属性値2 に指定することで、同じレベル帯のプレイヤー同士がマッチメイキングされます。

複数の属性値を指定した場合はすべての属性値の範囲をみたす相手を探すよう処理されます。

最初は狭い範囲で募集し、なるべく同じレベルのプレイヤー同士でマッチメイキングするが、なかなか対戦相手が見つからない場合、募集するレベル帯を広げて募集したいとします。 ギャザリングを作成しておよそ1分ごとに発火する GS2-Script イベントがあります。 このスクリプトにて、ギャザリングの属性値の範囲を緩和することで、徐々に希望する条件を緩和する処理を実装できます。

ギャザリングを作成するときのオプションとして、参加を許可するユーザIDリストを指定できます。 これを使い、フレンドのユーザIDリストを指定することで、参加者を限定することができます。

ギャザリングの作成時・検索時にユーザIDのブラックリストを指定できます。 検索時に指定したブラックリストは、条件に一致するギャザリングを発見し、参加したギャザリングのブラックリストに加えられます。 検索する際に自身がブラックリストに入っている場合はマッチメイキングの対象から除外されます。

ゲームによっては盾役1人、回復役1人、攻撃役2人でマッチメイキングを行いたいことがあります。 このときに使用できるのが ロール属性 です。

盾役として tank を、回復役として healer を、攻撃役として attacker というロールを想定します。 ギャザリングを作成するときに各ロールの募集人数を設定します。 ギャザリングの検索時に自分のロールに設定することで、ロールに関して空きのあるギャザリングだけが参加可能なギャザリングとして処理されます。

募集ロールにはエイリアスを設定することもできます。 例えば、 tank を更に具体的なロールで表現し、 paladinwarriordark_knight のいずれかとしてギャザリングの検索を行うとします。 ギャザリングを作成するときに、 tank のエイリアスに paladindark_knight を指定した場合、 warrior は参加できないギャザリングが作成できます。

マッチメイキング完了後に、1名が離脱し 1名だけ補充したいときに使用します。 このときに使用するのがパーティトークンです。パーティトークンを発行するにはパーティを編成するプレイヤーのプレイヤートークンです。 プレイヤートークンはプレイヤー自身が自分の代わりにパーティの代表者がマッチメイキングを行うことを許可するトークンで、3分間の有効期限があります。 パーティメンバーはそれぞれ、プレイヤートークンを発行するAPIに依頼をしてプレイヤートークンを入手し、パーティの代表者に送信します。 パーティの代表者はパーティメンバーからプレイヤートークンを受け取り、3分以内にパーティトークン発行APIに送信することで、パーティトークンを入手できます。 パーティトークンには10分の有効期限があり、10分以内にパーティトークンを使用してギャザリング作成処理または、ギャザリング検索処理を呼び出します。 こうすることで、パーティメンバー全員が参加した状態のギャザリングを作成、またはパーティメンバー全員が参加できるギャザリングに参加します。

4 vs 4 で対戦するゲームで、事前に編成したパーティを敵味方に分散することなく対戦したいことがあります。 たとえば、事前に3人で編成したパーティと、事前に4人で編成したパーティ、そして1人の野良プレイヤーをマッチメイキングして、 事前に3人で編成したパーティ + 1人の野良プレイヤー vs 事前に4人で編成したパーティ という対戦を実現したいとします。 このようなケースでは、まずは4人のマッチメイキングを実行します。 すると、3人で編成したパーティ + 1人の野良プレイヤー事前に4人で編成したパーティ の2つのギャザリングができます。 その後、それぞれのパーティの代表者が 定員2人のマッチメイキングをおこない、パーティ同士がマッチメイキングされます。 こうすることで 事前に3人で編成したパーティ + 1人の野良プレイヤー vs 事前に4人で編成したパーティ を実現できます。

マッチメイキングが完了していない状態でプレイヤー間のやりとりを必要とすることがあります。 GS2-Matchmaking では、ギャザリングの作成時にプレイヤー間の通信をする手段として2種類の方法を提供します。

募集が終わっていない状態でも、プレイヤー間のでメタデータを低頻度で交換したい場合は前者を、 募集しながらNPCを交えたり人数が不足した状態でゲームを遊ばせたい場合は後者を使用します。

低頻度・高頻度の判断基準として使用できるのが GS2-Chat の持つ制限です。 GS2-Chat は1つのチャットルームに対して秒間3回しか発言ができません。この頻度を超える場合は GS2-Realtime を使用してください。

マッチメイキングが完了したときに GS2-Gateway を使用したゲーム内プッシュ通知GS2-JobQueue によるジョブ登録 でプレイヤーにマッチメイキングの完了を知らせることができます。 また、GS2-Script を使用して任意のスクリプトを実行することもできます。 GS2-Realtime を使用してマッチメイキング後の対戦・協力プレイを実現する場合は、マッチメイキング完了後に実行される GS2-Script で GS2-Realtime のギャザリングを作成し、そのギャザリングのIPアドレス・ポートの情報を GS2-Gateway を使用したゲーム内プッシュ通知GS2-JobQueue によるジョブ登録 を使用して通知することで、ゲームサーバに誘導できます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

マルチプレイヤーゲームを実現するには 対戦相手や協力プレイの相手を見つける 仕組みが必要です。 GS2 ではこの仕組みを実現するためのサービスとして、GS2-Matchmaking を提供しています。

一般的にマッチメイキングのアプローチは2種類存在します。

GS2 では、後者の自動マッチメイキングのみを提供しています。 手動マッチメイキングは、よりゲームプレイヤーのニーズに合ったルームを見つけられる利点はあるものの 一覧取得参加 のアクションの間に 人間による選別 というタイムラグが発生するため 参加しようとした部屋が満室になっていた という状態に陥りやすく、UX設計が非常に難しい一方で、手動マッチメイキングを行う必要があるほど厳密に対戦相手を探したいユースケースは多くないためです。

ギャザリング

GS2-Matchmaking では、マッチメイキング条件の見合うゲームプレイヤー同士を引き合わせて ギャザリング を構築します。 ギャザリングはルームのようなものと考えて問題ありません。 ギャザリングはホストとなるプレイヤーが最初に作成し、ゲストは条件の見合うギャザリングへ割り当てられます。 ギャザリング内のゲームプレイヤーが規定人数に達すると、マッチメイキングが成立したとして通知の送信やスクリプトの実行が行われ、ギャザリングは消去されます。

条件の設定

属性値

マッチメイキング条件には属性値を最大5つ指定できます。 属性値は整数値で、最小値と最大値を指定できます。対戦相手を探す際にはすべての属性値が希望する範囲に含まれるゲームプレイヤーを探します。

ロール

役割に応じたマッチメイキングを実現するときに使用します。 役割とは 盾役 回復役 攻撃役 のような分類で、マッチメイキング条件として 盾役を1人 回復役を1人 攻撃役を2人 といった形で指定できます。

ロールのエイリアス

ロールにはエイリアス(別名)を設定できます。 たとえば、役割としては 盾役 ではあるものの、ゲーム内の職業としては 戦士ナイト があったとします。 どうしても対戦相手として参加してきてほしい職業は 戦士 であってほしい。という場合に使用できるのがエイリアスです。

自分の役割として直接 盾役 と申告するのではなく 戦士 ナイト という職業レベルで申告するようにしておき、一般的なマッチメイキングでは 盾役 のエイリアスには 戦士 ナイト を設定しておきます。 すると、 戦士 でも ナイト でも 盾役 として参加できるギャザリングが出来上がります。 どうしても 戦士 しかマッチメイキングしてほしくない場合は、エイリアスに 戦士 のみを含めます。 すると ナイト盾役 として認識されなくなりますので、マッチメイキング対象から外されます。

マッチメイキング条件を緩めていく

条件が厳密であれば厳密であるほど良質なマッチメイキングとなります。 たとえば、レーティングをマッチメイキング条件に設定する場合、まったく同じレーティング値であればあるほど良質なマッチメイキングといえるでしょう。 一方で、良質なマッチメイキングを実現するにはたくさんのゲームプレイヤーが必要です。なぜならそんなに都合よくスキルが拮抗するゲームプレイヤーはいないからです。

だからといって、最初から良質なマッチメイキングをあきらめて、マッチメイキング条件として使用するレーティングの幅を大きくとる必要はありません。

GS2-Matchmaking ではギャザリング開始から1分ごとにスクリプトを実行できるようにしています。 この機能を利用すれば待ち時間に応じてマッチメイキングの条件を緩和していくことができます。 これによって、相手がいる場合は良質なマッチメイキングを、そうでない場合はとりあえず対戦相手を見つけるよう設計することができます。

フレンドのみ参加可能なギャザリング

ギャザリングには参加可能なユーザIDを列挙することで、参加者に制限をかけることができます。 この機能を利用することで、フレンドのみが参加可能なマッチメイキングを実現することができます。

ブラックリスト

GS2-Matchmaking にはブラックリストの機能があります。 マッチメイキングの条件が見合っていても、自分のユーザIDがブラックリストに含まれているゲームプレイヤーは、そのギャザリングには参加できなくなります。 ブラックリストの情報は各プレイヤーが持てるようになっており、ギャザリングに参加している全プレイヤーのブラックリストがマージされたものがギャザリングのブラックリストとして使用されます。

パーティトークンとプレイヤートークン

1つのギャザリングへ複数のプレイヤーが一緒に参加するために、 パーティトークン が使用できます。 パーティトークンを利用することで、複数のプレイヤーが一緒にギャザリングを作成したり、一緒に同じギャザリングへ参加したりできます。

パーティトークンは、パーティの代表者が、パーティ内の各プレイヤーから プレイヤートークン を集めて作成します。 プレイヤートークンは3分、パーティートークンは10分の有効期限があります。

追加募集

一度マッチメイキングが完了したものの、何らかの理由で1名が離脱し再度募集を募りたいことがあります。 このような場合、残ったメンバーでパーティトークンを作成し、再度マッチメイキングを行うことで欠員を補充する追加募集を実現することができます。

パーティ同士のマッチメイキング

ゲームの仕様によっては 4vs4 のようなチーム戦を行うことを希望するかもしれません。 このようなケースにおいて、フレンド同士をチームにしたいという仕様もあるでしょう。 GS2-Matchmaking を使ってこのような仕様を実現するには、一旦4人でマッチメイキングを実行してチームを作成します。 この際、フレンド同士のチームはフレンド限定のマッチメイキングで作成します。 その後、各チームの代表者が2人のマッチメイキングを実行して、 4vs4 の対戦相手となるギャザリングを発見するように設計します。

マッチメイキング中のプレイヤー間通信

マッチメイキングが終わっていない状態でもプレイヤー間でメッセージングを行いたいことがあります。 このような時に使用できるのが GS2-Chat / GS2-Realtime 連携です。 この機能を利用すると、 ギャザリング作成時 または マッチメイキング完了時 に GS2-Chat または GS2-Realtime のルームを作成することができます。 今回の場合は ギャザリング作成時 にルームを作成することで、マッチメイキング実行中のメッセージングに使用できます。

GS2-Chat と GS2-Realtime のどちらを使用するべきかを判断するにはいくつかの指標がありますが、マッチメイキング完了後の対戦で GS2-Realtime を利用するのであれば、マッチメイキング中のコミュニケーションにも GS2-Realtime を使うのが実装の手間は少なく済むでしょう。 もう一つの指標は通信間隔です。GS2-Chat は 1つのルームに対するメッセージの送信は秒間3回まで という制約がありますので、これを超える頻度の通信が必要なら GS2-Realtime を使うことになります。 最後の指標はコストです。GS2-Chat は 一般的なAPIリクエスト回数 による課金ですが、GS2-Realtime はルームの維持時間1分当たり+ネットワーク通信量の課金で、APIリクエスト回数には費用は発生しません。

この辺りを材料に自分のゲームにとってどの方法で仕様を実現するのがいいかを検討してみてください。

マッチメイキングの進捗のハンドリング

GS2-Realtime はマッチメイキングの進捗を GS2-Gateway によるプッシュ通知でハンドリングすることができるようになっています。 通知の種類は以下のものが用意されています。

マッチメイキングが完了したときは GS2-Gateway による通知のほかに、GS2-JobQueue を使用したハンドリングと、GS2-Script を使用したハンドリングにも対応しています。

GS2-Realtime との連携機能を使用すると ギャザリングの作成時 または マッチメイキング完了時 にルームの作成リクエストが自動的に発行され、ルームの作成が完了すると GS2-Gateway による通知が届きます。 通知には起動したゲームサーバのIPアドレスおよび待ち受けポート。通信パケットの暗号化に使用する暗号鍵などの情報が含まれており、それらの情報を使用して対戦に引き継げるようになっています。

ギャザリングの作成

ギャザリングの作成例(Unity)

yield return gs2.Matchmaking.CreateGathering(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.GatheringId); // string ギャザリング
            Debug.Log(r.Result.Item.Name); // string ギャザリング名
            Debug.Log(r.Result.Item.AttributeRanges); // list[AttributeRange] 募集条件
            Debug.Log(r.Result.Item.CapacityOfRoles); // list[CapacityOfRole] 参加者
            Debug.Log(r.Result.Item.AllowUserIds); // list[string] 参加を許可するユーザIDリスト
            Debug.Log(r.Result.Item.Metadata); // string メタデータ
            Debug.Log(r.Result.Item.CreatedAt); // long 作成日時
            Debug.Log(r.Result.Item.UpdatedAt); // long 最終更新日時
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  ネームスペース名
    player,   //  自身のプレイヤー情報
    capacityOfRoles,   //  参加者
    allowUserIds,   //  参加を許可するユーザIDリスト
    attributeRanges   //  募集条件(オプション値)
);

ギャザリングを作成するためには 募集条件参加可能なユーザIDリスト を設定する必要があります。 募集条件は 属性値募集人数 の2種類があります。

属性値

属性値のモデル構造

パラメータ データ型    必須  説明
name    string  true    属性名
min     integer true    ギャザリング参加可能な属性値の最小値
max     integer true    ギャザリング参加可能な属性値の最大値

最大5つの属性値を指定します。属性値は整数値の範囲で指定できるようになっています。 ゲームプレイヤーがマッチメイキングリクエストを出す際には自分の属性値の値を申告し、属性値の範囲に該当するギャザリングが参加候補のギャザリングとして判定されます。

募集条件

募集条件モデル構造

パラメータ         データ型            必須  説明
roleName        string          true    ロール名
roleAliases     list[string]    false   ロール名の別名リスト
capacity        integer         true    募集人数
participants    list[Player]    true    参加者のプレイヤー情報リスト

募集人数は文字通り何人のゲームプレイヤーを見つけるかというパラメータです。 募集人数は 役割 ごとに設定できます。 役割 とは 盾役1人 回復役1人 攻撃役2人 というように設定できます。 特にこだわりがない場合は1つの役割だけ指定し、募集人数の値だけ埋めてギャザリングの作成を行うことになります。

マッチメイキングの実行

すでに存在するギャザリングの中で自分が参加可能なギャザリングを見つけて参加を行います。 マッチメイキングAPIは一定時間経過しても参加可能なギャザリングが見つからず、また全てのギャザリングの走査が終わっていない場合は コンテキストトークン を応答します。 コンテキストトークン はマッチメイキング処理を再開するために必要となる情報を文字列化したもので、マッチメイキングAPIの呼び出し時に指定すると、マッチメイキング処理を再開できます。 最後まで処理を実行したにも関わらず参加可能なギャザリングを見つけられなかった時には null を返しますので、その応答をもってギャザリングの作成処理を行うのがいいでしょう。

マッチメイキングリクエスト例(Unity)

yield return gs2.Matchmaking.DoMatchmaking(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.GatheringId); // string ギャザリング
            Debug.Log(r.Result.Item.Name); // string ギャザリング名
            Debug.Log(r.Result.Item.AttributeRanges); // list[AttributeRange] 募集条件
            Debug.Log(r.Result.Item.CapacityOfRoles); // list[CapacityOfRole] 参加者
            Debug.Log(r.Result.Item.AllowUserIds); // list[string] 参加を許可するユーザIDリスト
            Debug.Log(r.Result.Item.Metadata); // string メタデータ
            Debug.Log(r.Result.Item.CreatedAt); // long 作成日時
            Debug.Log(r.Result.Item.UpdatedAt); // long 最終更新日時
            Debug.Log(r.Result.MatchmakingContextToken); // string マッチメイキングの状態を保持するトークン
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  ネームスペース名
    player,   //  自身のプレイヤー情報
    matchmakingContextToken   //  検索の再開に使用する マッチメイキングの状態を保持するトークン(オプション値)
);

マッチメイキングの中断

すでにいずれかのギャザリングに参加した状態で、ギャザリングから離脱したいときに使用します。

マッチメイキングキャンセル例(Unity)

yield return gs2.Matchmaking.CancelMatchmaking(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.GatheringId); // string ギャザリング
            Debug.Log(r.Result.Item.Name); // string ギャザリング名
            Debug.Log(r.Result.Item.AttributeRanges); // list[AttributeRange] 募集条件
            Debug.Log(r.Result.Item.CapacityOfRoles); // list[CapacityOfRole] 参加者
            Debug.Log(r.Result.Item.AllowUserIds); // list[string] 参加を許可するユーザIDリスト
            Debug.Log(r.Result.Item.Metadata); // string メタデータ
            Debug.Log(r.Result.Item.CreatedAt); // long 作成日時
            Debug.Log(r.Result.Item.UpdatedAt); // long 最終更新日時
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  ネームスペース名
    gatheringName   //  ギャザリング名
);

GS2-Mission

ミッション機能を提供するサービスです。

ミッション機能は「n回 クエストをクリアする」「n体のキャラクターを入手する」のような条件を設定し、達成することで報酬が得られる仕組みです。 これを実現するために ”ミッションの条件設定” ”カウンターの購読” ”カウンターの上昇” という仕組みを使います。

ミッションの条件設定とは、ミッションが達成されたことになるカウンターの閾値を設定するものです。

カウンターの購読とは、ミッションの対象となるカウンターの設定です。 1つのカウンターは複数のミッションから購読することができ、ミッションごとに異なるリセットタイミングを設定できます。 たとえば、「クエストを通算1000回クリアした」というミッションと、「クエストを今日10回クリアした」というデイリーミッションを作りたい場合、 クエストをクリアするごとにカウントアップされる1つのカウンターを両方のミッションから購読し、前者には 'リセットなし' 、後者には '毎日' のリセットタイミングを設定することで、1回のカウンター操作で複数の条件のミッションを処理することができます。

カウンターの上昇 は文字通りカウンターを上昇させる操作で、例えばクエストをクリアしたときにクエストのクリア回数のカウンター上昇を報酬に含めて使用します。

ミッションには2つの期間を設定できます。 1つ目はカウンターを操作できる期間で、カウンターを上昇させるのをイベント開催期間に限定するようなケースで使用します。 2つ目はミッション達成報酬の受け取り可能期間で、イベントが終わった後もしばらくミッションの報酬だけは受け取れるようにしたい場合は2つの期間を別に設定して使用します。 期間の指定には GS2-Schedule のイベントを関連付けます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一カウンターに対する操作は1秒間に3回までにおさえてください。 異なるユーザや、同一ユーザでも異なるカウンターに対する処理であればこの制限はありません。

基本的な使い方

TBD

GS2-Money

課金通貨を管理するサービスです。

課金通貨とは、日本の資金決済法に定義された、前払式支払手段 (自家型) に該当するゲーム内通貨を指します。 資金決済法に準拠するために、GS2-Money では、残高や通貨のトランザクションの管理を行います。

異なるプラットフォーム間でウォレットを共有したくない場合、アクセス元のプラットフォームによって同一アカウント内のウォレットを分ける仕組みも提供します。 その場合でも、無償提供分は、異なるプラットフォーム間で共有することができます。

データ構造

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

通常のAPI利用料金に加えて流通額に対するレベニューシェアが適用されます。

月間流通額の範囲 レベニュー
¥0 〜 ¥999,999 10%
¥1,000,000 〜 ¥9,999,999 8%
¥10,000,000 〜 ¥99,999,999 7%
¥100,000,000 〜 ¥999,999,999 5%
¥1,000,000,000 〜 ¥100,000,000,000 3%

レベニューシェアに対しては通常の無料枠の適用はありませんが、月間の流通額が 10,000円 に満たない場合は例外として利用料金を頂戴しません。

制限・制約

同一ウォレットに対する操作は1秒間に3回までにおさえてください。 異なるユーザや、同一ユーザでも異なるウォレットに対する処理であればこの制限はありません。

基本的な使い方

ゲームの収益化の方法は大きく3種類あります。

この中でもアプリ内課金を使って収益化を実現するために必要となる機能を提供するのが GS2-Money です。 GS2-Money はゲーム内で課金通貨を扱うために必要な機能が一通り実装されています。

課金通貨とは

課金通貨は、ゲーム内通貨の中でも、現金を使って購入できる通貨を指します。 現金を使って購入した通貨は、日本国内において 資金決済に関する法律 通称 資金決済法 の制約を受け、その規定に従った管理をおこなう必要があります。

資金決済法

電子決済に関する法律で、ゲーム内の通貨は 前払式支払手段 に該当することが一般的です。 前払式支払手段 には 自家型第三者型 がありますが、自社で発行した通貨を自社で使用する場合は 自家型 となり、ゲーム内の通貨は 前払式支払手段(自家型) に該当することが一般的です。 ※ 実際自分のタイトルのゲーム内通貨がどの方式に該当するかは法律の専門家にご相談ください。

GS2-Money の機能

GS2-Money は『前払式支払手段(自家型)』の法的要件を満たすよう設計されています。 具体的には以下の機能を有しています。

なお、 GS2-Money は課金通貨の無料付与分も統合的に管理できます。これにより、通貨の消費時には、ユーザーが有料で購入した通貨と、無料で入手した通貨を、自動で合算して使用することが可能です。

通貨の対現金単価

GS2-Money では、通貨の入手ごとに、現金に対する単価を管理します。 たとえば、200円でゲーム内通貨を 100 入手したあと、100円でゲーム内通貨を 100 入手した場合、 GS2-Money では対現金単価 1.5 のゲーム内通貨を 200 持っていると扱うのではなく、対現金単価 2 のゲーム内通貨と対現金単価 1 のゲーム内通貨をそれぞれ 100 持っているものとして管理します。無料入手分は、対現金単価 0 として扱われます。 また、ゲーム内通貨の消費時にも、どの対現金単価のゲーム内通貨がどれだけ消費され、どの対現金単価のゲーム内通貨がどれだけ残っているかが管理されます。

通貨の消費優先度

GS2-Money の内部では対現金単価の異なるゲーム内通貨が区別して管理されますが、消費の際に個別の所持量を意識する必要はありません。 たとえば、対現金単価 1 のゲーム内通貨だけで 100 持っている場合でも、対現金単価 1 と 2 のゲーム内通貨をあわせて 100 持っている場合でも、その違いを意識せずにそこからゲーム内通貨を 100 消費することができます。 異なる対現金単価のゲーム内通貨を複数持っている場合に、どこから優先して消費するかは、以下のポリシーから選択できます。

レシートの記録

GS2-Money は決済プラットフォームごとのレシートを検証し、使用済みのレシートを記録する機能が用意されています。 現在対応しているプラットフォームは以下です。

近日対応予定のプラットフォームは以下です

通貨の入手

セキュリティに関する注意事項

課金通貨の入手はきわめてセンシティブな操作です。 なぜなら、ゲーム性やゲーム内経済に影響を与えるだけでなく、現金的価値を有し、その管理について資金決済法の制約を受けるユーザ資産を増加させる操作だからです。 課金通貨の入手に関してクライアントから任意の操作を受け付けることは、その現金的価値を不正に増殖させることになりかねません。

そこで、GS2 ではスタンプシートの報酬としてしか課金通貨の入手を行えないようにしています。 スタンプシートの報酬の内容はサーバに定義を持ち、また報酬を得る前にゲーム内ストアでの購入処理などをおこなったことをスタンプシートの仕組みが保証するため、課金通貨の不正な増殖を防げます。 したがって、課金通貨の入手には、ゲーム内ストアでの購入や、クエストの報酬として入手するような報酬設計が必要です。

通貨の消費

通貨の消費例(Unity)

yield return gs2.Money.Withdraw(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.Slot); // integer スロット番号
            Debug.Log(r.Result.Item.Paid); // integer 有償課金通貨所持量
            Debug.Log(r.Result.Item.Free); // integer 無償課金通貨所持量
            Debug.Log(r.Result.Item.UpdatedAt); // long 最終更新日時
            Debug.Log(r.Result.Price); // float 消費した通貨の価格
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  ネームスペースの名前
    slot,   //  スロット番号
    count,   //  消費する課金通貨の数量
    paidOnly   //  有償課金通貨のみを対象とするか
);

GS2では通貨の消費に関しても GS2-ShowcaseGS2-Exchange を利用して対価の交換と報酬の付与で表現することを推奨しています。 しかし、サーバで管理しない報酬を付与する場合にも利用できるよう、通貨の消費はAPIで実行できるようになっています。

二次通貨の管理

GS2-Money は二次通貨の取り扱いにも対応しています。 二次通貨とは、課金通貨と引き換えに入手したアイテムのことで、資金決済法の解釈によっては、これらも、直接現金と引き換えに入手した課金通貨に準じた扱いを求められる可能性があります。 たとえば、100円相当の課金通貨でスタミナドリンクを購入した場合、そのスタミナドリンクもまた、継承的に100円相当の価値を持つ課金通貨であるとも考えられるためです。 GS2 では、 GS2-Showcase や GS2-Exchange で対価に GS2-Money の課金通貨(上記の例では、ゲーム内通貨)を設定し、報酬にも GS2-Money の課金通貨(上記の例では、スタミナドリンク)を設定すると、課金通貨の種類をまたいでその価値を引き継げるようになっています。

TIPS

現金で課金通貨を購入する
月額サブスクリプションを実現する

GS2-News

お知らせ機能を実現するサービスです。

お知らせで配信する記事データは Hugo(https://gohugo.io/) で作成されている必要があります。 GS2-News に Hugo のサイトデータ一式を zip で圧縮したデータをアップロードするか、GitHub のリポジトリを指定して反映します。 サイトデータは全体で100MB以内に収める必要があります。

GS2-News は受け取ったサイトデータから /content/{sectionName}/{contentName}/index.html を走査します。 index.* の Front Matter を解析し、公開期間などの情報を抽出します。そのため、サイトデータのツリー構造はこの形式に従っている必要があり、 コンテンツの上部に記述する Front Matter は必ず yaml 形式で記述する必要があります。

Front Matter にはいくつかのGS2に向けた制御構文を記述することができます。 詳細は後述しますが、コンテンツの公開条件を Front Matter の制御構文で定義した場合、GS2-News は公開条件を満たさないコンテンツを削除してサイトをビルドします。 GS2-News は公開条件が設定されたコンテンツの公開・非公開状態のすべてのパターンを事前にビルドし、Webサーバに配置します。 そのため、公開条件が設定されたコンテンツが多数ある場合、組み合わせ爆発が発生し更新にかかる時間や費用が増大します。 公開条件が設定されたコンテンツが最小になるように工夫をしてください。

ビルドが行われるパターン数はサイトデータのアップロード時の戻り値で確認することができます。

クライアントが自身にとって現在有効なサイトデータを取得する方法として複数の方法が提供されます。

サイトのビルドには 『-D, --buildDrafts』 オプションは指定しません。 たとえ、サイトデータ内にコンテンツがあったとしても Front Matter で draft として宣言されたコンテンツは配信されません。

Front Matter に記述可能な制御構文

公開期間の設定

x_gs2_scheduleEventId: ${GS2-Schedule のイベントGRN}

と記述することで、そのコンテンツは該当イベントの開催期間にしか公開されなくなります。 イベントに連動したコンテンツの配信に利用できます。

また、GRNには {region}{ownerId} のプレースホルダを使用でき、それぞれ実行中のリージョン・オーナーIDに置換されます。

grn:gs2:{region}:{ownerId}:schedule:schedule-0001:event:event-0001

サンプル

https://github.com/gs2io/gs2-news-sample

データ構造

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

通常のAPI利用料金に加えて

サイトビルド 1パターンにつき 1円 サイトデータダウンロード容量 1GB あたり 30円

制限・制約

同一ウォレットに対する操作は1秒間に3回までにおさえてください。 異なるユーザや、同一ユーザでも異なるウォレットに対する処理であればこの制限はありません。

基本的な使い方

TBD

GS2-Quest

クエストの進捗管理をします。

クエストには開始するために必要な対価、クエストに成功したときの報酬、クエストに失敗したときの報酬 を設定できます。

クエストを開始するために必要な対価にはスタミナを消費する処理や、回数制限のカウンター上昇などを設定します。

クエストに成功したときの報酬は複数設定でき、クエスト開始時に抽選する際に使用する確率を設定できます。 これを使用することで、クエストとしては同じだが、通常コンテンツとレアエネミーが出現するコンテンツを出し分けることができます。 クエスト成功時の報酬には、キャラクターの経験値やアイテム、ゲーム内通貨などを設定します。

クエストに失敗したときの報酬とは、クエストに失敗したときに開始時に受け取った対価を返却する場合などに設定します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

クエストとはゲーム内のストーリー進行や報酬付きタスクを実現するための機能であり、GS2 ではこの機能を実現するために GS2-Quest を提供しています。

GS2-Quest で提供するのは、具体的には以下の保証です。

以下の内容については、 GS2-Quest は関知しません。

クエストモデルの登録

GS2-Quest では、クエスト内容の設定をサーバサイドで管理します。 そのため、この機能を利用するには、事前に GS2-Quest にマスターデータをアップロードしておく必要があります。

クエストのマスターデータの設定項目(GS2-Quest にはどんな機能があるのか)については、以下のドキュメントを参考にしてください。

前提クエスト

ゲーム仕様上、クエストにはシーケンシャルなアクセスを求めることが多くあります。 たとえば、2章をプレイするには1章をクリアしていなければならない、というものです。

このように、あるクエストを開始するのに、事前にクリアしている必要があるクエストを 前提クエスト と呼びます。 GS2 では、各クエストに対して前提クエストを設定でき、前提クエストのクリアフラグが立っていなければクエストを開始できないようにすることができます。

クエストの挑戦対価

クエストに挑戦するために対価を必要とするよう設定できます。 対価にはたとえば スタミナクエスト挑戦権利アイテム を設定でき、それらが足りない状態ではクエストを開始できないようにすることができます。

クエストの報酬

クエストの報酬には大きく分けて2種類が設定できます。

クリア報酬は文字通りのクエストをクリアしたときに得られる報酬です。 失敗報酬とは、クエストに失敗したときに得られる報酬で、一般的には挑戦に使用したスタミナを払い戻すような機能として用いられます。

クリア報酬のランダム化

毎回同じクリア報酬では少し味気ないかもしれません。 GS2-Quest では、複数のクリア報酬を設定し、ランダムに選択させることができます。 その際の確率は GS2-Lottery と同じく重みベースの確率設定が行えます。

クエスト報酬データにはメタデータを保存する領域も付随します。 たとえば、クリア報酬のメタデータにモンスターの出現パターンを載せておき、クリア報酬に応じたモンスターが出現するように設定するようなこともできます。

クエストの開始

クエストの開始例(Unity)

yield return gs2.Quest.Start(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.StampSheet); // string クエストの開始処理の実行に使用するスタンプシート
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  カテゴリ名
    questGroupName,   //  クエストグループ名
    questName,   //  クエストモデル名
    force,   //  すでに開始しているクエストがある場合にそれを破棄して開始するか(オプション値)
    config   //  スタンプシートの変数に適用する設定値(オプション値)
);

クエストを開始すると、内部では複数登録されたクエスト報酬から抽選を行い、報酬の内容を確定します。 つまり、GS2-Quest を使う場合、クエストを開始する時点でクリア時に得られる報酬が確定します。 これは クエストを開始後、完了するまでの間にアプリを強制終了 するなどして報酬の再抽選を行う不正行為が行えないようにするための仕様です。

クエストの開始処理のレスポンスには スタンプシート が応答されます。スタンプシートを実行するまでクエスト開始処理は保留されます。

クエストの完了

クエストの完了例(Unity)

yield return gs2.Quest.End(
    r => {
        if (r.Error != null)
        {
            // エラーが発生した場合に到達
            // r.Error は発生した例外オブジェクトが格納されている
        }
        else
        {
            Debug.Log(r.Result.Item.ProgressId); // string クエスト挑戦
            Debug.Log(r.Result.Item.TransactionId); // string トランザクションID
            Debug.Log(r.Result.Item.QuestModelId); // string クエストモデル
            Debug.Log(r.Result.Item.RandomSeed); // long 乱数シード
            Debug.Log(r.Result.Item.Rewards); // list[Reward] クエストで得られる報酬の上限
            Debug.Log(r.Result.StampSheet); // string 報酬付与処理の実行に使用するスタンプシート
        }
    },
    gameSession,    // GameSession ログイン状態を表すセッションオブジェクト
    namespaceName,   //  カテゴリ名
    transactionId,   //  トランザクションID
    rewards,   //  実際にクエストで得た報酬(オプション値)
    isComplete,   //  クエストをクリアしたか(オプション値)
    config   //  スタンプシートの変数に適用する設定値(オプション値)
)

クエストの完了時には 実際にクエストで得た報酬 を指定できます。 指定しなかった場合は開始時に応答された報酬を全て入手します。ゲーム内で敵や宝箱を無視したことで報酬が減額される場合はここで数量を減らして報告してください。 開始時に決定された報酬より多くの報酬を得ようとしたり、得られない報酬を得ようとするとエラーになります。

クエストの完了報告のレスポンスには スタンプシート が応答されます。スタンプシートを実行するまでクエスト完了処理は保留されます。

GS2-Ranking

スコアやクリアタイムのランキング処理を実現します。

ランキングには全員が同じデータを参照するグローバルランキングと、 フレンド間やギルド内のランキングのようなユーザによって見えるランキングの内容の異なるスコープドランキングがあります。

グローバルランキングは、15分~24時間の間で指定した間隔でランキングの集計処理がおこなわれ、 ゲームユーザーからのリクエストは事前に集計された結果を応答します。 これによって、大量のゲームプレイヤーが居たとしてもレスポンスタイムは変わらないように設計されています。

スコープドランキングは各ゲームプレイヤーがスコアバケットをもち、購読しているゲームプレイヤーがスコアを登録するとスコアバケットにスコアが追加されます。 各ゲームプレイヤーは自分のスコアバケット内のスコアを使ってランキングを処理します。

グローバルランキング・スコープドランキング ともにユーザーID毎に保持できるスコアを1個にするか、複数保持できるかを設定できます。 ランキングを登録したときに最後に登録したスコアだけをランキング対象としたいか、登録したすべてのスコアをランキング対象としたいかで設定できます。 すべてのスコアをランキング対象とする場合、データ量が膨れ上がりやすくなります。その結果、GS2の利用料金にも影響を与えることとなります。 ゲームシステム上の大きな理由がなければ最後に登録された1つのスコアのみがランキング対象となるように設定することをお勧めします。

データ構造

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

通常のAPI利用料金に加えて

#### グローバルランキング

集計処理1回につき、ランキング対象スコア1万件あたり 2円

#### スコープドランキング

スコア登録1回につき、スコアの購読数10件あたり 0.01円

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

TBD

GS2-Realtime

対戦・協力プレイ機能を実現します。

ルームを作成すると、IPアドレス ポート 暗号鍵 の3つの情報が発行されます。 ルームに参加するには、発行された IPアドレス ポート に WebSocket で接続します。 その通信には発行された 暗号鍵 を使用して通信します。

同時接続数や通信頻度の制限はありません。

ルームのライフサイクル

作成

GS2-Realtime はルームの作成リクエストを受け付けてからサーバを起動します。 この時、ルームの数が規定数未満の場合は規定数に達するようホットスタンバイとなるルームを追加で起動します。 規定数の数はGS2によって自動的に調整されます。

つまり、リクエストを受け付けてからサーバを起動する コールドスタート 時と ホットスタンバイされたルームを利用する ホットスタート でルームの作成リクエストを受け付けてから実際に割り当てられるまでの時間に差が生じます。

コールドスタート時の待ち時間の目安は 40秒〜60秒 ホットスタート時の待ち時間の目安は3〜5秒です。

終了

起動されたルームは作成後5分間誰も接続に来なかった場合終了されます。 この終了処理はホットスタンバイのルームにも適用されますが、ホットスタンバイの起動時間は利用料金に加算されません。

ルームに参加するすべてのプレイヤーから無通信状態が1分間続いても自動的に終了されます。

ルームの最大起動時間は3時間です。作成から3時間が経過すると自動的に終了されます。

コールドスタートの発生条件

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

通常のAPI利用料金に加えて

ルームの起動時間1分あたりの利用料金と、データのアウトバウンド通信容量に対して費用が発生します。 ルームが1つであれば、ルームへの参加者が2人でも100人でも利用料金は変わりません。

正式サービス開始までにサーバのスペックの変更をできるようにします。 対戦人数が多い場合や、通信頻度が高い場合は高スペックなサーバを必要とする場合があります。

正式サービス開始後のマイルストーンとして、サーバサイドの実装を差し替える機能を提供予定です。 この機能が実装されると、サーバ側でロジックを定義できるようになり、パケットリレーよりも柔軟なサーバ機能を利用できるようになります。 この場合、サーバサイドのロジックの複雑さによって高スペックなサーバを必要とする場合があります。

現在利用可能なものは realtime1.nano のみです。

スペック 1分あたりの利用料金
realtime1.nano 0.04円
realtime1.micro 0.06円
realtime1.small 0.12円
realtime1.medium 0.24円
realtime1.large 0.5円

データ転送容量(アウトバウンド)

15円/1GB

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

TBD

GS2-Schedule

ゲーム内のイベントの予定を管理します。

このサービスを単独で使うことはなく、 GS2-Showcase や GS2-Quest などと連携して、イベント開催期間のみストアに陳列する商品やクエストを実現します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一トリガーに対する操作は1秒間に3回までにおさえてください。 異なるユーザや、同一ユーザでも異なるトリガーに対する処理であればこの制限はありません。

基本的な使い方

TBD

GS2-Script

ゲーム固有の仕様を実現するためのサービス。

GS2内の様々なイベントをトリガーとしてスクリプトを実行できます。 例えば、アカウントの登録をしたときのイベントをトリガーとして、アカウントの初期設定スクリプトを実行したり、 プレイヤーのレベルアップイベントをトリガーとして、スタミナを回復するスクリプトを実行したり、 ゲームの仕様を標準のGS2の機能で実現できないときにスクリプトを使用して機能を拡張できます。

データ構造

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

通常のAPI利用料金に加えて

- スクリプトの実行時間1秒あたり 0.005円

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

TBD

GS2-Showcase

ゲーム内ストア機能を提供します。

販売する商品には対価と報酬を設定します。対価にも報酬にも複数のリソースを設定できます。

ステップガチャや、初回のみディスカウントといった仕組みを実現するために、商品グループという仕組みがあります。 商品グループは、商品ごとに優先度を付けて複数の商品をまとめる仕組みで、購入可能な条件を満たし、かつもっとも優先度の高い商品が陳列されます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

TBD

GS2-Stamina

現実時間経過によって回復するスタミナ値を実現します。

スタミナには回復間隔と回復量を設定できます。 スタミナには2段階の最大値を設定できます。 1つ目は時間経過による自然回復の最大値。2つ目はUIの都合による最大値です。

GS2では、レベルアップ時にスタミナを全回復するときに公平性を期するため、自然回復の最大値を超えて回復できるようにしています。 一方で、これを無制限に許可してしまうと、UIに破綻をきたしてしまう可能性があります。 そのため、二段階目の最大値を設けており、仮にこちらを超えるようなことが会っても GS2-Distributor を使用していればプレゼントボックスにスタミナ回復リソースを届けられるようになっています。

この仕組みを応用すると、街づくりゲームの採集機能も実装できます。 スタミナモデルに建築物を設定し、スタミナ値を建築物の生産量をとして表現します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一スタミナに対する操作は1秒間に3回までにおさえてください。 異なるユーザや、同一ユーザでも異なるスタミナモデルに対する処理であればこの制限はありません。

基本的な使い方

TBD

GS2-Version

ゲームのバージョンチェックを実現します。 バージョンチェックを通過すると、新しいプロジェクトトークンを取得できます。 つまり、ゲームに組み込むクレデンシャルはバージョンチェックを行うのに十分な権限のみ付与し、バージョンチェックを通過することでゲームを動作させるのに十分な権限のプロジェクトトークンを取得できるようにすることで、バージョンチェックを通らなければゲームが遊べない状態を作り出します。

ネームスペースには最大10個のバージョンモデルを宣言でき、すべてのバージョンが要求バージョン以上のときにのみバージョンチェックを通過とみなします。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

TBD

用語集

一般

GRN のフォーマット

grn:gs2:{リージョン}:{オーナーID}:{サービス名}:{モデル名}:{代替キー}:{モデル名}:{代替キー}...

リージョンの種類

ap-northeast-1 = 日本

ポリシードキュメントのフォーマット
Actions に呼び出せるアクションを羅列し、Resources に操作出来る対象リソースを羅列する
Actions / Resources には末尾にのみワイルドカードを使用出来る

{
    'Version': '2016-04-01',
    'Statements': [
        {
            'Effect': 'Allow',
            'Actions': [
                'Gs2Account:Create*',
            ],
            'Resources': ['*'],
        },
    ]
}
用語 意味
GRN Gs2 Resource Name
GS2 が扱うすべての情報に付与される一意なリソース名
サービス名 GS2 のマイクロサービスの種類
リージョン GS2 が動作しているデータセンターの場所。
アカウントID GS2 のアカウントを一意に識別するためのID
プロジェクトID ゲームを一意に識別するためのID。アカウントIDに複数紐づけて管理
オーナーID {アカウントID}${プロジェクトID}
GS2内のデータは基本的にこの情報に紐づけて保存される
ユーザID ゲームプレイヤーを識別するID
アクセストークン GS2-Auth によってゲームプレイヤーの認証がおこなわれた証明として払い出されるトークン
各マイクロサービスが利用者のユーザIDを特定する為に使用される

有効期限が設定されており、発行から10時間で再取得が必要となる。
プロジェクトトークン クレデンシャル を使用して取得する一時的な認証トークン。
Open ID Connect ベースで実装されており、Open ID Connect 的に言うと IDトークン。

有効期限が設定されており、発行から10時間で再取得が必要となる。
セキュリティポリシー GS2-Identifier のユーザに紐付けて管理される権限
クライアントIDを使用してどのAPI操作を行えるかを定義する
ポリシードキュメント セキュリティポリシー の権限設定を定義した JSON ドキュメント
モデル GS2 内のエンティティの型
エンティティ モデルを実体化したもの
代替キー エンティティのGRN以外のエンティティ固有の値を格納したフィールド名
GS2-Account の Game: gameName, Account: userId, TakeOver: type, userIdentifier
アクション API の種類を表す識別子 Gs2Inbox:CreateInbox のように定義され、前半部はサービスを表し、後半部はアクションの種類を表す。
プロパティID ゲーム内のキャラクターやアイテムを一意に識別するためのID
リソースGRN ゲームプレイヤーの所持物のGRNの総称
GS2-Inventory の所持物や、GS2-Experience の経験値情報、GS2-Stamina のスタミナ値など
リクエストID GS2 に対するAPIリクエスト毎に割り当てられるID
そのAPIより派生した GS2-Script 内のAPIアクセスなどは同じリクエストIDが共有される

リクエストID によって一連の処理の流れを追跡することが可能となり、
トランザクション処理においては、同一処理系によるデッドロックが発生することを避けることができる
コンテキスト GS2 のリクエストIDに割り当てられたスタック領域の名前
あるいは同一リクエストIDの処理系におけるスタック領域の状態。
スタック領域 リクエストIDが同一の処理系において処理がチェインする毎に積み重ねるメモリ空間

GS2-Account

用語 意味
アカウント認証情報・署名 GS2-Account によって認証が成功したときに発行される情報。署名をあわせて利用することで改ざん検出が可能
引き継ぎ設定 GS2-Account が発行したユーザIDとパスワードを復元するための設定。引き継ぎを実行するために必要となる任意の値を設定可能なユーザID・パスワードを保持する

GS2-Identifier

用語 意味
クライアントID API の利用者を識別する為のID
GS2-Identifier のユーザに紐付けて管理される
クライアントシークレット クライアントID が正規の利用者によって使用されていることを識別するためのキー
クレデンシャル クライアントID と クライアントシークレット の組み合わせ

GS2-Money

用語 意味
課金通貨 資金決済法の前払式支払手段(自家型)に該当するゲーム内通貨

GS2-Matchmaking

用語 意味
ギャザリング マッチメイキングによって作成されたプレイヤーの集合
ロール マッチメイキング時の条件に使用するプレイヤーの役割(タンク・ヒーラーなど)

サービス間の依存関係

GS2-Account

依存するサービス 依存する理由 依存する条件
GS2-Key::Key 認証結果の署名発行 必ず
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Auth

依存するサービス 依存する理由 依存する条件
GS2-Key::Key GS2-Account の認証結果の署名検証 GS2-Account の認証結果を使って アクセストークンの発行 をしようとしたとき

GS2-Chat

依存するサービス 依存する理由 依存する条件
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Distributor

依存するサービス 依存する理由 依存する条件
GS2-Identifier::User 報酬付与を実行する際に実行権限を設定したユーザが必要 必ず
GS2-Inbox::Namespace 報酬が溢れたときに GS2-Inbox に届けるのに必要 GS2-Inventory/GS2-Stamina への報酬付与を実行した場合に依存
その他すべて スタンプシートの実行のため スタンプシートの対価や報酬に設定されたマイクロサービスに依存

GS2-Exchange

依存するサービス 依存する理由 依存する条件
GS2-JobQueue::Namespace 報酬が複数存在する場合に報酬の付与に使用 報酬を複数設定した場合に依存
GS2-Key::Key スタンプシートの署名発行 必ず
その他すべて スタンプシートの発行のため スタンプシートの対価や報酬に設定するマイクロサービスに依存

GS2-Experience

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートで報酬を付与する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Formation

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートで報酬を付与する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Friend

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートで報酬を付与する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-Gateway::Namespace フレンドリクエスト通知 常時接続通信セッションを利用してプッシュ通知を受ける設定をした場合に依存

GS2-Gateway

依存するサービス 依存する理由 依存する条件
Firebase::FCM モバイルプッシュ通知を送信する プッシュ通知リクエストの際に対象ユーザがオフラインだった場合、モバイルプッシュに転送するオプションが有効な場合に依存

GS2-Identifier

依存するサービス 依存する理由 依存する条件

GS2-Inbox

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでメッセージを送信する場合に依存
GS2-Key::Key スタンプシートの発行 メッセージに報酬を添付する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-JobQueue::Namespace 報酬が複数存在する場合に報酬の付与に使用 メッセージに複数の報酬を設定した場合に依存
GS2-Gateway::Namespace 新着メッセージ通知 常時接続通信セッションを利用してプッシュ通知を受ける設定をした場合に依存

GS2-Inventory

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでアイテムを付与する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-JobQueue

依存するサービス 依存する理由 依存する条件
GS2-Gateway::Namespace ジョブが登録された通知 常時接続通信セッションを利用してプッシュ通知を受ける設定をした場合に依存

GS2-Key

依存するサービス 依存する理由 依存する条件

GS2-Limit

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでカウンターを加算する場合に依存

GS2-Lock

依存するサービス 依存する理由 依存する条件

GS2-Lottery

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートで抽選する場合に依存
GS2-Key::Key スタンプシートの発行 抽選する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-JobQueue::Namespace 報酬が複数存在する場合に報酬の付与に使用 報酬に複数の報酬を設定した場合に依存

GS2-Matchmaking

依存するサービス 依存する理由 依存する条件
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-Gateway::Namespace マッチメイキング進行通知 常時接続通信セッションを利用してプッシュ通知を受ける設定をした場合に依存

GS2-Mission

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでカウンターを操作する場合に依存
GS2-Key::Key スタンプシートの発行 達成報酬を受け取る場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-JobQueue::Namespace 報酬が複数存在する場合に報酬の付与に使用 報酬に複数の報酬を設定した場合に依存
GS2-Schedule::Event ミッション開催期間を設定 開催期間のあるタスクを設定した場合に依存

GS2-Money

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートで残高を加算する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Quest

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでクリアフラグを操作する場合に依存
GS2-Key::Key スタンプシートの発行 クエスト開始・完了処理を実行する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-JobQueue::Namespace 報酬が複数存在する場合に報酬の付与に使用 報酬に複数の報酬を設定した場合に依存
GS2-Schedule::Event クエスト公開期間を設定 公開期間のあるクエストを設定した場合に依存

GS2-Ranking

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでスコアを登録する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Realtime

依存するサービス 依存する理由 依存する条件
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Schedule

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 相対期間イベントトリガーを実行する場合に依存

GS2-Script

依存するサービス 依存する理由 依存する条件
すべて スクリプト内から GS2-SDK を利用可能なため スクリプト内から GS2-SDK を利用した場合に依存

GS2-Showcase

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの発行 商品購入処理を実行する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-JobQueue::Namespace 報酬が複数存在する場合に報酬の付与に使用 報酬に複数の報酬を設定した場合に依存
GS2-Schedule::Event 商品の販売期間を設定 販売期間のある商品を設定した場合に依存

GS2-Stamina

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでスタミナ値を操作する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Version

依存するサービス 依存する理由 依存する条件
GS2-Key::Key クライアントから受け取ったバージョン情報が妥当か署名検証をするのに使用 クライアントからバージョン情報を受け取り、署名検証を有効にした場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存