Amazon GameLiftのUnreal Engineプラグインを使ってローカルPCやEC2フリートでゲームサーバーを実行してみた

2023.11.06

こんにちは、ゲームソリューション部の入井です。

前回、Amazon GameLiftのUnreal Engine用プラグインが発表されたことや、その導入手順について記事を書きました。

この記事の中で、デプロイ方法としてオンプレミスサーバーをホストにするGameLift Anywhereへデプロイする方法と、EC2フリートにデプロイする方法があると記載しました。

今回は、それらの具体的な手順と動作させてみた結果について書いていきます。なお、基本的な流れは以下の公式ドキュメント記載の手順と同じですが、合間に独自の補足などを入れていきます。

Integrating games with the Amazon GameLift plugin for Unreal Engine - Amazon GameLift

実行環境等

  • Windows 11 Professional
  • Unreal Engine 5.3(ソースビルド)
  • Amazon GameLift Plugin for Unreal Engine v1.0
  • Visual Studio 2022

事前準備

GameLift Anywhere・EC2フリートのいずれを使う場合も、以下の手順を事前に行っておく必要があります。

UEプロジェクトの用意

最初に、プラグインを使用するUEプロジェクトを用意します。

私は、デフォルトで用意されているThird PersonテンプレートからGameLiftTestという名前の新規プロジェクトを作成しました。公式ドキュメントでもThird Personテンプレートを前提に手順が書かれているので、とりあえずプラグインを動かしてみたいという人は、Third Personの利用をお勧めします。(というより、プラグインに用意されている動作テスト用アセットがThird Person向けのものだけのように見える)

また、このプラグインを利用するにはC++でのコード編集が必要となるため、プロジェクト作成時にBlue PrintではなくC++プロジェクトを選択します。

ユーザープロファイルの設定

以前の記事にも書いた通り、このプラグインの利用にはあからじめ用意したIAMユーザーの認証が必要です。

以下のプラグイン設定画面でIAMユーザーのアクセスキー・シークレットキーを入力した上で「Bootstrap and update profile」をクリックすると、指定したリージョンのS3にプロジェクト構成やビルド成果物等を保存するためのバケットが作成され、プラグイン内にもプロフィール情報が保存されます。このプロフィール情報を使って各種デプロイを行います。

このような処理を行う関係上、使用するIAMユーザーにはGameLiftだけでなくS3の読み書き権限も最低限必要です。EC2フリートへのデプロイを行う場合は更に多くの権限が必要となりますが、それについては後述します。

通信用ソースコードの追加

GameModeファイルを編集し、ゲームサーバーとAmazon GameLift間の通信を行うコードを追加します。GameModeファイルは、Unreal Engineのマルチプレイにおいての基本的なルールを記述するためのものです。

まず、ヘッダファイルにクラスのメンバ関数やクラス変数を定義します。

コード編集のために、プロジェクトルートにある.slnファイル(例: GameLiftTest.sln)をVisual Studioで開きます。プロジェクトが開いたら、既に作成されているGameModeのヘッダファイルを探します。デフォルトでは、[UEプロジェクト]/Source/[プロジェクト名]/[プロジェクト名]GameMode.cppに存在します。

私の場合はヘッダファイルを以下のように編集しました(クラス名以外公式ドキュメントそのまま)

GameLiftTestGameMode.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "GameLiftTestGameMode.generated.h"

struct FProcessParameters;

DECLARE_LOG_CATEGORY_EXTERN(GameServerLog, Log, All);

UCLASS(minimalapi)
class AGameLiftTestGameMode : public AGameModeBase
{
    GENERATED_BODY()

public:
    AGameLiftTestGameMode();

protected:
    virtual void BeginPlay() override;

private:
    void InitGameLift();

private:
    TSharedPtr<FProcessParameters> ProcessParameters;

};

続いて、同じディレクトリに存在するソースファイルを編集します。私の場合は以下のようになり、これも大部分は公式ドキュメントのコードと同じです。

