はじめに
はじめまして、インディーゲームスタジオ Pegass85 代表の竹内佑介です。
現在、私は巨大ロボット同士の読み合いバトルゲーム、機動倶楽部Gブレイバーバースト (以下、Gブレイバーバースト) の開発しています。Gブレイバーバーストはオンライン対戦機能を実装しており、バックエンドは AWS のサーバーレスアーキテクチャを採用しています。この記事では、なぜ Gブレイバーバーストがサーバーレスアーキテクチャを採用したのか、サーバーレスを用いたネット対戦の際に発生する問題とその解決策について詳しく説明します。
builders.flash メールメンバー登録
Gブレイバーバーストの紹介

サーバーレスアーキテクチャを採用した理由
Gブレイバーバーストは three.js で制作されたブラウザゲームです。このゲームにネット対戦を導入するためには、以下の要件が必要でした。
- WebSocket通信ができる対戦サーバー
- スケーラビリティ
- 自動デプロイ
Amazon GameLift や Photon は素晴らしいゲームサーバーですが、1 に関する情報が不足していたので採用は諦めました。その後、AWS Alien Attack: A Serverless Adventure という AWS 公式のハンズオンを発見し、サーバーレスなら 1 から 3 までの条件を満たすことがわかりました。さらに、サーバーレスはノウハウも豊富なことから、Gブレイバーバーストでのサーバーレス採用を決定しました。
アーキテクチャ
Gブレイバーバーストのアーキテクチャのポイントは、以下の通りです。
Amazon S3 ホスティング + Amazon CloudFront での静的コンテンツ配信
Amazon API Gateway + AWS Lambda + Amazon DynamoDB での API
AWS Fargate による常時起動サーバーでのマッチメイク
IDaaS としての Auth0
1 には three.js 製の Single Page Application、2 は serverless framework による WebSocket API および Rest API、3は AWS CDK で Fargate を構築しています。2、3 は GitHub にソースコードを公開しているので、興味のある方は以下から確認をお願いします。
Gブレイバーバースト バックエンド+インフラ ソースコード »
ネット対戦のキモとなる状態同期は 2 だけで実現していますが、どのように実装したのかを次のセクションで説明します。

サーバーレスで状態同期する方法
はじめにターンベース制のゲームで状態同期するロジックを考えてみましょう。こちらが状態同期ロジックの概要図です。
- プレイヤーがゲームサーバーにコマンドを送信
- ゲームサーバーに 2 人分のコマンドが存在するかをチェック
- コマンドが揃っていた場合、ダメージ計算などを行いゲームを進める
- 3. の結果をプレイヤーに送信する

素朴な状態同期 API

同時アクセスの問題
素朴な状態同期 API は一見問題無いように見えますが、同時アクセスの問題があります。たとえば、こののように各プレイヤーの API 呼び出し時間帯が被った場合、ゲーム進行以降の処理が 2 回呼ばれることがあります。
データベースの種類によっては上記挙動とは異なる場合もありますが、同じデータに対して複数プレイヤーが同時に読み書きをしているので、いわゆる排他制御を導入する必要があります。キューを活用して一人ひとりが順番に処理実行できるようにする、テーブルに悲観ロックをかけて処理が完了するまで他プレイヤーを待たせる、などが代表的な排他制御ですが、AWS Alien Attack では別の方法が提示されていました。

AWS Alien Attack の状態同期方法
AWS Alien Attack は一人で遊ぶインベーダーゲームのようなものですが、他プレイヤーも含めたスコアランキングがリアルタイムで更新されるという特徴がありますが、この部分がターンベース制ゲームでの状態同期実装方法の参考になります。
AWS Alien Attackでは、ルームに参加しているプレイヤーの間でスコアランキングが共有されので、登場人物は以下のようになります。
ルーム作成者
ルーム参加プレイヤー
これを踏まえ、AWS Alien Attack では、こちらのようにスコアランキングのリアルタイム更新を実現しています。
ルーム作成者という代表者一人だけでスコアランキング作成 API を定期的に呼び出しす、いわゆるポーリングをしているので、素朴な状態同期 API で問題になった同時アクセス問題が解決されています。この考え方をターンベース制ゲームの状態同期に応用します。

Gブレイバーバーストの状態同期
AWS Alien Attack ではルーム作成者が一人だけでスコアランキング作成をポーリングすることで、同時アクセス問題を解決していました。これを Gブレイバーバーストにも応用したいのですが、同作にはルーム作成者は存在しません。なのでプレイヤーの代表者一人が、ルーム作成者も兼ねるようにしました。これより、Gブレイバーバーストのネット対戦の登場人物は以下のようになります。
通常プレイヤー
ポーリング担当プレイヤー
以上を踏まえて、Gブレイバーバーストではこのように状態同期をしています。

フローチャート
ポーリング担当プレイヤーだけがコマンドチェックをすることで、素朴な状態同期API で問題になった同時アクセス問題を解決しているのがミソです。なお、ポーリング担当プレイヤーは、試合前にランダムで決められます。
図のみではポーリング担当プレイヤーのフローが分かりづらいので、こちらにフローチャートを提示します。

余談:ポーリング担当プレイヤーの割り当てが難しいケース
本節で提示したポーリング担当プレイヤーを割り当てる方法は、汎用的な状態同期方法として使えそうです。しかし、ポーリング担当プレイヤーが固定されないケース、たとえばカジュアルマッチなどロビーの入退室が頻繁に起こるようなケースでは、どのようにしてポーリング担当プレイヤーを引き継ぐのかという問題があり、この解決は並大抵ではありません。
Gブレイバーバーストではカジュアルマッチを実装していますが、マッチングが成立したプレイヤーはロビーから退出となるため、ポーリング担当プレイヤーの割り当てができません。なので常時起動サーバーとして Fargate を立ち上げて、マッチングチェックのポーリングを実行させています。
まとめ
本エントリーでは Gブレイバーバーストを題材にして、サーバーレスアーキテクチャを使用してターンベースのオンライン対戦ゲームを実現する方法について解説しました。Gブレイバーバーストでは、同時に実行できないコマンドチェックなどの処理を、代表者一人がポーリングすることで整合性を確保しています。
Gブレイバーバーストのネット対戦実装で参考にした AWS Alien Attack には、Amazon Kinesis を使用したデータ分析や Amazon Cognito を活用した権限管理など、ゲームプレイそのものとは直接関係しませんが、サービス運営に必要なノウハウが豊富に含まれています。現在の Gブレイバーバーストの規模では、これらの要件がすぐに必要なわけではありませんが、サービスが成長した暁には再度 AWS Alien Attack を勉強する予定です。
筆者プロフィール
竹内 佑介
Pegass85
Pegass85 の代表で、IT 企業に勤務しながら「機動倶楽部Gブレイバーバースト」を開発中。 Gブレイバーバーストの原型は大学時代に作成したものであり、当時からネット対戦機能が欲しいとの声があったがスキル不足で諦めていた。 しかし、社会人になり AWS に出会い、スキルさえあればどんなサービスでも自作できることを確認。 業務で AWS の知見を得つつ、それをフィードバックして Gブレイバーバーストを完成させた。

Did you find what you were looking for today?
Let us know so we can improve the quality of the content on our pages