サーバーレスでターンベース制のネット対戦ゲームを作った話

2024-01-04
デベロッパーのためのクラウド活用方法

Author : 竹内 佑介 (Pegass85)

はじめまして、インディーゲームスタジオ Pegass85 代表の竹内佑介です。

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


Gブレイバーバーストの紹介

Gブレイバーバーストは巨大ロボで読み合いを楽しむゲームです。本作はターン制バトルながら、運任せの要素がほとんどありません。攻撃、防御のエネルギー配分を考え、ロボ、パイロットの特殊能力を使いこなし、勝利をこの手に掴みましょう。


サーバーレスアーキテクチャを採用した理由

Gブレイバーバーストは three.js で制作されたブラウザゲームです。このゲームにネット対戦を導入するためには、以下の要件が必要でした。

  1. WebSocket通信ができる対戦サーバー
  2. スケーラビリティ
  3. 自動デプロイ

Amazon GameLift や Photon は素晴らしいゲームサーバーですが、1 に関する情報が不足していたので採用は諦めました。その後、AWS Alien Attack: A Serverless Adventure という AWS 公式のハンズオンを発見し、サーバーレスなら 1  から 3 までの条件を満たすことがわかりました。さらに、サーバーレスはノウハウも豊富なことから、Gブレイバーバーストでのサーバーレス採用を決定しました。


アーキテクチャ

Gブレイバーバーストのアーキテクチャのポイントは、以下の通りです。

  1. Amazon S3 ホスティング + Amazon CloudFront での静的コンテンツ配信
  2. Amazon API Gateway + AWS Lambda + Amazon DynamoDB での API
  3. AWS Fargate による常時起動サーバーでのマッチメイク
  4. IDaaS としての Auth0

1 には three.js 製の Single Page Application、2 は serverless framework による WebSocket API および Rest API、3は AWS CDK で Fargate を構築しています。2、3 は GitHub にソースコードを公開しているので、興味のある方は以下から確認をお願いします。

Gブレイバーバースト バックエンド+インフラ ソースコード »

ネット対戦のキモとなる状態同期は 2 だけで実現していますが、どのように実装したのかを次のセクションで説明します。


サーバーレスで状態同期する方法

はじめにターンベース制のゲームで状態同期するロジックを考えてみましょう。こちらが状態同期ロジックの概要図です。

  1. プレイヤーがゲームサーバーにコマンドを送信
  2. ゲームサーバーに 2 人分のコマンドが存在するかをチェック
  3. コマンドが揃っていた場合、ダメージ計算などを行いゲームを進める
  4. 3. の結果をプレイヤーに送信する

素朴な状態同期 API

上記ロジックを素朴に実装するなら、こののような素朴な状態同期 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 を勉強する予定です。


builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます


筆者プロフィール

竹内 佑介 
Pegass85

Pegass85 の代表で、IT 企業に勤務しながら「機動倶楽部Gブレイバーバースト」を開発中。 Gブレイバーバーストの原型は大学時代に作成したものであり、当時からネット対戦機能が欲しいとの声があったがスキル不足で諦めていた。 しかし、社会人になり AWS に出会い、スキルさえあればどんなサービスでも自作できることを確認。 業務で AWS の知見を得つつ、それをフィードバックして Gブレイバーバーストを完成させた。

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する