GS2 Blog

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

Vue + TS に触れて2週間でシングルページアプリケーションを作ってリリースした話

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

本日 Game Server Services は次世代版のベータテストを開始しました。
これまで提供していた GS2 を GS2 Classic と呼び、次世代版 GS2 を単に GS2 と呼びます。

gs2.io

GS2 のリリースにあたっては非常に多くの新しいチャレンジを行いました。
それについては少しずつブログや登壇を通じて皆さんにお伝えしていきたいと思います。

今回は、GS2 のマネージメントコンソールをシングルページアプリケーションとして作りました。
しかも、Vue + TypeScript に触れて2週間で。というトピックで振り返ってみようと思います。

なぜ Vue + TypeScript にいたったか

GS2 Classic のマネージメントコンソールは Google App Engine を使用していました。
弊社はフルサーバレスアーキテクチャでサービスを作ることに並々ならぬ努力を重ねてきました。
そのため、マネージメントコンソールを作るにあたっても採用できる技術は非常に限られています。

GS2 Classic を GAE で運用してみて感じた一番のツラミはコールドスタート時の スピンアップ速度 でした。
GS2 も GS2 Classic も Function as a Service(いわゆる AWS Lambda) を計算基盤として開発しています。
こちらもスピンアップ速度は課題にはなるのですが、弊社では AWS SDKOSS で提供されているものではなくよりフットプリントが小さく、高速に動作するものを用意して使用するなど、サービスのレスポンスタイムを短くする努力をしてきました。

一方で、そこまで大量にアクセスが来るわけではないマネージメントコンソールは開発リソースをあまり割けていないのと、アクセス数がサービスのエンドポイントより圧倒的に少ない。というダブルパンチでコールドスタート時の応答速度がストレスを感じるレベルになっていました。
やはり、開発効率重視で Web Application Framework(WAF) を使ったアプリケーションを GAE に乗せてしまったのは大きな過ちだったのだと思います。
コンテナの起動速度だけでなく、ランタイムに使用しているJava仮想マシン(GS2 Classic はサービスでは Python を使用していますが、マネージメントコンソールは Java(Kotlin) で作っています)。そして、WAFの起動とレスポンスタイムを遅くする要素が満載です。

FaaS でも WAF を使った開発をしようというアプローチもありますが、スピンアップ速度が遅くなるので私はおすすめしません。
GS2 Classic も GS2 も非常に薄いレイヤーで設計されており、スピンアップ速度も最小になるよう心がけています。

というわけで、GS2 のマネージメントコンソールを開発するにあたっては割と早期に
『HTML + JS でサービスと同じ REST API エンドポイントを使って提供する』ということを決めていました。

なぜ Vue なのか

React や Angular といった WAF もあります。その中で Vue を選んだ最大の理由は、学習コストが低そうだったことです。
そう、タイトルにもあるように開発にかけられる時間が非常に限られていたのです。

なぜ TypeScript なのか

単に型が厳密な言語が好みだからです。

開発期間は2週間なのはわかったけど、人数は?

基本的に1人。
しかも大量に他の仕事も兼務してる。実質5営業日くらいしかかけられない。

で、できたものは?

f:id:kazutomo:20190716232113p:plain
Bootstrap 臭いUIなのは気にしないで

f:id:kazutomo:20190716232131p:plain
Vue コンポーネントを活用して複雑なUIもパズルのように組み合わせて実現してます

f:id:kazutomo:20190716232142p:plain
いたって普通の入力フォームはこんな感じ

f:id:kazutomo:20190716232154p:plain
ビューによってはこんなとんでもない入れ子構造のデータもあるけど、Vueコンポーネントならなんとかなった

f:id:kazutomo:20190716232210p:plain
当然、入れ子構造は入力側でも…

ページ数・コンポーネント数は?

正確に数えてないけど、1マイクロサービスあたりページ数で10。
ビュー用のパネルや入力用のパネルのコンポーネント数で50〜80くらいあると思う。
そのマイクロサービスが30個くらいあります。ひいふうみい…。おう…。

