Amazon Web Services ブログ
たった数行のJavaScriptで作るマルチプレイヤーモバイルゲームのサーバー
Game Developers Conference (GDC) 2019でプレビューがアナウンスされたAmazon GameLift リアルタイム サーバーですが、この度正式版がリリースされました! リアルタイムサーバーを利用することで、ゲームサーバーの作成やカスタマイズを手頃な価格で実現することができます。
優れたマルチプレーヤーゲームのエクスペリエンスを構築するにあたり、ゲーム開発者によるマルチプレーヤーゲーム構築を妨げる障壁があります。その障壁を取り払うためには、時間とコストがかかり、すべてのゲーム開発者が持っているわけではないような専門知識が必要です。
GameLift リアルタイム サーバーを使えば、数行のJavaScriptでカスタマイズ可能なマルチプレイヤーゲームサーバーを作成し、プレーヤー1人あたり少額の月額課金で数百万人の規模にスケールアップすることができる。
GameLiftリアルタイムサーバーは以下の15のAWSリージョンで利用可能です。
米国東部(バージニア北部とオハイオ)、米国西武(オレゴンと北カリフォルニア州)、カナダ(モントリオール)、欧州(フランクフルト、ロンドンとアイルランド)、アジアパシフィック(ムンバイ)、アジアパフィシック(ソウルと東京)、アジアパシフィック(シンガポールとシドニー)、中国(北京)、南米(サンパウロ)
詳しく知りたい方は、Amazon GameLift製品詳細ページまたは開発者ガイドをご覧ください。
GameLift リアルタイムサーバーをすぐに使い始めるために、以下のサンプルを用意しました。ぜひご参考ください!
マルチプレイヤーサーバーは難しい
従来、カスタムゲームサーバの開発はとても骨の折れる仕事です。サーバの構築にはネットワーキングシステムやバックエンド開発、サーバオペレーションに関する多くの知識が必要になります。これらのシステムを開発するために必要なリソースを持たない小規模なチームにとっては困難な状況です。ただ素晴らしいゲームを作りたいだけの場合でさえ、プレイヤーのための機能を開発することよりゲームサーバーを稼働させることに多くの時間とお金が費やされています。
ちょっと待って!いや、マルチプレイヤーサーバは簡単!
ゲームプレイのサーバーがとても軽い(ライトウェイトな)場合でも、そんなに複雑なサーバを構築する必要があるでしょうか?いえ、結局のところ、答えは”No”です。これから、Amazon GameLiftリアルタイムサーバーを利用して、わずか数時間で競争力のある2人用レーシングゲームを開発する方法をご紹介します。
開発者は、GameLiftリアルタイムサーバーを利用して、お手頃なゲームサーバーを数行のJavaScriptを使って迅速に作成、更新することができます。多くのバックエンド処理能力を必要をしない、モバイル、ターンベース、メッセージングゲームなどに最適です。
サーバーの構築なしでカスタムゲームロジックを作成することがアイデアのベースとなっています。必要な作業としては、スクリプト(JavaScriptの使用)を利用して、プレイヤー間の通信やゲームの状態の変化を処理するコールバックを実装するだけです。たとえば、あるプレイヤーがゲームに参加したときの処理や、他のプレイヤーに新しいメッセージを送信したときの処理が挙げられます。GameLiftにスクリプトをアップロードするだけの単純なインテグレーションとなります。ゲームクライアント側では、.NET SDKなどのクライアント向けSDKがサーバと接続します。GameLiftはクライアントとサーバーが互いにコミュニケーションするための作業を行い、お互いメッセージを送受信するための簡単なAPIを提供し、サーバーホスティング、スケーリング、およびその他の運用上のものをすべて扱うため、ご自身で実装する必要はありません。あなたはゲームのコードのみにフォーカスできます。
“カエルのジャンプ”から始めてみよう!
私はシンプルなワンクリックで競争するレースゲームがGameLiftリアルタイムサーバーを試す素晴らしい方法だと考えていましたが、テーマが必要でした。カリフォルニアで育った子供は、エンジェルキャンプで行われている、なるべく遠くにカエルをジャンプさせるという熱狂的なイベントに参加します。誰もがジャンプするカエルと意地悪なカメが登場する古典的なアーケードゲームを知っています。
というわけで、今回は”カエル”をテーマにしたゲームを用意しました。ようこそ!”MEGA FROG RACE”の開発へ!
このゲームオブジェクトはとてもシンプルです。スペースキーで2人のプレイヤーがカエルをコントロールし、どちらが早くゴールするかを競います。しかし、スペースキーを一番早く叩くだけの競争ではありません!再びスペースキーを打つ前にカエルがそのホップを完了するまで待たないといけません。また、カエルがイライラしたときは着地後しばらく動きません。これは、誰が最速のインターネット接続なのか、最速でキーを打つかによって勝者が決定しないことを意味します。
私はWindowsのUnityでゲームクライアントを開発しました。Unityの初心者向けのUnity Personalを使ってビルドすることが可能です。今回説明するすべてのテクニックは、.NET Runtimeで実行できるすべてのサービスで活用します。もしサンプルの実行や独自のプロジェクト上での設定で問題が発生した場合はフォーラム(英語)またはソリューションアーキテクトまでお知らせください。
始めるにあたり、まずは以下が必要になります。
- GameLift、IAM、Amazon Cognitoにアクセス可能なAWSアカウント
- Microsoft Visual Studio (Community版でも可)
- Unity(Microsoft Visual Studioもインストールされます)
- “Getting Started” ページからダウンロードしたRealtime Servers SDK
ステップ1: GameLiftの準備
新規プロジェクトを開始する最も簡単な方法はGameLiftにスクリプトをホストし、サーバーにコンピュートパワーを割り当てる方法です。コンピュートパワーはフリート(Fleet)と呼ばれ、ゲームサーバーを稼働させるAmazon EC2の仮想マシンの集まりです。
GameLiftリアルタイムサーバーが稼働する最も最小のスクリプトを作成するところから始めていきましょう。サンプルのスクリプトはこちらのページにあります。お気に入りのエディタを使い、”MegaFrogRaceServer.js”という名前で新規ファイルを作成、サンプルのスクリプトをファイルにペーストし、セーブします。次は、スクリプトをzipファイルに圧縮します。この例では、スクリプトをサブフォルダに入れないようにしてください。Windowsでは、JavaScriptファイルを右クリックし、[送信]-[圧縮(zip形式)]に送信しました。MegaFrogRaceServer.zipファイルが作成されます。
次は、GameLiftにスクリプトをアップロードし、フリートを作成します。
- Amazon GameLiftのコンソールを開きます。
- コンソールの右上でゲームサーバーをホストしたいリージョンを選択します。選択したリージョンでスクリプトコンテナとフリートが作成されます。
- “ダッシュボードに切り替える”をクリックします。
- ダッシュボードをクリックしプルダウンメニューを開きます。
- スクリプトメニューの”スクリプトの作成”を選択します。
- 「スクリプトを作成」フォームでスクリプトの名前とバージョン番号を入力します。今回は”MegaFrogRaceServer”とし、バージョン番号は”1.00″としました。
- スクリプトタイプをzipに変更します。(フォームのデフォルト値はAmazon S3バケットからスクリプトをアップロードできる”ユーザストレージ”オプションですが、今回は使用しません。)
- 先程作成したZipファイルを選択します。
- 送信をクリックし、スクリプトコンテナを作成します。
これでスクリプトの準備は完了しました。次はリアルタイムサーバーのフリートを作成します。
- GameLiftのプルダウンメニューから”フリートの作成”を選択します。
- 「フリートを作成」フォームでフリートの名前を入力し、フリートのタイプを”オンデマンド”のままにします。(本デモには関係ありませんが、スポットインスタンスを利用することでゲームが人気になったときにかなりのインスタンスコストを節約することができます。詳細はこちらをご参照ください。)
- バイナリ型を”スクリプト”に変更します。
- スクリプトで上記で作成したスクリプトコンテナ(MegaFrogRaceServer)を選択します。
- 残りの詳細は空白のままにします。
- インスタンスタイプを選択します。本デモではc4.largeで十分です。無料利用枠で利用できます。
- プロセス管理ではGameLiftがサーバー起動時にどのスクリプトを実行するか指定する必要があります。アップロードしたzipファイルは複数のファイルを含む場合もあります。(実際に後ほどの例で実行します)そのため、メインのスクリプトファイルを指定する必要があります。上記で作成したJavaScriptファイ”MegaFlogRaceServer.js”を指定します。
- 起動パラメータは空で、同時プロセスは”1”を入力します。
- 右側に表示される緑のチェックボックスをクリックし、設定に追加することを忘れないでください。
10. フォームのその他の項目はデフォルトのままにして、”フリートの初期化”をクリックします。
11. 成功のメッセージが表示され、新しいフリートのステータスが含まれるフリートの詳細を見ることができます。フリートがアクティベート状態になるまで数分かかります。その間にステップ2を進めていきましょう。
ステップ2: ゲームとGameLift間のglue(つなぎ)
さて、次のステップは実際のゲームを作成する以外に最も複雑なものです。GameLiftと対話してゲームに参加したり、新しいゲームセッションを開始したりするクライアントサービスを作成します。GameLift SDKをゲームクライアントに直接統合することは可能ですが、ほとんどの場合においてベストプラクティスではありません。直接統合すると外部からの呼び出しを保護する方法が無く、セキュリティキーが変更されたり期限切れになったりしたときにゲームクライアントを更新する必要があるためです。そこで、デモではAWS Lambdaを使ってサービスを実行し、Amazon Cognitoを使ってセキュリティを確保する、小さなクライアントサービスを作成します。
今回の例はちょっとしたおもちゃのような実装ですが、より機能が豊富なクライアントサービスを作成するための正しい方向性を示しています。
最初のタスクはLambda関数の作成です。AWS Lambda関数とは、ユーザによるサーバー管理が不要なAWSサーバーで実行されるコードです。
- Lambdaのコンソールを開き、”関数の作成”をクリックします。
- “一から作成”を選択します。
- 関数の名前に”ConnectClientToServer”を入力します。
- ランタイムで”Node.js 8.10″を選択します。
- アクセス権限で”実行ロールの選択または作成”を表示し、”基本的なLambdaアクセス権限で新しいロールを作成”が選択されていることを確認します。
- “関数の作成”をクリックします。
- Lambdaエディタが開きます。Lambda関数がGameLift APIにアクセスできるか確認する必要があります。画面をスクロールし、”実行ロール”セクションがあることを確認します。
- 新しく作成されたロールを”既存のロール”のドロップダウンの中で確認することができます。”service-role/ConnectClientToServer-role-abc1defg”のような名前です。すぐ下のロールを表示するリンクをクリックします。
- 新しいウインドウもしくはタブでロール設定のIAMのページが開きます。アクセス権限タブでポリシーのリストが確認できます。そこにはすでに”AWSLambdaBasicExecutionRole”が存在します。GameLift APIへのアクセスを許可するために2番目のポリシーを追加する必要があります。
- “ポリシーをアタッチします”をクリックします。そのリンクを新しいウインドウもしくはタブで開くことをおすすめします。本ページに後ほど戻ってきます。
- “ポリシーの作成”をクリックします。
- ビジュアルエディタを利用します。(JSONのテンプレートを提供することもできましたが、いろいろなAPIが利用可能で、それらがどのように構築されているかを確認いただくのも興味深いと思います)
- “サービスの選択”をクリックします。
- GameLiftを検索し、結果をクリックします。
- アクセスレベルで”読み込み”を展開し、”DescribeGameSessions”と”SearchGameSessions”をチェックします。
- “書き込み”を展開し、”CreateGameSession”と”CreatePlayerSession”をチェックします。
17. これらはクライアントサービスを作成するために必要な最小限の機能です。これらの機能を利用して参加するゲームセッションを検索し、新しいゲームセッションを作成し、そのセッションに参加することができます。デモではこれ以上関数を利用する必要はありませんが、他の利用可能な関数を調べてみるとクライアントサービスを拡張するためのアイデアが得られるかもしれません。
18. “ポリシーの確認”をクリックし、ポリシーの名前に”GameLiftClientServicePolicy”と入力します。完了後、”ポリシーの作成”をクリックします。
19. そして、IAMロールのアクセス権限のページに戻ります。画面を更新し、新しいポリシーである”GameLiftClientServicePolicy”を検索し、選択、”ポリシーのアタッチ”をクリックします。これで関数が先程選択したGameLift APIをコールすることができるようになりました。
20. Lambda関数にコードを追加します。Lambdaエディタのページに戻り、以下のコードを関数コードのエディタにペーストします。元々存在していたコードもすべて上書きします。(本コードはこの記事の後半にリンクされているコードサンプルのAWSフォルダでも紹介しています)
const uuid = require('uuid');
const AWS = require('aws-sdk');
const GameLift = new AWS.GameLift({region: 'ap-south-1'});
const MegaFrogRaceFleetID = "fleet-00aaaa00-a000-00a0-00a0-aa00a000aa0a";
exports.handler = async (event) => {
let response;
let gameSessions;
// find any sessions that have available players
await GameLift.searchGameSessions({
FleetId: MegaFrogRaceFleetID,
FilterExpression: "hasAvailablePlayerSessions=true"
}).promise().then(data => {
gameSessions = data.GameSessions;
}).catch(err => {
response = err;
});
// if the response object has any value at any point before the end of
// the function that indicates a failure condition so return the response
if(response != null)
{
return response;
}
// if there are no sessions, then we need to create a game session
let selectedGameSession;
if(gameSessions.length == 0)
{
console.log("No game session detected, creating a new one");
await GameLift.createGameSession({
MaximumPlayerSessionCount: 2, // only two players allowed per game
FleetId: MegaFrogRaceFleetID
}).promise().then(data => {
selectedGameSession = data.GameSession;
}).catch(err => {
response = err;
});
if(response != null)
{
return response;
}
}
else
{
// we grab the first session we find and join it
selectedGameSession = gameSessions[0];
console.log("Game session exists, will join session ", selectedGameSession.GameSessionId);
}
// there isn't a logical way selectedGameSession could be null at this point
// but it's worth checking for in case other logic is added
if(selectedGameSession != null)
{
// now we have a game session one way or the other, create a session for this player
await GameLift.createPlayerSession({
GameSessionId : selectedGameSession.GameSessionId ,
PlayerId: uuid.v4()
}).promise().then(data => {
console.log("Created player session ID: ", data.PlayerSession.PlayerSessionId);
response = data.PlayerSession;
}).catch(err => {
response = err;
});
}
else
{
response = {
statusCode: 500,
body: JSON.stringify({
message: "Unable to find game session, check GameLift API status"
})
};
}
return response;
};
21. GameLiftコンソールに戻り、先程作成したフリードIDを確認する必要があります。フリートIDはダッシュボードもしくはフリートの詳細ページで確認できます。フリードIDをコピーし、Lambda関数内の”MegaFrogRaceFleetID”の値を書き換えます。
22. もしus-east-1リージョン以外を利用している場合、3行目のリージョンをGameLiftクライアントが初期化されるリージョン(現在作業中のリージョン)に変更してください。
23. エディタの右上の“保存”をクリックし、クライアントサービスが準備完了になります。”テスト”ボタンをクリックすることで関数が実行され、ゲームセッションが作成されます。
このクライアントサービスは非常にシンプルです。プレイヤーはゲームに参加するにあたり何の選択も必要ありません。クライアントサービスは既存のゲームセッションを検索します。既存のゲームセッションが存在し、かつそのセッションが参加可能な状態であれば、プレイヤーはそのセッションに参加し、ゲームが開始されます。もし既存の参加可能なゲームセッションが存在しない場合、新しいゲームセッションを作成し、プレイヤーはそちらに参加します。プレイヤーがゲームに参加すると、Lambda関数はクライアントの接続に必要なリアルタイムサーバーインスタンスの情報を返答します。
2番目のタスクは、ゲームクライアントからクライアントサービスを呼び出し可能にすることです。このデモでは、ゲームクライアントのリクエストに対し認証が必要です。Amazon Cognitoを使ってどのように認証を実現するかご紹介しましょう。”Unauthenticated Identity(認証されていないID)”(*)という機能によって、適切な情報を持つすべてのクライアントがこのLambda関数を呼び出すことができるようにします。クライアント側で資格情報の入力をせずにAWSサービスへのアクセスを許可するには、この方法をお勧めします。別の方法として、制限付きのIAMロールにキーを配布する方法がありますが、キーの有効期限が切れたときにゲームクライアントの更新が必要になるため、より不安定でエラーが発生しやすくなります。Amazon Cognitoの”Unauthenticated Identity”を有効にすると、後にクライアントがそのサービスを介してログインし、そこで払い出されたIDに基づいたIDをマッチメイクで利用することが容易になります。
- 別のウインドウまたはタブでAmazon Cognitoコンソールを開きます。
- ”IDプールの管理”をクリックします。
- “新しいIDプールの作成”をクリックします。
- IDプール名に”MegaFrogRaceAnonPool”と入力します。
- “認証されていないID”の下の”認証されていないIDに対してアクセスを有効にする”にチェックを入れます。
- すべてのクライアントがログインなしで接続できるようにするため、認証プロバイダーのセクションはそのままにします。こちらは完全な認証を追加する準備が整ったときに設定する項目となります。
- “プールの作成”をクリックします。
- “詳細の表示”をクリックして内容を展開します。
- “Your unauthenticated identities would like access to Cognito”と書かれているロールにある”ポリシードキュメントを表示”をクリックし、”編集”をクリックします。
- 以下のテキストボックスの内容に置き換えますが、まだ”許可”をクリックしないでください!このコードはこの記事の後半で紹介しているコードサンプルのAWSフォルダーにも保存されています。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Invoke",
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": "arn:aws:lambda:us-east-1:123456789123:function:ConnectClientToServer"
},
{
"Effect": "Allow",
"Action": [
"mobileanalytics:PutEvents",
"cognito-sync:*"
],
"Resource": [
"*"
]
}
]
}
11. AWS Lambdaのコンソールに戻ります。先ほど作成した”ConnectClientToServer”のLambda関数を開き、右上のARN(Amazon Resource Name)にある以下のような文字列をboxアイコンをクリックしてコピーします。
12. Cognitoのポリシードキュメントに戻り、はじめの”Resource”のカッコにあるARNの値を先ほどコピーしたLambdaのARNの値に置き換えます。
13. “許可”をクリックします。
14. Amazon Cognitoでは、認証なしユーザとしてアクセスできるゲームクライアントに追加すべきコードをサンプルとして提供しています。プラットフォームのドロップダウンで”.Net”を選択し、このページをそのまま残しておいてください。後ほどこのソースコードが必要になります。
ステップ3:クライアントとサーバーの通信
次のタスクはクライアントとサーバーが通信するというお楽しみのパートになります!私のプロジェクトはGitHubで公開しています。クライアントを実行するには多少の作業が必要です。
- “MegaFrogRace game project”をGitHubから取得します。
- 以下のコンポーネントを取得し、必要であれば解凍します。
a. GameLift Realtime Client SDK
b. AWS Mobile SDK for Unity
c. Demigiant DOTween - 手順に沿ってGameLift Client SDKをビルドしてください。ご利用のUnityが.NET 4.5と互換性があることを確認してください。(nuGetパッケージの復元後、ビルドを行ってください)
- Unityのプロジェクトを開きます。”File -> Open Project”で上記のGitHubからダウンロードした”MegaFrogRace game project”の”MegaFlogRace”をプロジェクトのフォルダとして選択します。 不足している点に関するエラーや警告は無視します。
- Assetメニューを開き、”Import Package -> Custom Package….”を選択し、AWS Mobile SDKにあるLambdaパッケージをインポートします。Lambdaパッケージには必要なCognito.dllが含まれているため、Cognitoパッケージをインポートする必要はありません。
- プロジェクトの中に“RTSAPI”というAssetフォルダを作成します。Realtime Client SDK buildフォルダにある以下のライブラリを新しいフォルダにドラッグします。
・GameScaleRealTimeClientSDKNet45.dll (GameLiftRealTimeClientSDKNet45.dllでは?)
・Google.Protobuf.dll
・Log4net.dll
・SuperSocket.ClientEngine.dll
・WebSocket4Net.dll - DOTweenをプロジェクトに追加するために、DOTween websiteにある手順を参照してください。今回はダウンロードしたDOTween_1_x_xxx_.zipのファイルを解凍し、解凍後にできたDOTweenファイルをUnityのAssetフォルダの中に保存しました。
- スクリプトファイルRTSClient.csを開き、CognitoAWSCredentialsを作成している行を探します。Identity Pool IDをAmazon Cognitoのサンプルコード(先ほど開いたままにしておいてほしいとお願いしたページ)にあるIDに置き換えます。また、IDを作成したリージョンと一致させるため、リージョンも変更する必要があります。
9. “File -> Build Settings”を開き、”Scenes In Build”パネルのプロジェクトにSceneを追加します。プロジェクトのScenesフォルダの中にある”Game”と”Title”のアイコンを”Scenes In Build”パネルにドラッグ&ドロップします。
10. “Edit -> Project Settings”を開き、”Input”をクリックします。Axesを展開し、Sizeを+2追加した20に変更します。また、2つのinputの名前をHopP1とHopP2に変更します。”Positive Button”はHopP1に”space”キー、HopP2に”right shift”(右シフト)をアサインします。以下が設定内容です。
11. これでスタンドアロンクライアントのビルドと実行が可能になります。タイトルのSceneからゲームがスタートすることを確認してください。Local Multiplayerはすぐに遊べる状態になっています。スペースキーと右シフトキーでカエルをコントロールできます。
GameLiftにアップロード&フリートにデプロイするサーバースクリプトをサーバースクリプトをアップデートする必要があります。最初のステップとして、すでに最小のスクリプトをアップロードしました。今回は新しいゲームクライアントと動作するためのMega Flog Raceサーバスクリプトにアップデートします。対象となるスクリプトファイルはダウンロードしたGitHubプロジェクトの中に含まれています。
- GitHubプロジェクト(https://github.com/tangmi/node-gameloop/blob/master/lib/gameloop.js)の中のServerAppフォルダの中にあるMegaFlogRaceServer.jsファイルを探します。
- サーバースクリプトは“gameloops.js”というコードと依存関係があります。該当のファイルをこちらからダウンロードし、MegaFlogRaceServer.jsファイルと同じディレクトリに配置します。
- 2つのファイルをルートで展開できる(フォルダ内ではない)ようにzipで圧縮します。
- GameLiftコンソールを開き、スクリプトのページに移動します。先程作成したスクリプトコンテナーのスクリプトIDをクリックします。
- スクリプトの詳細ページのアクションをクリックし、”スクリプトの編集”を選択します。
- スクリプトタイプを”Zipファイル”に変更し、上記で作成した.zipファイルを選択します。
- アップロードを実行します。”送信”をクリック後、GameLiftはスクリプトをフリートにデプロイします。(デプロイ完了までは数分かかります。)
以上で、GameLiftnにホストされたマルチプレイヤーモードのMega Frog Raceをプレイする準備が完了しました。2つのゲームクライアントもしくは2つの異なるコンピュータでゲームを実行し、テストしてみましょう。Have Fun!
追加情報
Amazon GameLift リアルタイムサーバー ドキュメント
Amazon GameLift フォーラム(英語のみ)
Amazon Gamelift オフィシャルページ: GameLiftのその他のいろいろな情報を取り扱っています
Amazon GameTech: Amazon GameTechが提供するゲーム向けのさービスやソリューションをご紹介しています
クライアントとサーバー開発に関する注意事項
もしマルチプレイヤーゲームに携わるのが初めてであれば、クライアント/サーバーゲームとしてMega Frog Raceを開発したときのプロセスに関するいくつかの追加情報が役に立つと思います。リアルタイムサーバーを利用すると開発が便利になりますが、シングルプレイヤーでの開発からマインドセットを変える必要があります。具体的には、クライアントとサーバー側で実行すべきコードの特性を把握し、それらの違いを抽象化するための最善な方法を考える必要があります。
これらを簡単にするために私が最初に行ったことは2人のプレイヤーが同じマシン上でゲームをコントロールするローカルマルチプレイヤーのゲームを実装することでした。これにより、別のサーバーコンポーネントを意識することなくゲームのロジックを試すことができます。また、どのロジックがサーバーと通信する必要があるのか、クライアント上に何を残すべきなのかを理解することができました。
最初から基本となるゲームロジックをローカルマシン以外の場所で実行できるアーキテクチャを採用しました。このアーキテクチャの典型的なモデルは次のとおりです。
- すべてのグラフィックスと入力を操作するゲームコントローラー
- ゲームループを実行し、プレイヤーの入力とゲームルールに基づいてゲームの状態を決定するシミュレーション
- ゲームコントローラーとシミュレーションの間でデータを仲裁するゲームクライアント
私はC#でUnity ゲームクライアント上で実行するシミュレーションを開発しました。これにより、ゲームがどのように動作するか、ゲームプレイをどのように調整するかをすばやく試作することができました。ボーナスとして、このゲームは人々がお互いに隣に座ってゲームをプレイすることができる「リビングルームモード」を持っています。 RTSClient クラスを見ると、ファイルの下部にシミュレーションコードが表示されます。 また、HopButtonPressed などのゲームコントローラハンドラでは、シミュレーションがローカルまたはリモートで実行されているかどうかを確認します。
ハンドラ自身にシミュレーションがローカルかどうかをチェックさせることは、私の理想よりも脆弱であることは認めます。しかし、ゲームは非常に小さく、サンプルコードを簡単に追うことができるため、ほとんどの脆弱点を取り除くことができます。 さらに優れたアーキテクチャは、ゲームクライアントの抽象的な概念を作成し、リアルタイムサーバーとローカルシミュレーションの具体的な実装を別々に作成します。 その後、ゲームの開始時に必要なインスタンスをインスタンス化します。 もしあなたがサンプルコードを書いていない場合、次の図のような方法をお勧めします。
このアーキテクチャは、必要に応じて新しいクライアントを追加できるため保守性が高く、ゲームの異なるコンポーネントを単独でテストしやすくなります。
ローカルシミュレーションの作成とテストが完了すれば、ゲームロジックをリアルタイムサーバーで動作するように変換するのは簡単でした。 LocalSimUpdateとLocalSimHopPressed RTSClientのコードは、サーバースクリプトの fGameLoop 関数とProcessHop関数とほぼ同じです。
最後に、イベントがクライアントでトリガーされてからサーバー上に表示されるまでの間に遅延が生じる可能性があることに注意してください。 これは、ゲーム設計の決定に影響を与えるはずです。 プレーヤーがボタンを押し、サーバーがそのボタンに関連する動作を行い、クライアント状態を更新するためにデータを送り返すまでに1 秒かかるということについて、どのような意味があるかを考えてみてください。 補間(Interpolation)はこのような問題を円滑に解決するための重要なツールになります。
サーバーのデバッグに関する注意事項
サーバーをデバッグする最善の方法は、サーバーコードで大量の console.log () ステートメントでログをファイルに出力し、その後ログファイルを調べることです。 ログファイルを調べるには、実行中のサーバーとの SSH セッションを開く必要があります。 以下は私がWindowsで行った手順です。
- AWS CLIツールをこちらからインストールします。
- 以下のコマンドを実行します。(フリートIDとリージョンは、ご自分のGameLiftコンソールに含まれる情報に合わせて変更してください。)こちらのコマンドでサーバーへアクセスするためのSSHポートを取得することができます。
3. 以下のコマンドでサーバインスタンスのリストを取得します。(今回は1つのインスタンスのみの取得になります)フリートIDはご自分の環境のものを指定してください。
4. 前のコマンドで取得したフリートインスタンスIDと、ご自分のフリートとリージョンIDを使って以下のコマンドを実行します。
5. 上記のコマンドはSSHセッションを開始するためのIPアドレスとサーバーにアクセスするためのプライベートキーという2つの情報が含まれるファイルを作成します。”Secret.txt”というファイルをテキストエディタで開きます。(改行コードの扱いの関係でNotepad++を利用されることをお勧めします)
6. クォートに囲まれたシークレットキーの値の内容を元に、Notepad++で新しいファイルを作成します。
7. 検索と置換を開きます。
8. 検索モードを”Extended(\n\r…”に変更します。
9. 検索に”\\n”(バックスラッシュ2つ)を入力します。
10. 置換に”\n”(バックスラッシュ1つ)を入力します。
11. “すべて置換”をクリックし、JSONファイルのすべての該当文字が改行文字に変換されます。
12. ファイル名を“MySecret.pem”として新規保存します。
13. SSHのためのツールが必要です。今回はPuTTYを利用します。ご自分のPCにインストールしてください。
14. Puttygenを実行します。
15. Loadをクリックします。
16. ファイルタイプを”All *.*”に変更します。
17. MySecret.pemファイルを選択します。
18. プライベートキーを”MySecret.ppk”として保存します。
19. PuTTYを実行します。
20. セッションフィールドにSecret.txtファイルから取得したIPアドレスを入力します。
21. “connection/data”のusernameを”gl-user-remote”に変更します。
22. “connection/SSH/Auth”の”private key file for authentication”フィールドにMySecret.ppkファイルを指定します。
23. connectionに戻り、nameに”Saved Sessions”と入力し、saveをクリックします。(次回からは設定無しで接続のロードと再接続が実行できます)
24. Openをクリックし、キーのキャッシュに関する質問ウインドウが表示されたら、”Yes”を選択します。
25. ログイン後、Amazon Linux shellが利用可能になります。
26. 以下を入力し、ログのディレクトリに移動します。
27. 以下を入力してください。こちらのコマンドを実行するとサーバースクリプトとログファイルが表示され、番号付きのフォルダのリストが表示されます。 これらの番号は、実行中または実行中のさまざまなサーバーのプロセス ID です。 ゲームの開始時に最も近いタイムスタンプを持つフォルダーを探します。 ディレクトリをそのフォルダに変更します。
28. 以下を入力してください。”server.log”で始まるログファイルのリストが表示されます。 最新のタイムスタンプを探します。
29. 最新のログファイルは以下のコマンドで開くことができます。スペースキーがページ送りになります。
30. 実行中にサーバーの出力を確認したい場合はtailコマンドが利用できます。
31. サーバーがループでスタックしている、またはサーバースクリプトで終了しなかったゾンビプロセスがある場合は、以下のコマンドで終了します。ゲームセッションとサーバープロセスは自動的に再起動されます。
AWS Summit Tokyo ( 6/12(水) ~ 14(金) 幕張メッセ、6/27(木) グランフロント大阪 )のAmazon Game TechブースではGameLift リアルタイムサーバーをはじめとした数々のゲーム向けソリューションの展示を行います。ぜひご来場ください。
翻訳:ソリューションアーキテクト 吉田