コードの意味については、日本語コメントで説明を追加しています。主に、ゲームサーバーの基本的な設定をパラメータからセットする処理や、GameLiftが関係する様々なイベント発生時に実行するゲームサーバー側処理の定義などを書いています。

GameLiftTestGameMode.cpp

#include "GameLiftTestGameMode.h"
#include "GameLiftTestCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include "Kismet/GameplayStatics.h"

#if WITH_GAMELIFT
#include "GameLiftServerSDK.h"
#include "GameLiftServerSDKModels.h"
#endif

#include "GenericPlatform/GenericPlatformOutputDevices.h"

DEFINE_LOG_CATEGORY(GameServerLog);

AGameLiftTestGameMode::AGameLiftTestGameMode() :
    ProcessParameters(nullptr)
{
    // コンストラクタでプレイヤーが操作する対象のクラスを定義
    // この場合は、BP_ThirdPersonCharacter(デフォルトのマネキン)を選択
    static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter"));
    if (PlayerPawnBPClass.Class != NULL)
    {
        DefaultPawnClass = PlayerPawnBPClass.Class;
    }

    UE_LOG(GameServerLog, Log, TEXT("Initializing AGameLiftServerGameMode..."));
}

// ゲーム開始時実行
void AGameLiftTestGameMode::BeginPlay()
{
    Super::BeginPlay();
 // マクロ定義WITH_GAMELIFTがtrueの場合のみInitGameLift()をコンパイル
#if WITH_GAMELIFT
    InitGameLift();
#endif
}

