Amazon Web Services ブログ
サーバーレスでスケールするマルチプレイヤーゲームを構築する
本投稿は、Sr.Solutions Architect, Developer Acceleration の Tim Bruce による記事を翻訳したものです。
ゲーム開発は、要件が急速に変化するため非常に繰り返しの多いプロセスです。ゲーム開発者の多くは、可能な限り多くの時間を機能の構築のために費やしたいと思っており、サーバー設定、インフラストラクチャ管理、スケーリング技術の習得に時間を費やしたくないと考えています。
AWS のサーバーレスサービス を利用することで、4つの大きなメリットが得られます。1つ目は、運用オーバーヘッドを削減することで、企画からリリースまでのプロセスを短くします。2つ目は、運用に必要なハードウェアとソフトウェアを過剰にプロビジョニングしないため、コスト削減を実現できる可能性があります。3つ目は、サーバーレスはユーザーのアクティビティに応じてスケールします。最後は、サーバーレスサービスには組み込みの統合機能があるため、サービス統合に労力をかけることなくゲーム開発に集中できます。
AWSを利用するゲーム開発者は、これらのメリットを活用することで、チームがサーバーやソフトウェアのセットアップや保守といった差別化につながらないタスクに時間を費やすのではなく、ゲーム体験の向上やコンテンツ開発により多くの時間を費やすことができます。これにより、ゲーム体験とコンテンツがより良いものとなり、リリースまでの時間も短縮されます。
本ブログ記事では、「サーバーレスファースト」アーキテクチャを持つゲームについて紹介します。 Simple Trivia Service はウェブベースのゲームであり、貴社のゲームにも適用可能な複数のアーキテクチャパターンを持っています。
Simple Trivia Service の紹介
Simple Trivia Service は、プレイヤーが作成したコンテンツを含むシングルプレイヤーおよびマルチプレイヤーのトリビアゲームを提供します。Simple Trivia Service には、ユーザー登録、チャット、コンテンツ作成、リーダーボード、ゲームプレイ、マーケットプレイスなど、多くの機能があります。
認証されたプレイヤーは、他のプレイヤーとチャットしたり、クイズを作成・管理したり、プロフィールを更新したりできます。また、シングルプレイヤーおよびマルチプレイヤーのクイズをプレイし、クイズをホストし、マーケットプレイスでクイズを売買することもできます。シングルプレイヤーおよびマルチプレイヤーゲームモードでは、接続要件と技術要件が異なる様々なゲームを「サーバーレスファースト」アーキテクチャでどのように実現するかを示します。ゲームモードとアーキテクチャソリューションについては、Simple Trivia Service バックエンドアーキテクチャセクションで説明します。
Simple Trivia Service フロントエンド
Simple Trivia Service フロントエンドは、バックエンドサービスにアクセスする Vue.js で作られたシングルページアプリケーション (SPA) です。この SPA アプリにはウェブブラウザ経由でアクセスしますが、ユーザーは、HTTPS、セキュアな WebSockets、および MQTT over WebSocket を使用してゲームエンドポイントにリクエストを送信できます。これらのリクエストでは、統合を使用してサーバーレス・バックエンドサービスにアクセスします。
Vue.js を使う事で、このリファレンスアーキテクチャがより使いやすくなっています。このフロントエンドでは、 AWS Amplify を使用して SPA を構築、デプロイ、ホスティングします。リソースのプロビジョニングと管理は必要ありません。
Simple Trivia Service バックエンドアーキテクチャ
Simple Trivia Service のバックエンドアーキテクチャでは、ゲームは部分的にAWS Serverless Application Model (AWS SAM) を使って定義されています。デプロイガイドは GitHub リポジトリの README.md ファイルに含まれています。バックエンドアーキテクチャを以下に示します。
使用するサービス
Simple Trivia Service は、 AWS Lambda、 Amazon API Gateway、 AWS IoT、 Amazon DynamoDB、Amazon Simple Notification Service(SNS)、 AWS Step Functions、 Amazon Kinesis、Amazon S3、Amazon Athena、およびAmazon Cognitoを使用して構築されています。
- Lambda で、Simple Trivia Service におけるサーバーレスマイクロサービス機能を実現しています。
- API Gateway は HTTP/RESTful および WebSocket 通信用のサーバーレスエンドポイントを提供し、AWS IoT は MQTT over WebSocket 通信用のサーバーレスエンドポイントを提供します。
- DynamoDB を使用することで、大規模アプリケーション向けのデータの保存と取得を実現しています。
- Amazon SNS は、Publish/Subscribe機能によるマイクロサービス間の通信を実現しています。
- Step Functions は、適切な結果が得られるよう、複雑なタスクを調整します。
- 分析機能は、Kinesis および S3 経由で、Athenaによりクエリ/可視化され提供されます。
- Amazon Cognito は、安全で標準に準拠したログイン機能とユーザーディレクトリを提供します。
サーバーレスではない 2 つのマネージドサービス、 Amazon VPC NAT Gateway と Amazon ElastiCache for Redisも使用します。VPC NAT Gateway は、VPCで利用可能な Lambda 関数が VPC 外のサービス(DynamoDB など)に到達するために必要です。ElastiCache は、ミリ秒未満のレイテンシー要件を持つアプリケーションに適したインメモリデータベースを提供します。
ユーザーセキュリティ、およびバックエンドサービスとの通信を実現する
プレイヤーは、プレイする前に登録してログインする必要があります。登録およびログイン認証情報は、 Secure Remote Password プロトコルを使用して Amazon Cognito に送信されます。正常にログインすると、Amazon Cognito は JSON ウェブトークン (JWT) と Amazon Cognito ユーザーセッションを返します。
このJWTは、API Gateway へのリクエストに含めます。API Gateway は、リクエストが Lambda に転送される前にトークンを検証します。
AWS IoT は、 AWS Identity and Access Management (IAM) ポリシーによる追加のセキュリティを求めます。Amazon Cognito ユーザーにアタッチされたポリシーにより、プレイヤーはIoT エンドポイントへの接続、サブスクライブ、およびメッセージの送信が許可されます。
ゲームタイプ別に見るアーキテクチャ
Simple Trivia Service には 3 つのゲームモードがありますが、モードによって、プレイヤーがバックエンドサービスとやり取りする方法が異なります。モードごとに、ゲーム内部では異なるアーキテクチャを使用します。
「シングルプレイヤー」クイズアーキテクチャ
シングルプレイヤーのクイズは、シンプルなルールや短いプレイセッションが特徴的で、幅広いプレイヤー層を対象としています。シングルプレイヤーゲームの通信は、プレイヤーとエンドポイント間のみとなります。これは、API Gatewayの HTTP API 経由で実現しています。
4 つの Lambda 関数(ActiveGamesList、GamePlay、GameAnswer、LeaderboardGet)を使う事で、シングルプレイヤーゲームを実現しています。これらの関数は API Gateway と統合されており、クライアントからの特定のリクエストに応答します。API Gateway は、URI、本文、クエリ文字列などのリクエストを適切な Lambda 関数に転送します。
プレイヤーが「Play」を選択すると、リクエストが API Gateway に送信され、ActiveGamesList 関数が呼び出されます。ActiveGamesList 関数は DynamoDB のActiveGames テーブルをクエリし、アクティブなゲームのリストをユーザーに返します。
プレイヤーがゲームを選択すると、GamePlay 関数をトリガーする別のリクエストが発生します。GamePlay 関数は DynamoDB のGamesDetail テーブルからゲームの問題を取得します。フロントエンドは、ゲーム中にユーザーの状態を維持します。
すべての問題に回答すると、SPA はプレイヤーの回答を API Gateway に送信し、GameAnswer 関数を呼び出します。GameAnswer 関数は、GameDetails テーブルを照会してプレイヤーの回答をスコア付けします。スコアと回答がユーザーに送信されます。
さらに、GameAnswer 関数は、リーダーボード用のプレイヤーのスコアおよびプレイヤーエクスペリエンスを 2 つの SNS トピック(LeaderboardTopic と PlayerProgressTopic)に送信します。ScorePut 関数および PlayerProgressPut 関数は、これらのトピックをサブスクライブします。これら 2 つの関数は、DynamoDB の Leaderboard テーブルおよびPlayer Progress テーブルに詳細を書き込みます。
このアーキテクチャでは、これら 2 つのアクションを非同期的に処理するため、プレイヤーは待たされることなくスコアと回答を受け取ります。また、PlayerProgressPut 関数のみが Player Progress テーブルに書き込むことができるため、プレイヤーの進行状況に対するセキュリティも強化されます。
最後に、プレイヤーはゲームのリーダーボードを表示できます。リーダーボードは LeaderboardGet 関数へのレスポンスとしてプレイヤーに返されます。LeaderboardGet 関数は、Leaderboardテーブルから上位 10 のスコアと現在のプレイヤーのスコアを取得します。
「マルチプレイヤー – カジュアル & 対戦」アーキテクチャ
これらのゲームタイプでは、プレイヤー間の通信およびサービスとプレイヤー間の通信が必要です。通常は、TCP/UDP ソケットまたは WebSocket プロトコルを使用して実行します。API Gatewayは WebSocket 通信を提供し、Lambda 関数がゲームホストやプレイヤーとの間でメッセージを送受信できるようにします。
ゲームホストは、[Host] ボタンでゲームを開始し、API Gateway 経由で LiveAdmin 関数にメッセージを送信します。LiveAdmin 関数は、ゲームを LiveGames テーブルに追加し、プレイヤーがゲームを見つけて参加できるようにします。ゲーム用の問題一式は、この時点で LiveAdmin 関数からゲームホストに送信されます。さらに、ゲームホストが GameConnections テーブルに追加され、どの接続がゲームに関連しているか追跡されます。プレイヤーも、ゲームに参加するときにLivePlayer 関数経由で GameConnections テーブルに追加されます。
ゲームホストクライアントは、すべてのプレイヤーのゲームの状態を管理し、ゲームの流れを制御し、API Gateway と LiveAdmin 関数経由でプレイヤーに問題、正答、リーダーボードを送信します。LiveAdmin 関数は GameConnections テーブルに追加されているプレイヤーにのみゲームメッセージを送信します。プレイヤーの回答は LivePlayer 関数経由でホストに送信されます。
ゲームを終了する際、ゲームホストは LiveAdmin 関数経由で最終リーダーボードを含むメッセージをすべてのプレイヤーに送信します。また、LiveAdmin 関数は、リーダーボードを Leaderboard テーブルに保存し、ActiveGames テーブルからゲームを削除し、プレイヤーの進行状況を示すメッセージを Player Progress トピックに送信します。
「マルチプレイヤー – ライブスコアボード」アーキテクチャ
これは、他のマルチプレイヤーゲームタイプと同様の通信を必要とする拡張版です。MQTT over WebSocket をトランスポートとしてAWS IoT を使用します。これにより、クライアントはトピックをサブスクライブし、受信したメッセージを処理できます。AWS IoT は、サブスクリプションに基づいてクライアントへのメッセージのルーティングを管理します。
このアーキテクチャは、状態管理をゲームホストクライアントで行わず、バックエンドのデータストアで行います。この変更には、ユーザーのアクションにすばやく応答できるデータベースが必要です。Simple Trivia Service は、このデータベースに ElastiCache for Redis を使用しています。ゲーム用の問題、プレイヤーの回答、リーダーボードはすべてRedisを使いクイズ中に保存され、更新されます。ElastiCache インスタンスは、VPC に配置することでインターネットトラフィックからブロックされます。セキュリティグループにより、同じ VPC 内の Lambda 関数に対するアクセス設定が行われます。
このゲームタイプのホストは、ホストすることでゲームを開始し、AWS IoT にメッセージを送信し、CacheGame 関数をトリガーします。CacheGame 関数は、ゲームを ActiveGames テーブルに追加し、DynamoDB から Redis にクイズの詳細をキャッシュします。プレイヤーはメッセージを送信してゲームに参加しますが、そのメッセージはJoinGame 関数に配信されます。JoinGame 関数は、ユーザーレコードを Redis に追加し、プレイヤーが参加したことをゲームホストに通知します。
ゲームホストは、AskQuestion 関数を呼び出してプレイヤーに問題を送信できます。AskQuestion 関数は、Redis内で現在の問題番号を更新し、AskQuestion 関数経由でサブスクライブされたプレイヤーに問題を送信します。ReceiveAnswer 関数はプレイヤーの回答を処理します。回答を検証し、Redis に保存し、スコアボードを更新し、最初の正答の後、更新されたスコアボード情報と共にすべてのプレイヤーに送信します。プレイヤーのゲームのスコアボードはリアルタイムに更新されます。
ゲームが終了すると、ゲームホストは AWS IoT 経由で EndGame 関数にメッセージを送信します。EndGame 関数は、ゲームのリーダーボードを Leaderboard テーブルに書き込み、プレイヤーの進行状況を Player Progress SNS トピックに送信し、キャッシュからゲームを削除し、ActiveGames テーブルからゲームを削除します。
まとめ
本記事では、AWS で「サーバーレスファースト」アーキテクチャで構築した、シングルプレイヤーおよびマルチプレイヤーゲームである Simple Trivia Service について説明しました。ゲームクライアントから、シングルプレイヤーゲームとマルチプレイヤーゲームの両方において「サーバーレスファースト」バックエンドへの接続を実現するさまざまなソリューションを取り上げました。また、各ソリューションを使ったアーキテクチャの説明も行いました。
Simple Trivia Service の GitHub リポジトリの指示に従って、このソリューションのコードをご自身の AWS アカウントにデプロイできます。
サーバーレスに関する学習リソースの詳細については、 Serverless Landを参照してください。
翻訳はソリューションアーキテクト木村が担当しました。原文はこちらです。