物理的に無理じゃない?

感のいいひとは気づいてしまったでしょう。2週間で 1500〜2000 のコンポーネントを実装するとか無理ゲーだということに。

GS2 はマイクロサービスを DDL で定義していて、そこから色々なものを自動生成しています。
FaaS で動くサービスの雛形とか、サービスにアクセスするための各プログラミング言語向けのSDKとか。
TypeScript でアクセスするための SDK もそうやって作ってます。

@data_structure(
    parent=Namespace.Namespace,
    data_owner=DataOwner(
        owner_holder=DataOwner.OwnerHolderSelf,
        owner_type=GamePlayer(),
    ),
    primary_key=String('accountId'),
    alternate_keys=[
        AlternateKey(
            field_name='userId',
        ),
    ],
    description='''
    ゲームプレイヤーアカウント
    
    Account はゲームプレイヤーを特定するためのユーザIDとパスワードを記録します。
    ''',
)
class Account(BaseModel):

    accountId = GenericGrn(
        primary_key=True,
        require=True,
        updatable=False,
    )
    userId = UUID(
        description='アカウントID',
        require=True,
        primary_key=True,
    )
    password = String(
        description='パスワード',
        require=True,
        updatable=True,
        default=Default.Value(),
    )
    createdAt = CreateTimestamp()

モデルの定義はこんな感じ。

@result(
    items=Array(Account),
    nextPageToken=String(
        description='リストの続きを取得するためのページトークン',
    ),
)
class Describe(BaseDescribeAction):

    namespaceId = Namespace.Namespace.namespaceId
    pageToken = PageToken()
    limit = database.AccountDefaultLimit()

    @path(GET, '/{namespaceName}/account')
    @policy('DescribeAccounts')
    @description(
        '''
            {model_name}の一覧を取得
        '''
    )
    @request(
        (namespaceId, [Request.Default()]),
        (pageToken, [Request.Default()]),
        (limit, [Request.Default()]),
    )
    def describeAccounts(self):
        pass

メソッドの定義はこんな感じ。

で、マネージメントコンソールようにこんな DDL を作った。

@description('{model_name}の新規作成')
@path(GET, '/namespace/:namespaceName/account/:userId/take_over/create')
class CreatePage(BaseConsolePage):

    def __init__(self):
        super().__init__(
            parent=Account.Console.DetailPage,
            name='create_take_over',
            panels=[
                Console.Panel.CreatePanel,
            ],
        )

class CreateInputPanel(BaseGeneralInputPanel):
    def __init__(self):
        super().__init__(
            properties=TakeOver.Create.createTakeOver.request,
        )

class CreatePanel(BaseCreatePanel):
    def __init__(self):
        super().__init__(
            method=TakeOver.Create.createTakeOver,
            input_panel=Console.Panel.CreateInputPanel,
        )

CreatePage はどういうページを作るかを定義してあって、
CreateInputPanel はどういうフィールドの入力フォームを用意するかが定義してあって、
CreatePanel は入力パネルとその送信先メソッドを定義している。

CreateInputPanel と CreatePanel が分離しているのは CreateInputPanel を入れ子構造の入力フォームで再利用できるように Submit ボタンの挙動を分離している。

というわけで、すべてのモデルデータに対応するDDL宣言を追加していって ジェネレータで一気に Vue コンポーネント を生成するようにした。

つらかったこと

これだけページやパネルがあるとビルド時に node のメモリが不足してなかなか動かなかった。
結局、最終的に32GB割り当ててビルドすることになった。
GS2 ではメモリ 64GB のマシンをみんなに割り当ててるのでよかったけど、みんなどうしてるんだろう。

gs2.hatenablog.com

最後に

めっちゃ厳しいスケジュールの中、なんとかリリースできました。
サービスの内容は 先日の Game Tool & Middleware Forum で発表した資料をみてね。

speakerdeck.com

まだまだ突貫工事感あるので、少しずつ改善していきます。