void AGameLiftTestGameMode::InitGameLift()
{
// BeginPlay()同様、マクロ定義WITH_GAMELIFTがtrueの場合のみ以降のコードをコンパイル
#if WITH_GAMELIFT
    UE_LOG(GameServerLog, Log, TEXT("Calling InitGameLift..."));

    // SDKのモジュール取得
    FGameLiftServerSDKModule* GameLiftSdkModule = &FModuleManager::LoadModuleChecked<FGameLiftServerSDKModule>(FName("GameLiftServerSDK"));

    // GameLift Anywhereのパラメータ用変数定義
    FServerParameters ServerParametersForAnywhere;

    // コマンドライン引数からGameLift Anywhere環境か否かを判定
    bool bIsAnywhereActive = false;
    if (FParse::Param(FCommandLine::Get(), TEXT("glAnywhere")))
    {
        bIsAnywhereActive = true;
    }

    // Anywhereの場合の処理
    if (bIsAnywhereActive)
    {
        UE_LOG(GameServerLog, Log, TEXT("Configuring server parameters for Anywhere..."));

        // 以下、さまざまなサーバパラメータをコマンドライン引数から取得し、専用変数へ格納する処理の繰り返し

        // WebSocketのURL
        FString glAnywhereWebSocketUrl = "";
        if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereWebSocketUrl="), glAnywhereWebSocketUrl))
        {
            ServerParametersForAnywhere.m_webSocketUrl = TCHAR_TO_UTF8(*glAnywhereWebSocketUrl);
        }

        // AnywhereのフリートID
        FString glAnywhereFleetId = "";
        if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereFleetId="), glAnywhereFleetId))
        {
            ServerParametersForAnywhere.m_fleetId = TCHAR_TO_UTF8(*glAnywhereFleetId);
        }

        // AnywhereのプロセスID
        FString glAnywhereProcessId = "";
        if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereProcessId="), glAnywhereProcessId))
        {
            ServerParametersForAnywhere.m_processId = TCHAR_TO_UTF8(*glAnywhereProcessId);
        }
        else
        {
            // プロセスIDがコマンドライン引数にない場合、ランダムな文字列をIDとしてセット
            ServerParametersForAnywhere.m_processId =
                TCHAR_TO_UTF8(
                    *FText::Format(
                        FText::FromString("ProcessId_{0}"),
                        FText::AsNumber(std::time(nullptr))
                    ).ToString()
                );
        }

        // AnywhereのホストID
        FString glAnywhereHostId = "";
        if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereHostId="), glAnywhereHostId))
        {
            ServerParametersForAnywhere.m_hostId = TCHAR_TO_UTF8(*glAnywhereHostId);
        }

        // Anywhereの認証トークン
        FString glAnywhereAuthToken = "";
        if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereAuthToken="), glAnywhereAuthToken))
        {
            ServerParametersForAnywhere.m_authToken = TCHAR_TO_UTF8(*glAnywhereAuthToken);
        }

        // 各パラメータの内容をログ書き出し
        UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_YELLOW);
        UE_LOG(GameServerLog, Log, TEXT(">>>> Web Socket URL: %s"), *ServerParametersForAnywhere.m_webSocketUrl);
        UE_LOG(GameServerLog, Log, TEXT(">>>> Fleet ID: %s"), *ServerParametersForAnywhere.m_fleetId);
        UE_LOG(GameServerLog, Log, TEXT(">>>> Process ID: %s"), *ServerParametersForAnywhere.m_processId);
        UE_LOG(GameServerLog, Log, TEXT(">>>> Host ID (Compute Name): %s"), *ServerParametersForAnywhere.m_hostId);
        UE_LOG(GameServerLog, Log, TEXT(">>>> Auth Token: %s"), *ServerParametersForAnywhere.m_authToken);
        UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_NONE);
    }

    UE_LOG(GameServerLog, Log, TEXT("Initializing the GameLift Server..."));

    // GameLiftエージェントとSDKの接続確立
    FGameLiftGenericOutcome InitSdkOutcome = GameLiftSdkModule->InitSDK(ServerParametersForAnywhere);
    if (InitSdkOutcome.IsSuccess())
    {
        UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_GREEN);
        UE_LOG(GameServerLog, Log, TEXT("GameLift InitSDK succeeded!"));
        UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_NONE);
    }
    else
    {
        UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_RED);
        UE_LOG(GameServerLog, Log, TEXT("ERROR: InitSDK failed : ("));
        FGameLiftError GameLiftError = InitSdkOutcome.GetError();
        UE_LOG(GameServerLog, Log, TEXT("ERROR: %s"), *GameLiftError.m_errorMessage);
        UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_NONE);
        return;
    }

    ProcessParameters = MakeShared<FProcessParameters>();

    // ゲームセッション開始イベント時の処理を定義
    // プレイヤーを接続させる準備が整ったことをGameLiftへ通知
    ProcessParameters->OnStartGameSession.BindLambda([=](Aws::GameLift::Server::Model::GameSession InGameSession)
    {
        FString GameSessionId = FString(InGameSession.GetGameSessionId());
        UE_LOG(GameServerLog, Log, TEXT("GameSession Initializing: %s"), *GameSessionId);
        GameLiftSdkModule->ActivateGameSession();
    });

    // GameLiftがゲームプロセスの終了を要求した際の処理を定義
    // サーバーがシャットダウンすることをGameLiftへ通知
    ProcessParameters->OnTerminate.BindLambda([=]()
    {
        UE_LOG(GameServerLog, Log, TEXT("Game Server Process is terminating"));
        GameLiftSdkModule->ProcessEnding();
    });

    // GameLiftが60秒ごとに呼び出すヘルスチェック処理を定義
    // ヘルスチェック開始から60秒以内にtrueが返されないと、GameLiftはヘルスチェック失敗と判断する
    ProcessParameters->OnHealthCheck.BindLambda([]()
    {
        UE_LOG(GameServerLog, Log, TEXT("Performing Health Check"));
        return true;
    });

    // コマンドラインで指定されたポート番号のセット
    ProcessParameters->port = FURL::UrlConfig.DefaultPort;
    TArray<FString> CommandLineTokens;
    TArray<FString> CommandLineSwitches;

    FCommandLine::Parse(FCommandLine::Get(), CommandLineTokens, CommandLineSwitches);

    for (FString SwitchStr : CommandLineSwitches)
    {
        FString Key;
        FString Value;

        if (SwitchStr.Split("=", &Key, &Value))
        {
            if (Key.Equals("port"))
            {
                ProcessParameters->port = FCString::Atoi(*Value);
            }
        }
    }

    // ログファイルのパス設定
    TArray<FString> Logfiles;
    Logfiles.Add(TEXT("GameServerLog/Saved/Logs/GameServerLog.log"));
    ProcessParameters->logParameters = Logfiles;

    // GameLiftへこのプロセスがゲームセッションをホストできる状態であることを通知する
    UE_LOG(GameServerLog, Log, TEXT("Calling Process Ready..."));
    FGameLiftGenericOutcome ProcessReadyOutcome = GameLiftSdkModule->ProcessReady(*ProcessParameters);

    if (ProcessReadyOutcome.IsSuccess())
    {
        UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_GREEN);
        UE_LOG(GameServerLog, Log, TEXT("Process Ready!"));
        UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_NONE);
    }
    else
    {
        UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_RED);
        UE_LOG(GameServerLog, Log, TEXT("ERROR: Process Ready Failed!"));
        FGameLiftError ProcessReadyError = ProcessReadyOutcome.GetError();
        UE_LOG(GameServerLog, Log, TEXT("ERROR: %s"), *ProcessReadyError.m_errorMessage);
        UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_NONE);
    }

    UE_LOG(GameServerLog, Log, TEXT("InitGameLift completed!"));
