GS2 Blog

Game Server Services(https://gs2.io/) の最新情報をお届けします

【サービス拡張】GS2-ConsumableItem のエンハンス

みなさん、こんにちは。GS2の丹羽です。

今日は GS2-ConsumableItem についてお知らせします。

マスターデータ管理がJSONに対応

GS2-ConsumableItem はリリースから1年以上が経過しており、
最近では基本的な設計として利用しているJSONによるマスターデータ管理に対応していませんでした。

本日 GS2-ConsumableItem はJSONによるマスターデータ管理に対応し、
開発環境と製品環境でマスターデータをやりとりする手間が少なくなりました。

現在 GS2-ConsumableItem を利用されている皆様のデータは自動的にJSON形式でのマスターデータ管理にマイグレーションされています。

有効期限付の消費型アイテムに対応

これまで GS2-ConsumableItem の消費型アイテムは有効期限を設定出来ませんでした。
今回のアップデートで消費型アイテムに有効期限が設定出来るようになり、今日限定スタミナ回復アイテム や 今月限定ガチャチケット のようなアイテムを用意出来るようになりました。

ご利用中の消費型アイテムに関しては、有効期限無しの状態で設定し反映しています。

また、この変更に伴っていくつかクォーターの消費量に関して注意事項があります。

DescribeInventory で取得する際に同一アイテムが複数返る可能性があるように

1つのアイテムでも有効期限が異なるアイテムを持てるようになります。
その結果、DescribeInventory で取得する際に同一アイテムが複数返ることがあり得るようになります。

GetInventory でインベントリを取得する際に expireAtの指定が必要に

クォーターの消費量がわかりにくくなるため、おすすめはしませんが expireAt 自体は必須ではありません。
指定しない場合全ての有効期限のアイテムがマージされた数量が応答されるようになります。なお、その際マージ前のインベントリの数分クォーターを消費します。

ConsumeItem で消費するアイテムの種類だけでなく、どの expireAt のインベントリから消費するかを指定できるように

こちらも指定は必須ではありませんが、指定することで特定の有効期限のアイテムを消費出来ます。
ユーザに明示的に有効期限が異なる同一アイテムを並べて、ユーザの意思で消費するアイテムを選択して貰うUIを提供する場合は指定して使うことになります。

逆に、指定しない場合はどうなるか。というと、有効期限に近いアイテムから消費数量を満たせるだけ複数のインベントリからアイテムを消費します。
その際消費したインベントリの数分クォーターを消費します。

所持数量の上限は有効期限毎に効果を発揮する

わかりにくくて申し訳ありませんが、スケーラビリティ確保のために仕方が無かったとご理解いただきたいのですが
GS2-ConsumableItem はアイテム毎に所持数量の上限を設定出来ます。
しかし、有効期限毎にアイテムを別に管理する仕組みにした関係で、このアイテムの所持数量制限は有効期限が異なれば別の所持数量としてカウントされます。

今後もGS2は既存サービスも含めて機能拡張を続けて参ります。

それでは、また。

【登壇情報】TECH x GAME COLLEGE #2

みなさん、こんにちは。GS2の丹羽です。

今日は新しい登壇予定の情報をお知らせします。

【TECH x GAME COLLEGE #2】ゼロからサーバレスの先端に追いつこう
techxgamecollege.connpass.com

こちらで、サーバレスアーキテクチャに関する内容でお話ししたいと考えています。
内容としては、2016年からさまざまな形でサーバレスアーキテクチャに関する登壇をしてきましたが
それらを再編集してお話しすることで、まったくサーバレスアーキテクチャに関する知識のない方に、サーバレスアーキテクチャの最先端に追いついていただこう。という内容です。

ご興味があればエントリーしてみてください。

それでは、また。

【新サービス公開】GS2-Level の公開のお知らせ

みなさん、こんにちは。GS2の丹羽です。

本日は新しいサービス GS2-Level の公開をお知らせします。
GS2-Level は名前の通り経験値やレベルの実装をサポートするためのサービスです。

経験値・レベルというと、キャラクターのパラメータを一番に思い浮かべると思いますが、
昨今のスマホゲームではキャラクターだけでなく、プレイヤーレベル・スキルレベル・装備レベル・ギルドレベル・親愛度・建築物レベル など様々な要素で経験値・レベルという概念が使われています。
GS2-Level はそれら全てに対応出来るよう設計されており、今後 GS2 を使ってゲーム開発をする際に外せない機能になっていくことを期待しています。

GS2-Level の特徴

レベルテーブル

レベルとは経験値の積み重ねによって成長していく要素です。
つまり、経験値をレベルに変換するテーブルが必要となります。
GS2-Level もその概念を元としており、累積獲得経験値の値から現在のレベルを導き出すレベルテーブルを定義して使用します。

リソースタイプ

スマホゲームでは同じキャラクターという枠組みでもレアリティという要素でカテゴライズされるのがスタンダードです。
さらに、ゲームの味付けとしてレアリティが低いキャラクターは成長させやすいけれど、天井が低い。という味付けがされていることが多いです。

つまり、同じキャラクターでも複数のレベルテーブルがあるのが望ましい。ということになります。
GS2-Level はそれを表現するためにリソースタイプというものを用意しました。
キャラクターや装備にレベルテーブルを都度当てはめるのでは管理コストが高すぎると感じたため、リソースタイプに対してレベルテーブルを設定すれば、あとはキャラクターや装備のような細かなリソースの情報は GS2-Level への登録は不要としています。

レベルキャップ

スマホゲームはキャラクターの成長に軸足を置いているタイトルが多いですが、長期運営を考えればレベルの上限の引き上げは避けられません。
また、成長の楽しみをより演出するためであったり、ガチャのダブりに対する救済としてレベル上限の解放ができるシステムを実装していることも多々あります。

GS2-Level もレベルキャップに関する機能を実装しており、リソースタイプにはデフォルトのレベルキャップが設定出来るようになっています。そしてレベルキャップの変更APIを備えています。
これによって、デフォルトではレベル50を上限に成長しなくなるが、特殊なアイテムを使ったり、ダブったキャラクターを合成することでレベル上限が引き上げられ、最大レベル70まで成長させられる。というような実装が出来ます。

GS2-Level の使い方

例によって、GS2 SDK for Unity での実装例を示します。
GS2-Level で現在の経験値やレベルを取得するサンプルは以下です。

yield return gs2.Level.GetByResourceTypeAndResourceId(
    r =>
    {
        if (r.Error != null) throw r.Error;
        var status = r.Result;
        Debug.Log("ステータスのID: " + status.Id);
        Debug.Log("リソースタイプ名: " + status.ResourceType);
        Debug.Log("リソースのID: " + status.ResourceId);
        Debug.Log("レベル: " + status.Level);
        Debug.Log("リソースのレベルキャップ: " + status.LevelCap);
        Debug.Log("累計獲得経験値: " + status.Experience);
        Debug.Log("次のレベルになる累計獲得経験値: " + status.NextLevelExperience);
    },
    _session,             // GS2 ログインセッション
    ResourcePoolName,     // リソースプール名
    ResourceTypeName,     // リソースタイプ名
    ResourceId1           // リソースID(キャラクターのIDや装備のIDなど。事前にマネージメントコンソールによる登録は不要)
);

gs2.Showcase.GetByResourceTypeAndResourceId() で現在の経験値・レベルを取得できます。

ステータスのIDが分かっている場合は gs2.Level.Get() が使用出来ます。

yield return gs2.Level.Get(
    r =>
    {
        if (r.Error != null) throw r.Error;
        var status = r.Result;
        Debug.Log("ステータスのID: " + status.Id);
        Debug.Log("リソースタイプ名: " + status.ResourceType);
        Debug.Log("リソースのID: " + status.ResourceId);
        Debug.Log("レベル: " + status.Level);
        Debug.Log("リソースのレベルキャップ: " + status.LevelCap);
        Debug.Log("累計獲得経験値: " + status.Experience);
        Debug.Log("次のレベルになる累計獲得経験値: " + status.NextLevelExperience);
    },
    _session,                 // GS2 ログインセッション
    ResourcePoolName,         // リソースプール名
    StatusId                  // ステータスID
);

ステータスIDを複数指定してまとめて経験値・レベルを取得出来ます。
キャラクターに関する経験値・レベルが複数存在し、1画面を構成するのにまとめて取得したいときなどに活用出来ます。

yield return gs2.Level.List(
    r =>
    {
        if (r.Error != null) throw r.Error;
        var statuses = r.Result;
        Debug.Log("ステータスのID: " + statuses[0].Id);
        Debug.Log("リソースタイプ名: " + statuses[0].ResourceType);
        Debug.Log("リソースのID: " + statuses[0].ResourceId);
        Debug.Log("レベル: " + statuses[0].Level);
        Debug.Log("リソースのレベルキャップ: " + statuses[0].LevelCap);
        Debug.Log("累計獲得経験値: " + statuses[0].Experience);
        Debug.Log("次のレベルになる累計獲得経験値: " + statuses[0].NextLevelExperience);
    },
    _session,                        // GS2 ログインセッション
    ResourcePoolName,                // リソースプール名
    new []{ StatusId1, StatusId2 }   // ステータスIDリスト
);

ステータスIDが分からない場合はステータスIDのリストを省略すると、
ログインしているユーザが所有している全ての経験値・レベルを取得出来ます。

ただし、このリクエストはステータスIDを指定する場合の2倍のクォータを必要とします。

yield return gs2.Level.List(
    r =>
    {
        if (r.Error != null) throw r.Error;
        var statuses = r.Result;
        Debug.Log("ステータスのID: " + statuses[0].Id);
        Debug.Log("リソースタイプ名: " + statuses[0].ResourceType);
        Debug.Log("リソースのID: " + statuses[0].ResourceId);
        Debug.Log("レベル: " + statuses[0].Level);
        Debug.Log("リソースのレベルキャップ: " + statuses[0].LevelCap);
        Debug.Log("累計獲得経験値: " + statuses[0].Experience);
        Debug.Log("次のレベルになる累計獲得経験値: " + statuses[0].NextLevelExperience);
    },
    _session,                        // GS2 ログインセッション
    ResourcePoolName                 // リソースプール名
);

ここまで読んでいて、読み込みばかりで加算がないじゃないか。と思われたかもしれません。
GS2-Level はデフォルトでは GS2 SDK for Unity からは経験値の加算は行えない権限設定になっています。
これはチート対策のためであり、プログラムを改造したり、不正にGS2のサーバに通信をすることで意図しない形で経験値を加算する行為を避けるためにこのような仕様になっています。

現在も GS2-Script 経由であれば安全に経験値を加算することは出来ますが、GS2-Level が本領を発揮するのは
今後『クエスト』や『合成・強化』のようなサービスを提供開始したときになると思います。しばらくお時間をいただくことになりますが、
そう遠くない時期にこれらのサービスも追加していきます。

GS2-Level の利用料金

最後に料金についてですが、GS2-Level はサービスクラスによる時間課金+APIコール回数による課金となっています。

level1.nano 2円/時 10クォーター/秒
level1.micro 10円/時 50クォーター/秒
level1.small 20円/時 100クォーター/秒
level1.medium 60円/時 300クォーター/秒
level1.large 200円/時 1000クォーター/秒
level1.xlarge 400円/時 2000クォーター/秒
level1.2xlarge 800円/時 4000クォーター/秒

ステータスの取得 3クォーター/回
経験値の加算 5クォーター/回
レベルキャップの変更 5クォーター/回
ステータスの一括取得(ステータスID指定) 3クォータ/1件
ステータスの一括取得(ステータスID未指定) 6クォータ/1件

上記に加え、API呼び出し 1,000回あたり3円 が発生します。

月額1,500円で、秒間3回の読み込み/2回の書き込み + API呼び出し1000回あたり3円で利用出来るとイメージしてください。

それでは、また。

【新サービス】GS2-Gacha の公開と GS2-Showcase のエンハンスのお知らせ

みなさん、こんにちは。GS2の丹羽です。

GS2-Gacha を公開

本日は新しいサービス GS2-Gacha の公開をお知らせします。
GS2-Gacha は名前の通りガチャの実装をサポートするためのサービスです。

GS2-Gacha の特徴

抽選確率の管理

レアリティ毎の抽選確率を設定し、そのレアリティ内でリソース毎の抽選確率を設定出来ます。 これによって、レアリティ毎の排出確率を調整する手間が省け、運用がスムーズになります。
また、抽選確率は重みベースの確率設定が可能で、登録したりソースの抽選確率の合計が100%になるよう調整するような手間もありません。
これはたとえば、リソースAを1に、リソースBを4に設定すると、リソースAの排出率が20%。リソースBの排出率が80%として処理されることを意味します。

GS2に登録した抽選確率をクライアントに提供する機能も存在します。 これによって、実際の抽選確率とドキュメントの抽選確率に齟齬が発生するような運用上のミスを最小限に抑えることが可能です。
GS2-Gacha においては、ガチャと排出確率テーブルは別途管理されており、異なるガチャで排出確率テーブルを使い回すことが出来ます。
これによって、単発ガチャと10連ガチャで同じ排出確率テーブルを共有することが可能です。

10連ガチャでSR以上確定のような実装も可能

10連ガチャのような複数回抽選するガチャにおいて、抽選回数毎に異なる排出確率テーブルを設定出来ます。 これによって、10連ガチャでSR以上確定。のような設定が可能です。

GS2-Showcase との連携

GS2-Showcase と連携することで、ガチャの公開期間や抽選回数の制限を設けることが出来ます。

GS2-Showcase のアップデート

実装方法も非常に簡単です。その話をする前に GS2-Showcase のエンハンスの話をします。

商品の購入通貨に GS2-ConsumableItem を追加

これまで GS2-Showcase の対価には ストア決済 / GS2-Money / GS2-Gold が指定出来ました。今回のエンハンスによって GS2-ConsumableItem に対応しました。
GS2-ConsumableItem に対応したことで、ガチャチケットのような機能を実装出来るようになります。

購入対象商品に GS2-Gacha を追加

もう一つのエンハンスは、購入対象に GS2-Gacha が追加されたことです。
これによって、GS2-Showcase にガチャを陳列することが出来るようになり、ガチャの抽選処理まで GS2-Showcase が発行するスタンプシートで処理出来るようになりました。
この機能を使用する場合は、GS2-Gacha の採用にあたって新しく実装をする必要は無く、GS2-Showcase に GS2-Gacha の商品を陳列するだけで対応出来ます!

GS2-Showcase の実装方法については以下のエントリを参考にしてください。
gs2.hatenablog.com

GS2-Gacha の使い方

GS2-Gacha に登録した景品の排出確率を取得するサンプルは以下です。

レアリティ毎の排出確率を取得

    yield return gs2.Gacha.GetGachaProbability(
        result => {
            if(result.Error != null) throw result.Error;
            EzGachaProbabilityList probabilities = result.Result;
            foreach (var item : probabilities)
            {
                Debug.Log("レアリティ名: " + item.Name);
                Debug.Log("排出確率: " + item.Rate);        // 0.0f〜1.0f
            }
        },
        session,            // GS2 ログインセッション
        GachaPoolName,      // GS2-Gacha で作成したガチャプール名
        GachaName,          // GS2-Gacha で作成したガチャ名
        1                   // 何回目の抽選における排出確率を取得するか
    );

リソース毎の排出確率を取得

    yield return gs2.Gacha.GetGachaProbabilityDetail(
        result => {
            if(result.Error != null) throw result.Error;
            EzGachaProbabilityDetailList probabilities = result.Result;
            foreach (var item : probabilities)
            {
                Debug.Log("レアリティ名: " + item.RarityName);
                Debug.Log("リソースID: " + item.ResourceId);
                Debug.Log("排出確率: " + item.Rate);        // 0.0f〜1.0f
            }
        },
        session,            // GS2 ログインセッション
        GachaPoolName,      // GS2-Gacha で作成したガチャプール名
        GachaName,          // GS2-Gacha で作成したガチャ名
        1                   // 何回目の抽選における排出確率を取得するか
    );

ここからは GS2-Gacha を採用するけれど、GS2-Showcase を使用しない場合の実装例を示します。
特別な理由が無ければGS2としては GS2-Showcase 経由での利用を推奨します。

ガチャ一覧の取得

    yield return gs2.Gacha.ListGachas(
        result => {
            if(result.Error != null) throw r.Error;
            // ガチャリストの取得に成功
            EzGachaList gachas = result.Result;
            foreach (var item : gachas)
            {
                Debug.Log("ガチャの名前: " + item.Name);
                Debug.Log("メタデータ: " + item.Meta);
                Debug.Log("抽選回数: " + item.DrawCount);
            }
        },
        session,            // GS2 ログインセッション
        GachaPoolName       // GS2-Gacha で作成したガチャプール名
    );

ガチャの取得

    yield return gs2.Gacha.GetGacha(
        result => {
            if(result.Error != null) throw r.Error;
            // ガチャの取得に成功
            EzGacha gacha = result.Result;
            Debug.Log("ガチャの名前: " + gacha.Name);
            Debug.Log("メタデータ: " + gacha.Meta);
            Debug.Log("抽選回数: " + gacha.DrawCount);
        },
        session,            // GS2 ログインセッション
        GachaPoolName,      // GS2-Gacha で作成したガチャプール名
        GachaName           // GS2-Gacha で作成したガチャ名
    );

ガチャを抽選する

    yield return gs2.Gacha.DoGacha(
        result => {
            if(result.Error != null) throw result.Error;
            EzPrizeList prizes = result.Result;
            foreach (var item : prizes)
            {
                Debug.Log("排出されたリソースID: " + item.ResourceId);
            }
        },
        session,            // GS2 ログインセッション
        GachaPoolName,      // GS2-Gacha で作成したガチャプール名
        GachaName           // GS2-Gacha で作成したガチャ名
    );

GS2-Gacha の利用料金

最後に料金についてですが、GS2-Gacha は完全IO課金となっており、

API呼び出し 1,000回あたり3円 に加え、以下の操作をしたときに追加の費用が発生します。

ガチャ一覧/ガチャ情報の取得 1円/1,000回
抽選処理 3円/1,000回
ガチャマスタのエクスポート 1円/回

それでは、また。

【新サービス提供開始】GS2-Showcase がご利用いただけるようになりました

みなさん、こんにちは。GS2の丹羽です。

先日発表した GS2-Showcase の提供を開始しましたので、お知らせします。

gs2.hatenablog.com

いつも通り、GS2-Showcase を Unity から使う方法を軽く説明したいと思います。

まず、商品情報の登録が必要です。
先日のブログエントリでも説明したとおり、商品には「商品」と「商品グループ」があり
それらを「陳列商品」として陳列することでショーケースができあがります。

これらは GS2 のマネージメントコンソールで登録して、JSON形式の設定ファイルをエクスポートすることができます。
JSON形式の設定ファイルですので、独自の管理ツールを作成して、そちらで作ったファイルで運用することも可能です。

設定ファイルを「ショーケースマスター」としてアップロードすることで、実際に商品情報を反映出来ます。


商品情報が登録出来たら、次は Unity から商品一覧を取得してみます。

yield return gs2.Showcase.DescribeSalesItems (
    result => {
        if(result.Error != null) throw result.Error;
        // 商品リストの取得に成功
        foreach (var item : result.Result)
        {
            Debug.Log("商品の種類: " + item.ItemType);
            Debug.Log("数量: " + item.ItemAmount);
            Debug.Log("通貨の種類: " + item.CurrencyType);
            Debug.Log("販売価格: " + item.Price);
        }
    },
    session,            // GS2 ログインセッション
    ShowcaseName,       // GS2-Showcase で作成したショーケース名
);

このようなコードを書くと、先ほど登録した商品情報が取得出来ます。
その際に、GS2-Schedule や GS2-Script で公開期間を設定していた場合は、期間外の商品は取得出来ません。
また、商品グループとして陳列した商品は条件を満たす1つの商品だけが結果に含まれます。


次に、商品を購入してみます。

yield return gs2.Showcase.BuyItem (
    result => {
        if(result.Error != null) throw result.Error;
        // 商品の購入に成功
        var stampSheet = result.Result; // スタンプシート
    },
    session,            // GS2 ログインセッション
    ShowcaseName,       // GS2-Showcase で作成したショーケース名
    itemId,             // DescribeSalesItems() で取得した商品ID
    KeyName             // 購入処理で使用する GS2-Key 暗号鍵名
);

購入したい商品の 商品ID と、購入処理で使用する「スタンプシート」の暗号化に使う暗号鍵を指定します。
スタンプシートの詳しい解説は以下のブログエントリを参照してください。

gs2.hatenablog.com


商品を購入すると、スタンプシートが発行されます。
この時点では対価などは消費されていませんし、購入したアイテムも付与されていません。
スタンプシートのタスクを実行することで、対価が消費されたり、購入したアイテムが付与されることになります。

なので、この後はスタンプシートのタスクを実行します。

stampSheet.OnCompleteTasks += () =>
{
    Debug.Log("購入した商品の種類: " + stampSheet.Item.ItemType);
    Debug.Log("入手した数量: " + stampSheet.Item.ItemAmount);
}

yield return stampSheet.RunTask (
    result => {
        if(result.Error != null) throw result.Error;
        // 全てのタスクを完了
    },
    session,            // GS2 ログインセッション
    KeyName,            // 購入処理で使用する GS2-Key 暗号鍵名
    new BuyConfig
    {
        StaminaMaxValue = 50,
    }                   // 購入時に使用するオプション値
);

スタンプシートには RunTask メソッドがあり、そちらを呼び出すことでタスクを実行出来ます。
エラーが発生すると、指定したコールバック関数にエラーが返ります。
エラーを気にせず、再び RunTask メソッドを呼び出すと、失敗したところから処理を再開することが出来ます。
リトライ回数をカウントしつつ、一定回数失敗したら諦めるような実装を推奨します。

スタンプシートの RunTask メソッドの引数で指定するコールバックの他に OnDoneTask / OnCompleteTasks といったコールバックを登録して待ち受けることも出来るようになっています。
扱いやすい方で購入完了処理をハンドリングしてください。


最後に料金についてですが、GS2-Showcase は完全IO課金となっており、

API呼び出し 1,000回あたり3円 に加え、以下の操作をしたときに追加の費用が発生します。

商品一覧/商品情報の取得 1円/1,000回
購入処理 3円/1,000回
商品マスタのエクスポート 1円/回

それでは、また。

【インタビュー記事】GameWith Magazine さんにインタビューしていただきました

みなさん、こんにちは。GS2の丹羽です。

本日は GameWith Magazine さんにインタビューをしていただいた記事が公開されましたので、お知らせします。

magazine.gamewith.co.jp

私とゲームの出会いや、なぜ GS2 という事業を始めたのか、これからどうなりたいのか。といった点を記事にしていただきました。
よろしければ、ご覧ください。

それでは、また。

【登壇情報】CEDEC2018 に登壇します

みなさん、こんにちは。GS2の丹羽です。

本日は、国内最大のゲーム開発者カンファレンスであるCEDECでの登壇が決まったことをお知らせします。

https://2018.cedec.cesa.or.jp/session/12018.cedec.cesa.or.jp

『サーバレスアーキテクチャによるゲームサーバ開発』という題目で、サーバレスアーキテクチャとは何か?といった初歩的な内容から
サーバレスアーキテクチャをゲームに応用することでどのような恩恵が得られるかをお話しさせていただきたいと思います。

https://2018.cedec.cesa.or.jp/2018.cedec.cesa.or.jp

CEDECパシフィコ横浜 で 2018.08.22 (水) - 08.24 (金) の期間開催される予定です。
ご参加なさる際には、GS2のセッションにも是非足をお運びいただければ幸いです。

それでは、また。

(C) Game Server Services, Inc.