あと、ベータテストのフィードバックを GitHub Issue で受け付けて対応予定とかも載せてるので、興味があれば見てみてください。

github.com

それでは、また。

【登壇資料】Cloud2.0時代のゲーム開発を支えるGS2 / 協業のお知らせ #gc_cloud

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

本日開催された Google Cloud INSIDE 7 の登壇資料を公開しましたので、お知らせします。

speakerdeck.com

本登壇の中で 第二世代GS2 の発表をさせていただきました。
第二世代のGS2 は現行世代の GS2 では利用料金が時間あたりの利用料金とAPIリクエストあたりの料金の二軸でしたが、完全リクエスト単価制に変更します。
この変更によって、あらゆる規模のゲームにフェアプライシングでサービスを提供できるようになると確信しています。

さらに、Googleの協力の下第二世代GS2の負荷テストを実施したことも報告させていただきました。
第二世代のGS2は現行世代よりも多くのマイクロサービスを実装した状態でこの夏にβテストを開始し、年内にグローバルで正式サービスを開始します。
マイクロサービスを組み合わせることで、パズドラ・モンストのようなゲームをサーバ開発・運用をすることなく提供できるようになります。
本日よりエントリーを受け付けていますので、GS2のサイトより申し込みください。

第二世代のGS2は現時点で秒間10万リクエストをさばけることを確認しており、
この数値はローンチ時にSNSで話題となり、AppStoreやGooglePlayでセールスランク1位となったスクウェア・エニックスさんのシノアリスの秒間アクセス数5.5万を遥かに上回る数値です。
正式サービス開始までにより高い数値を出せるようチューニングを続けてまいります。

あわせて、本日 クラウドエース株式会社 との協業についても報告できることとなりました。
クラウドエースは GCP のプレミアムパートナー で確かな技術を持った会社です。
GS2 は クラウドエース と戦略的業務提携をおこない、公式のGS2リセラーに認定いたしました。
クラウドエースを経由してGS2を利用する場合、GS2を直接契約して利用するよりも細やかなサポートが受けられます。

ぜひともこのパートナーシップを活用して、面白いゲームを夜に送り出していただければと思います。

それでは、また。

【設立2周年】新しい事務所のご紹介と人材募集のお知らせ

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

2年前の9月7日に Game Server Services 株式会社 は フルサーバレスアーキテクチャで設計開発された安定したゲームサーバをゲーム開発者の皆様に提供し
ゲームサーバの開発・運用業務から解放し、ゲームを面白くすることに集中出来る環境を提供することをミッションとし、ゲームサーバにおけるUnityたる存在になるべく設立し、無事設立2周年を迎えることが出来ました。
設立当初は本当にサービスを使用してもらえるのか不安だったこともありましたが、採用事例も出てきはじめました。
また、多くの会社様に声をかけていただき、開発の進んでいるタイトルも複数存在するというありがたい状況になりました。

特に今年3月末には多くの皆様のご助力により、無事資金調達を終え新しい仲間を迎えられる資金と、以前から支えてくれているワンダープラネットに加え、DeNA / KLab / GameWith / 大和企業投資 という心強いパートナーを得ることが出来ました。
新しいパートナーの存在によって、より安心してサービスをご利用いただける環境が整い、大企業から声をかけていただく機会も増えました。

まだまだ GS2は開発途上であり、今後も精力的に開発を行いサービスを魅力的なものにしていきます。

新しい事務所のご紹介

新しいメンバーを受け入れる体制を整えるべく、先月末に事務所の改装がおわりましたので、本日は新しい事務所のご紹介から入らせていただきます。

エントランス

エントランスホールのような贅沢な空間はありませんが、玄関ドアを抜けるとコーポレートロゴのあしらわれた壁が出迎えてくれます。

AWS さんからオフィス開設祝いのお花をいただいています。
設立間もないスタートアップですが、いろいろ良くしていただいており助かっています。

執務スペース