#endif
}

クライアントのゲームマップ設定

このプラグインには、クライアントがゲームサーバーとの接続を行うための処理が含まれたマップアセットが含まれています。

マップアセットには、以下のような処理が用意されています。

  • ゲームセッションの検索
  • ゲームセッションの新規作成
  • ゲームセッションへの参加
  • Amazon Cognitoユーザープールの認証・ユーザー情報取得

また、デプロイ方法がGameLift Anywhere、EC2フリートのどちらであっても同じマップで対応可能になっています。

このマップをエディタやビルドしたクライアントで使用するには、Project Settings → Project → Map & Modesを開き、以下のように設定します。

  • Default Maps設定
    • Editor Startup MapStartupMapを設定
      • UEエディタでのプレビュー時に最初にStartupMapがロードされる
    • Game Default MapStartupMapを設定
      • ビルドされたクライアントで最初にStartupMapがロードされる
    • Server Default MapThirdPersonMapを設定
      • Dedicated Server起動時に最初にThirdPersonMapがロードされる
      • Third Personテンプレートに付属のマップ
  • Default Modes設定
    • Global Default Server Game Modeに先ほどの手順でコードを追加したGameModeを設定
      • Dedicated Server起動時に指定したGameModeがロードされる

これらの設定によりどのようなマップがロードされるかについては、後ほど紹介します。

サーバー・クライアントのビルド

ビルド実行の前に、ターゲットファイルを編集してビルド設定を調整する必要があります。

以下の手順は、プロジェクト名がGameLiftTestであることを前提にしています。

  1. GameLiftTest/Source/GameLiftTestEditor.Target.csをコピーして、同じディレクトリ内にGameLiftTestClient.Target.csGameLiftTestServer.Target.csの2つのファイルを作成する。
  2. それぞれのファイルを以下のように編集する

GameLiftTestClient.Target.cs

using UnrealBuildTool;
using System.Collections.Generic;

public class GameLiftTestClientTarget : TargetRules
{
    public GameLiftTestClientTarget(TargetInfo Target) : base(Target)
    {
        // ビルドターゲットタイプにClientを指定
        Type = TargetType.Client;
        // 以下2つはUEのバージョンによって異なる
        DefaultBuildSettings = BuildSettingsVersion.V4;
        IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_3;

        ExtraModuleNames.Add("GameLiftTest");
    }
}

GameLiftTestServer.Target.cs

using UnrealBuildTool;
using System.Collections.Generic;

public class GameLiftTestServerTarget : TargetRules
{
    public GameLiftTestServerTarget(TargetInfo Target) : base(Target)
    {
        // ビルドターゲットタイプにServerを指定
        Type = TargetType.Server;
        // 以下2つはUEのバージョンによって異なる
        DefaultBuildSettings = BuildSettingsVersion.V4;
        IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_3;

        ExtraModuleNames.Add("GameLiftTest");
    }
}
  1. GameLiftTest/Source/GameLiftTest/GameLiftTest.Build.csについて、以下のように編集する。
using UnrealBuildTool;

public class GameLiftTest : ModuleRules
{
    public GameLiftTest(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });

        bEnableExceptions = true;
                // サーバービルドか否かでServerSDKモジュールの参照やマクロ定義WITH_GAMELIFTの値を変えている
        if (Target.Type == TargetRules.TargetType.Server)
        {
            PublicDependencyModuleNames.AddRange(new string[] { "GameLiftServerSDK" });
            PublicDefinitions.Add("WITH_GAMELIFT=1");
        }
        else
        {
            PublicDefinitions.Add("WITH_GAMELIFT=0");
        }
    }
}
  1. Visual Studio内でソリューションのリビルドを実行。
  2. UEディタを開き、以下の手順でクライアントとサーバーそれぞれのビルドを実行。 a. サーバービルド 1. Platforms → Windows → GameLiftTestServerを選択 2. Platforms → Windows → Package Projectを選択 3. ビルドの出力先を選択してビルド実行 4. 指定した出力先にGameLiftTestServer.exeなどが出力されていることを確認 b. クライアントビルド 1. Platforms → Windows → GameLiftTestClientを選択 2. Platforms → Windows → Package Projectを選択 3. ビルドの出力先を選択してビルド実行 4. 指定した出力先にGameLiftTestClient.exeなどが出力されていることを確認

それぞれのビルドは1時間程度かかることもあるのでご注意ください。

Amazon GameLift Anywhereでのゲームサーバーの実行

事前準備が完了したところで、GameLift Anywhereを使ったマルチプレイの動作テストを行います。

Anywhereを実行するサーバーの設定

UEエディタからHost with Anywhereを選択します。

Anywhereの設定画面は、上から順番に必要な項目を設定していくことで、最終的にマルチプレイを実行する環境が整うようになっています。

最初に、あらかじめ作成しておいたユーザープロファイルを設定します。Bootstrap statusがActiveになっていたら問題なく設定できています。

続いて、サーバーとクライアントそれぞれの実行ファイルのパスを設定します。これは、事前準備で出力されたものを指定します。

つづいて、マルチプレイに使用するGameLift Anywhereフリートを作成します。Create new Anywhere fleetボタンをクリックすることで、新しいフリートが自動的に作成されます。

次に、ローカルネットワーク内のどのデバイスをAnywhereフロートのリソースとして使用するかを設定します。デフォルトでは、UEを実行しているデバイスのIPが指定されています。別のデバイスを使用したい場合、ここでそのIPアドレスを入力します。

最後に、ローカルのデバイスとGameLift間での通信に使用する認証トークンをの項目がありますが、こちらはゲームサーバーを起動するたびに自動的に生成されるため、特にやることはありません。

ただ、トークンの期限は15分となっているため、それを過ぎたら再度発行する必要があります。

ゲームの実行

ここまで設定が完了したら、最後にボタンをクリックしてサーバーとクライアントを起動します。

サーバーを起動すると、実行対象のデバイス上でターミナルが立ち上がります。

クライアントを起動すると、StartupMapというマップ上でキャラクターを操作できる画面が表示されます。画面右上にTest Anywhere Fleetというボタンがあり、これをクリックすることでクライアントがサーバーにアクセスできます。