広々とした執務スペースはオシャレに飾りました。
1600mm × 1600mm という広いL字デスクを各人に割り当てており、椅子もお好みにあわせて購入しています。
ちなみに、私はハーマンミラーのエンボディチェアを愛用しており、既に本社勤務としてフルタイムで入社してくれている従業員も同じくハーマンミラーアーロンチェアを使用しています。

今年の AWS Summit 2018 で Startup Architecture of the Year で オーディエンス賞 を受賞していただいたトロフィー & AWS IoT Enterprise Button と、CEDEC 2018 に登壇した際にいただいたチビトロフィーも飾っています。

共有スペース

狭いながらも会議室も用意しています。

会議室とは別に共有部にも 幅2400mm という巨大なホワイトボードを設置しています。
設計の相談をする際に活用しています。

キッチンスペースにはネスプレッソと紅茶のフリードリンクと小腹が空いた時用にお菓子を用意しています。
疲れてきたらお菓子とコーヒーで一服してリフレッシュします。

まだ用意出来ていませんが、近々部屋の奥に仮眠空間を用意する予定があります。
眠い状態で作業を続けてもいいことはありません。
であれば、30分でも1時間でも仮眠を取ってリフレッシュしてから仕事を再開した方が総合的には生産性は高くなると考えています。
仮眠空間にはハンモックやコットを設置してゆっくりできるようにする予定です。

パソコン

ぼかしだらけで失礼します。
全員に iMac 27インチの i7 モデルに64GBのメモリを割り当てたモデルと Ultra Fine 5K ディスプレイを割り当てています。
広大な画面には2枚のIDEとブラウザ、SlackやAsanaといったコラボレーションツールを並べられるスペースがあります。

GS2 ではSDKの開発で様々な言語向けのプログラムを開発したり、各種ゲームエンジン向けにもSDKを開発する必要があるため広い画面と、巨大なメモリが活躍します。

f:id:kazutomo:20180905103225p:plain

実際にこちらが私のメモリの使用率です。
64GBのメモリが無ければスワップして作業にならないところです。

作業スペースにイヤホンが見えますが、GS2では仕事中に音楽を聴きながら仕事をしても大丈夫です。
ただし、肩を叩かれたりSlackで声をかけられたりしたときには速やかにコミュニケーションがとれる体勢に切り替えて貰う必要はあります。

ちょうど iMac の向こう側には Game Server Services が提供するサービスの稼働状態を表示するディスプレイがあります。
レスポンスタイムやエラーレートが上がるとすぐに検出出来る状態になっています。

人材募集のお知らせ

GS2 では 様々な仕様を持つゲームに対応出来るゲームサーバをフルサーバレスアーキテクチャで設計する という高度なミッションを掲げており、このミッションを成し遂げられる人材を募集しています。

ジョブデスクリプションや諸条件は下記の募集要項ページを参照いただきたいですが、ざっくりと 裁量労働制 年収792万円(月額66万円・みなし残業20時間込み・賞与無し)+ ストックオプション という条件で、サービス開発エンジニアとソリューションアーキテクト兼セールスを募集しています。
要求スキルの高さやミッションの困難さは私も自覚するところですが、スタートアップらしい柔軟な考えでエンジニアが働きやすい職場作りを心がけていますので、我こそは。という方は是非応募してみてください。

gs2.io

それでは、また。

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

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

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

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

GS2-Gold は最近では基本的な設計として利用しているJSONによるマスターデータ管理に対応していませんでした。

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

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

1つのリソースで複数のゲーム内通貨を扱えるように

これまで GS2-Gold は1つのリソースで1つのゲーム内通貨しか扱えませんでした。
そのため、ゲーム内で複数種類の通貨を扱おうとすると複数のリソースを作成する必要があり、
GS2-Gold はサービスクラス型の課金形態のため、リソース配分の最適化を行うのが難しい。という課題がありました。

今回、1つのリソースで複数のゲーム内通貨を扱えるようになったことで、より低コストで複数のゲーム内通貨を実装いただけるようになりました。

それでは、また。

【サービス拡張】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円で利用出来るとイメージしてください。

それでは、また。

(C) Game Server Services, Inc.