実際にAnywhereでのマルチプレイを実行している様子は、以下の動画で紹介しています。複数クライアントを立ち上げてサーバーにアクセスすることで、それぞれのプレイヤーの位置情報がAnywhereサーバーを通して共有されています。

Amazon EC2フリートでのゲームサーバーの実行

続いて、EC2フリートでのマルチプレイの動作テストを行います。基本的な流れはAnywhereと似ていますが、こちらの場合はサーバーはAWS環境で動作しています。

AWS環境で使用するサーバービルドの用意

EC2フリートへのデプロイを行うにあたり、サーバービルドを用意する必要があります。

大部分は事前準備でビルドしたファイルのままで問題ありませんが、サーバービルドのディレクトリ内にいくつか追加する必要があります。

  • インストールスクリプト
    • プラグインディレクトリ内からコピー
      • [UEプロジェクト]/Plugins/Resources/CloudFormation/extra_server_resources/install.bat
    • EC2リソースにサーバービルドをインストールするのに必要
  • Visual C++ 再頒布可能パッケージ
    • Visual Studioのディレクトリ内からコピー(以下のパスはVS2022の例)
      • C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Redist/MSVC/v143/VC_redist.x64.exe
    • C++のランタイム
  • OpenSSL DLL
    • ビルドにしようしたものと同じバージョンのDLLが必要
    • 以下の2つをコピー(パスはOpenSSLのインストール場所による)
      • C:/Program Files/OpenSSL-Win64/libssl-3-x64.dll
      • C:/Program Files/OpenSSL-Win64/libcrypto-3-x64.dll

シナリオの選択

UEエディタからHost with Managed EC2を選択します。

こちらの設定画面もAnywhere同様上から順番に必要な項目を設定していくことで、最終的にEC2フリートでマルチプレイを実行する環境が整います。

プロファイルなどの設定はAnywhereと同様なので飛ばし、3. Selectt deployment senarioまで進みます。

ここでは、AWS上でどのような環境を構築するかを以下の3つのうちから選択します。

  • Single-region fleet
    • その名の通り単一のフリートにサーバーをデプロイします。
    • 主にテストの用途で使用します。
  • Spot fleet
    • 複数のロケーションで実行される3つのフリートにサーバーをデプロイします。
      • 1つはバックアップ用
    • 実稼働、あるいはそれに近い環境にデプロイしたい時に使用します。
  • FlexMatch fleet
    • スポットフリート同様複数のフリートにサーバーをデプロイするとともに、FlexMatchのセットアップも行います。

更に詳細な情報は公式ドキュメントをご参照ください。

今回は最低限の環境で良いので、Single-region fleetを選択します。

ゲームパラメータの設定

続いて、デプロイするゲームの様々な情報を設定します。

  • Server build name
    • サーバービルドの名前
    • AWSでデプロイされる各種リソースにここで設定した名前が使用される
    • 特に理由がなければUEプロジェクト名の流用で良い
  • Server build OS
    • サーバー実行環境のOSを指定
    • 今回はWindows用にビルドしたので、Windows Server 2016を選択
    • 他にはAmazon Linuxが選択できる
  • Server build folder
    • サーバービルドの出力先ディレクトリを指定
  • Server build executable
    • サーバービルドのexeファイルのパスを指定
  • Client configuration output path
    • クライアントがAWSとの通信などに使用する設定ファイルの保存先
    • 以下のようにクライアントビルド内のディレクトリを指定
      • C:/Users/irii.keita/Documents/Unreal Projects/GameLiftTest/WindowsClient/GameLiftTest/Content/CloudFormation

CloudFormationのデプロイ

『Deploy scenario』ボタンをクリックすることで、このプロジェクト用に生成されたCloudFormationテンプレートを元にAWS上でリソースの構築が始まります。リージョンは、プロファイルで指定したリージョンが自動的に選択されます。

CloudFormationでは、GameLift以外にもCognitoやLambdaなど、今回のマルチプレイ環境に必要な様々なリソースが構築されるため、プロファイルに指定したIAMユーザーはそれぞれのサービスについて十分な権限を持っている必要があります。

以下は、今回私が使用したIAMポリシーの例です。動かすこと優先でかなり強めの権限を割り当てていますが、セキュリティを考えるともっと弱めたほうが良いでしょう。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "gamelift:*",
                "s3:*",
                "cloudformation:*",
                "ssm:*",
                "apigateway:*",
                "cognito-idp:*",
                "lambda:*",
                "iam:GetRole",
                "iam:CreateRole",
                "iam:AttachRolePolicy",
                "iam:GetRolePolicy",
                "iam:PutRolePolicy",
                "iam:DeleteRole",
                "iam:DeleteRolePolicy",
                "iam:DetachRolePolicy",
                "iam:PassRole",
                "wafv2:CreateWebACL",
                "wafv2:GetWebACL",
                "wafv2:UpdateWebACL",
                "wafv2:DeleteWebACL",
                "wafv2:ListTagsForResource",
                "wafv2:ListWebACLs",
                "wafv2:ListResourcesForWebACL",
                "wafv2:GetWebACLForResource",
                "wafv2:AssociateWebACL",
                "wafv2:DisassociateWebACL"
            ],
            "Resource": "*"
        }
    ]
}

CloudFormationのスタック作成はだいたい40分程度で完了し、結果がUEエディタ上でも確認できます。

ゲームの実行

ここまで設定が完了したら、最後にクライアントを起動します。

プラグインの画面にRun Clientというボタンがありますが、これをクリックしても上手くクライアントが起動しない場合があります。というのも、どうやら指定したパス内の空白文字を飛ばして読んでしまうバグがあるようで、『Unreal Project』のようなパスがあると正常に動作しません。

上記の理由から、上手く起動しない場合はクライアントのexeファイルを直接実行する必要があります。この時、そのまま実行するとフルスクリーンモードで起動し、複数のクライアントを起動したい場合はやりづらいので、以下のサイト等を参考にiniファイルの設定を変えてウィンドウモードにすると良いでしょう。

(UE4)フルスクリーン/ウィンドウモードの切り替え

クライアントを起動すると、Anywhereと同様にStartupマップが表示されます。今回は、Test Cloud Deploymentを選択します。

マップが切り替わり、EC2フリートにアクセスするためのユーザ名(メールアドレス)・パスワードを入力する項目が表示されます。最初はまだユーザーが存在しないため、各項目に入力をした上でSign Upボタンをクリックします。すると、指定したメールアドレスに認証コードが送られるので、それをクライアント画面上で入力すると認証が完了します。

なお、この認証関係のサーバーサイド処理はAmazon Cognitoで動いており、ユーザーを作成したあとにユーザープールを確認すると入力した内容が反映されているのが分かります。

認証が完了すると、マルチプレイサーバーにアクセスができます。

こちらも、実際にマルチプレイを実行している様子は以下の動画で紹介しています。Anywhere同様複数クライアントを立ち上げて、それぞれのプレイヤーの位置情報がEC2フリートのゲームサーバーを通して共有されています。

まとめ

Amazon GameLiftのUnreal Engineプラグインによる、Gamelift Anywhere、EC2フリートを使ったマルチプレイの動作テストを行う手順をご紹介しました。

前段階での準備は色々と必要ではありますが、サーバーサイド側の構築はこのプラグインを通すことでかなり手順を省略することができ、開発段階でGameLiftを使ったテストを行うには有用なプラグインだと思いました。

今回は公式ドキュメントを元にマルチプレイを動作させる流れをご紹介しましたが、クライアントからGameLiftへどのようなアクセスが行われているのかがあまり扱えていなかったため、次回の記事ではそのあたりの部分についても書いていきたいと思います。