はじめに
テスト環境
- Windows 11
- Unreal Engine 5.4.4(ソースコードビルド版)
- Amazon GameLift Plugin Unreal 2.0.1
- GameLift Cpp Server SDK 5.2.0
前提条件
- AWS アカウントを作成して IAM Identity Center が有効化されている事
- UE をソースコードビルドしてある事
手順
IAM ユーザーの作成
手順1
AWS のコンソールにアクセスし、「IAM Identity Center >ユーザー>ユーザーを追加」を押す。

手順2
「ユーザーの詳細を指定>プライマリ情報」を入力して「次へ」を押す。

手順3
今回はユーザーをグループに追加せずに「次へ」を押す。
(勿論、グループに追加しても OK)

手順4
「ユーザーの確認と追加>ユーザーを追加」でユーザーの作成を完了する。

手順5
IAM ユーザー作成時に「ユーザーの詳細を指定」で設定したメールアドレスを確認して AWS からのメールの「Accept invitation」を押す。

手順6
新規パスワードを入力して「新しいパスワードを設定」を押す。

手順7
「MFA デバイスの登録>認証アプリ」を選択して「Next」を押す。

手順8
認証アプリで QR コードを読み取って認証コードを入力し、「MFA を割り当て」を押す。

手順9
「認証アプリ登録済み>完了」を押す。

許可セットの作成
手順10
IAM ユーザーを作成した画面に戻り、「IAM Identity Center >マルチアカウント許可>許可セット>許可セットを作成」を押す。

手順11
「許可セットタイプを選択」で「許可セットのタイプ>事前定義された許可セット」と「事前定義された許可セットのポリシー>AWS マネージドポリシーを選択> AdministratorAccess」を選択して「次へ」を押す。

手順12
「許可セットの詳細を指定>許可セットの詳細>許可セット名」を入力して「次へ」を押す。

手順13
「確認して作成>作成」を押す。

許可セットの適用
手順14
「IAM Identity Center >マルチアカウント許可> AWS アカウント」で IAM ユーザーを作成したアカウントを選択して「ユーザーまたはグループを割り当て」を押す。

手順15
「グループ」から「ユーザー」にタブを切り替え、IAM ユーザーを選択して「次へ」を押す。

手順16
許可セットを選択して「次へ」を押す。

手順17
内容を確認して「送信」を押す。

手順18
「IAM Identity Center >設定>アイデンティティソース> AWS access portal URL」に移動して IAM ユーザーでログインする。

手順19
ログイン後、許可セット名を押してその IAM ユーザーでコンソールにアクセスできる事を確認する。

サンプルプロジェクトの作成
手順20
ソースコードビルド版の Unreal Editor を開き、「GAMES > Third Person」を選択して「BLUEPRINT」から「C++」に変更し、「Starter Content」にチェックを入れ、「Project Location」と「Project Name」を設定し「Create」を押す。

手順21
こちらを参考にしてプロジェクトに Amazon GameLift Plugin Unreal をインストールし、エディタのツールバーに Amazon GameLift 用のボタンが表示されている状態にする。

手順22
「Edit > Project Settings… > Project > Maps & Modes > Default Maps」で「Editor Startup Map」と「Game Default Map」を「StartupMap」に、「Server Default Map」を「ThirdPersonMap」に変更する。

手順23
「Edit > Project Settings… > Project > Maps & Modes > Default Modes > Advanced > Global Default Server Game Mode」を「{プロジェクト名}GameMode」に変更する。

手順24
「{プロジェクト名}/Content/ThirdPerson/Maps/ThirdPersonMap」を開き、「World Settings > Game Mode > GameMode Override」を「None」に変更する。

コーディング
手順25
「{プロジェクト名}/Source/{プロジェクト名}/{プロジェクト名}.Build.cs」を以下のように修正する。
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class SampleProject : ModuleRules
{
public SampleProject(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });
bEnableExceptions = true;
if (Target.Type == TargetRules.TargetType.Server)
{
PublicDependencyModuleNames.AddRange(new string[] { "GameLiftServerSDK" });
PublicDefinitions.Add("WITH_GAMELIFT=1");
}
else
{
PublicDefinitions.Add("WITH_GAMELIFT=0");
}
}
}
手順26
「{プロジェクト名}/Source/{プロジェクト名}/{プロジェクト名}GameMode.h」を以下のように修正する。
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "SampleProjectGameMode.generated.h"
struct FProcessParameters;
DECLARE_LOG_CATEGORY_EXTERN(LogSampleProjectServer, Log, All);
UCLASS(minimalapi)
class ASampleProjectGameMode : public AGameModeBase
{
GENERATED_BODY()
public:
ASampleProjectGameMode();
protected:
virtual void BeginPlay() override;
private:
TSharedPtr<FProcessParameters> ProcessParameters;
void InitGameLift();
};
手順27
「{プロジェクト名}/Source/{プロジェクト名}/{プロジェクト名}GameMode.cpp」を以下のように修正する。
// Copyright Epic Games, Inc. All Rights Reserved.
#include "SampleProjectGameMode.h"
#include "UObject/ConstructorHelpers.h"
#include "GenericPlatform/GenericPlatformOutputDevices.h"
#if WITH_GAMELIFT
#include "GameLiftServerSDK.h"
#include "GameLiftServerSDKModels.h"
#endif
DEFINE_LOG_CATEGORY(LogSampleProjectServer);
ASampleProjectGameMode::ASampleProjectGameMode():ProcessParameters(nullptr)
{
static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter"));
if (PlayerPawnBPClass.Class != NULL)
{
DefaultPawnClass = PlayerPawnBPClass.Class;
}
UE_LOG(LogSampleProjectServer, Log, TEXT("Initializing ASampleProjectGameMode..."));
}
void ASampleProjectGameMode::BeginPlay()
{
Super::BeginPlay();
#if WITH_GAMELIFT
InitGameLift();
#endif
}
void ASampleProjectGameMode::InitGameLift()
{
#if WITH_GAMELIFT
UE_LOG(LogSampleProjectServer, Log, TEXT("Calling InitGameLift..."));
// Getting the module first.
FGameLiftServerSDKModule* GameLiftSdkModule = &FModuleManager::LoadModuleChecked<FGameLiftServerSDKModule>(FName("GameLiftServerSDK"));
//Define the server parameters for a GameLift Anywhere fleet. These are not needed for a GameLift managed EC2 fleet.
FServerParameters ServerParametersForAnywhere;
bool bIsAnywhereActive = false;
if (FParse::Param(FCommandLine::Get(), TEXT("glAnywhere")))
{
bIsAnywhereActive = true;
}
if (bIsAnywhereActive)
{
UE_LOG(LogSampleProjectServer, Log, TEXT("Configuring server parameters for Anywhere..."));
// If GameLift Anywhere is enabled, parse command line arguments and pass them in the ServerParameters object.
FString glAnywhereWebSocketUrl = "";
if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereWebSocketUrl="), glAnywhereWebSocketUrl))
{
ServerParametersForAnywhere.m_webSocketUrl = TCHAR_TO_UTF8(*glAnywhereWebSocketUrl);
}
FString glAnywhereFleetId = "";
if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereFleetId="), glAnywhereFleetId))
{
ServerParametersForAnywhere.m_fleetId = TCHAR_TO_UTF8(*glAnywhereFleetId);
}
FString glAnywhereProcessId = "";
if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereProcessId="), glAnywhereProcessId))
{
ServerParametersForAnywhere.m_processId = TCHAR_TO_UTF8(*glAnywhereProcessId);
}
else
{
// If no ProcessId is passed as a command line argument, generate a randomized unique string.
FString TimeString = FString::FromInt(std::time(nullptr));
FString ProcessId = "ProcessId_" + TimeString;
ServerParametersForAnywhere.m_processId = TCHAR_TO_UTF8(*ProcessId);
}
FString glAnywhereHostId = "";
if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereHostId="), glAnywhereHostId))
{
ServerParametersForAnywhere.m_hostId = TCHAR_TO_UTF8(*glAnywhereHostId);
}
FString glAnywhereAuthToken = "";
if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereAuthToken="), glAnywhereAuthToken))
{
ServerParametersForAnywhere.m_authToken = TCHAR_TO_UTF8(*glAnywhereAuthToken);
}
FString glAnywhereAwsRegion = "";
if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereAwsRegion="), glAnywhereAwsRegion))
{
ServerParametersForAnywhere.m_awsRegion = TCHAR_TO_UTF8(*glAnywhereAwsRegion);
}
FString glAnywhereAccessKey = "";
if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereAccessKey="), glAnywhereAccessKey))
{
ServerParametersForAnywhere.m_accessKey = TCHAR_TO_UTF8(*glAnywhereAccessKey);
}
FString glAnywhereSecretKey = "";
if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereSecretKey="), glAnywhereSecretKey))
{
ServerParametersForAnywhere.m_secretKey = TCHAR_TO_UTF8(*glAnywhereSecretKey);
}
FString glAnywhereSessionToken = "";
if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereSessionToken="), glAnywhereSessionToken))
{
ServerParametersForAnywhere.m_sessionToken = TCHAR_TO_UTF8(*glAnywhereSessionToken);
}
UE_LOG(LogSampleProjectServer, SetColor, TEXT("%s"), COLOR_YELLOW);
UE_LOG(LogSampleProjectServer, Log, TEXT(">>>> WebSocket URL: %s"), *ServerParametersForAnywhere.m_webSocketUrl);
UE_LOG(LogSampleProjectServer, Log, TEXT(">>>> Fleet ID: %s"), *ServerParametersForAnywhere.m_fleetId);
UE_LOG(LogSampleProjectServer, Log, TEXT(">>>> Process ID: %s"), *ServerParametersForAnywhere.m_processId);
UE_LOG(LogSampleProjectServer, Log, TEXT(">>>> Host ID (Compute Name): %s"), *ServerParametersForAnywhere.m_hostId);
UE_LOG(LogSampleProjectServer, Log, TEXT(">>>> Auth Token: %s"), *ServerParametersForAnywhere.m_authToken);
UE_LOG(LogSampleProjectServer, Log, TEXT(">>>> Aws Region: %s"), *ServerParametersForAnywhere.m_awsRegion);
UE_LOG(LogSampleProjectServer, Log, TEXT(">>>> Access Key: %s"), *ServerParametersForAnywhere.m_accessKey);
UE_LOG(LogSampleProjectServer, Log, TEXT(">>>> Secret Key: %s"), *ServerParametersForAnywhere.m_secretKey);
UE_LOG(LogSampleProjectServer, Log, TEXT(">>>> Session Token: %s"), *ServerParametersForAnywhere.m_sessionToken);
UE_LOG(LogSampleProjectServer, SetColor, TEXT("%s"), COLOR_NONE);
}
UE_LOG(LogSampleProjectServer, Log, TEXT("Initializing the GameLift Server..."));
//InitSDK will establish a local connection with GameLift's agent to enable further communication.
FGameLiftGenericOutcome InitSdkOutcome = GameLiftSdkModule->InitSDK(ServerParametersForAnywhere);
if (InitSdkOutcome.IsSuccess())
{
UE_LOG(LogSampleProjectServer, SetColor, TEXT("%s"), COLOR_GREEN);
UE_LOG(LogSampleProjectServer, Log, TEXT("GameLift InitSDK succeeded!"));
UE_LOG(LogSampleProjectServer, SetColor, TEXT("%s"), COLOR_NONE);
}
else
{
UE_LOG(LogSampleProjectServer, SetColor, TEXT("%s"), COLOR_RED);
UE_LOG(LogSampleProjectServer, Log, TEXT("ERROR: InitSDK failed : ("));
FGameLiftError GameLiftError = InitSdkOutcome.GetError();
UE_LOG(LogSampleProjectServer, Log, TEXT("ERROR: %s"), *GameLiftError.m_errorMessage);
UE_LOG(LogSampleProjectServer, SetColor, TEXT("%s"), COLOR_NONE);
return;
}
ProcessParameters = MakeShared<FProcessParameters>();
//When a game session is created, Amazon GameLift Servers sends an activation request to the game server and passes along the game session object containing game properties and other settings.
//Here is where a game server should take action based on the game session object.
//Once the game server is ready to receive incoming player connections, it should invoke GameLiftServerAPI.ActivateGameSession()
ProcessParameters->OnStartGameSession.BindLambda([=](Aws::GameLift::Server::Model::GameSession InGameSession)
{
FString GameSessionId = FString(InGameSession.GetGameSessionId());
UE_LOG(LogSampleProjectServer, Log, TEXT("GameSession Initializing: %s"), *GameSessionId);
GameLiftSdkModule->ActivateGameSession();
});
//OnProcessTerminate callback. Amazon GameLift Servers will invoke this callback before shutting down an instance hosting this game server.
//It gives this game server a chance to save its state, communicate with services, etc., before being shut down.
//In this case, we simply tell Amazon GameLift Servers we are indeed going to shutdown.
ProcessParameters->OnTerminate.BindLambda([=]()
{
UE_LOG(LogSampleProjectServer, Log, TEXT("Game Server Process is terminating"));
GameLiftSdkModule->ProcessEnding();
});
//This is the HealthCheck callback.
//Amazon GameLift Servers will invoke this callback every 60 seconds or so.
//Here, a game server might want to check the health of dependencies and such.
//Simply return true if healthy, false otherwise.
//The game server has 60 seconds to respond with its health status. Amazon GameLift Servers will default to 'false' if the game server doesn't respond in time.
//In this case, we're always healthy!
ProcessParameters->OnHealthCheck.BindLambda([]()
{
UE_LOG(LogSampleProjectServer, Log, TEXT("Performing Health Check"));
return true;
});
//GameServer.exe -port=7777 LOG=server.mylog
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);
}
}
}
//Here, the game server tells Amazon GameLift Servers where to find game session log files.
//At the end of a game session, Amazon GameLift Servers uploads everything in the specified
//location and stores it in the cloud for access later.
TArray<FString> Logfiles;
Logfiles.Add(TEXT("LogSampleProjectServer/Saved/Logs/LogSampleProjectServer.log"));
ProcessParameters->logParameters = Logfiles;
//The game server calls ProcessReady() to tell Amazon GameLift Servers it's ready to host game sessions.
UE_LOG(LogSampleProjectServer, Log, TEXT("Calling Process Ready..."));
FGameLiftGenericOutcome ProcessReadyOutcome = GameLiftSdkModule->ProcessReady(*ProcessParameters);
if (ProcessReadyOutcome.IsSuccess())
{
UE_LOG(LogSampleProjectServer, SetColor, TEXT("%s"), COLOR_GREEN);
UE_LOG(LogSampleProjectServer, Log, TEXT("Process Ready!"));
UE_LOG(LogSampleProjectServer, SetColor, TEXT("%s"), COLOR_NONE);
}
else
{
UE_LOG(LogSampleProjectServer, SetColor, TEXT("%s"), COLOR_RED);
UE_LOG(LogSampleProjectServer, Log, TEXT("ERROR: Process Ready Failed!"));
FGameLiftError ProcessReadyError = ProcessReadyOutcome.GetError();
UE_LOG(LogSampleProjectServer, Log, TEXT("ERROR: %s"), *ProcessReadyError.m_errorMessage);
UE_LOG(LogSampleProjectServer, SetColor, TEXT("%s"), COLOR_NONE);
}
UE_LOG(LogSampleProjectServer, Log, TEXT("InitGameLift completed!"));
#endif
}
手順28
「{プロジェクト名}/Source/{プロジェクト名}Editor.Target.cs」を複製して「{プロジェクト名}Client.Target.cs」を作成し、以下のように修正する。
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class SampleProjectClientTarget : TargetRules
{
public SampleProjectClientTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Client;
DefaultBuildSettings = BuildSettingsVersion.V5;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_4;
ExtraModuleNames.Add("SampleProject");
}
}
手順29
「{プロジェクト名}/Source/{プロジェクト名}Editor.Target.cs」を複製して「{プロジェクト名}Server.Target.cs」を作成し、以下のように修正する。
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class SampleProjectServerTarget : TargetRules
{
public SampleProjectServerTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Server;
DefaultBuildSettings = BuildSettingsVersion.V5;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_4;
ExtraModuleNames.Add("SampleProject");
}
}
パッケージ化
手順30
Unreal Editor を閉じてプロジェクトのルートフォルダ内にある「Binaries」フォルダと「Intermediate」フォルダを削除する。

手順31
「{プロジェクト名}.uproject」を右クリックして「その他のオプションを確認 > Generate Visual Studio project files」を選択し、ソリューションファイルを再生成する。

手順32
ソリューションファイルを Visual Studio や Rider 等で開き、Configuration が Development Editor の状態と Development Server、Development Client の状態でそれぞれビルドし、Unreal Editor を開く。

手順33
「Platforms > Windows」で「{プロジェクト名}Client」と「{プロジェクト名}Server」でそれぞれ Development でパッケージ化する。

IAM ユーザーの登録
手順34
「Edit > Project Settings… > Plugins > Amazon GameLift > AWS Authentication > User profiles > + Add another profile」を押す。

手順35
AWS access portal に戻り、「アクセスキー」を押し、

下の方に表示された「AWS アクセスキー ID」と「AWS シークレットアクセスキー」を参考にしながら、

Unreal Editor で「Profile name」と「Access key ID」、「Secret access key」、「Default AWS Region」を入力して「Create new profile」を押す。

手順36
追加した IAM ユーザーを選択している状態で「Select profile」を押してから「Bootstrap profile」を押す。

手順37
新たに出てきたウィンドウの「Bootstrap profile」を押す。

手順38
「Bootstrap status」が Active になるまで待つ。

Anywhere Fleet でのローカルマルチプレイ
手順39
「Edit > Project Settings… > Plugins > Amazon GameLift > AWS Authentication > User profiles」に「Successfully configured your AWS user profile.」と表示されていない場合は登録した IAM ユーザーを選択している状態で「Select profile」を押し、「Successfully configured your AWS user profile.」と表示させる。

手順40
「Edit > Project Settings… > Plugins > Amazon GameLift > Getting Started > Test locally with Anywhere」を押す。

手順41
「Set up your game with Amazon GameLift > Path to server build」を「{Windows Server フォルダへのパス}/WindowsServer/{プロジェクト名}/Binaries/Win64/{プロジェクト名}Server.exe」に設定して「Set server build path」を押す。

手順42
「Connect to an Anywhere fleet > Create Anywhere fleet」を押す。

手順43
「Register your workstation > Register compute」を押す。

手順44
「Start Server」を押してサーバーを起動する。

手順45
「Start Client」を押す。

手順46
「Client build executable」に「{Windows Client フォルダへのパス}/WindowsClient/{プロジェクト名}Client.exe」を入力して「Start Client」を押す。

手順47
クライアントが起動したら「Test Anywhere Fleet」を押す。

手順48
「Connect to Server」を押し、複数クライアントでローカルマルチプレイ出来る事を確認する。


最後に
エラーと対処法
IAM ユーザーをプロジェクトに登録しようとすると「Bootstrap status」が Failed になる
IAM ユーザーをプロジェクトに登録しようとすると「Bootstrap status」が Failed になり、アウトプットログに以下のようなエラーが表示される場合は
GameLiftCoreLog: Error: Native log message: The security token included in the request is invalid.
GameLiftCoreLog: Warning: AwsAccountInstanceManager: Account is invalid.
GameLiftCoreLog: Warning: AWSBootstrapProfile: Unable to create AWS credentials profile with an error: GAMELIFT ERROR GENERAL.
AWS access portal の「アクセスキー」を押した際に表示される「オプション 2: AWS 認証情報ファイルにプロファイルを追加する」内のテキストをコピーし、

「C:/Users/{ユーザー名}/.aws」にある credentials ファイルに貼り付けて「region={リージョン名}」を追加し、
[{アカウント ID}_{許可セット名}]
aws_access_key_id={アクセスキー ID}
aws_secret_access_key={シークレットアクセスキー}
aws_session_token={セッショントークン}
region={リージョン名}
Unreal Editor に戻って、新たに追加された IAM ユーザーを選択して「Bootstrap profile」を押すと上手くいく。
Amazon GameLift Plugin Unreal をプロジェクトに含んでいる状態でビルドするとエラーが大量に発生する
「Binaries」フォルダや「Intermediate」フォルダを削除してソリューションファイルを再生成したにも関わらず、Amazon GameLift Plugin Unreal をプロジェクトに含んでいる状態でビルドすると Amazon GameLift Plugin Unreal 関連のエラーが大量に発生する場合は「{プロジェクト名}/Source」内にある「{プロジェクト名}.Target.cs」や「{プロジェクト名}Editor.Target.cs」、「{プロジェクト名}Client.Target.cs」、「{プロジェクト名}Server.Target.cs」に以下のように「bUseAdaptiveUnityBuild = false;」を追加して再びビルドすると上手くいく。
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class SampleProjectServerTarget : TargetRules
{
public SampleProjectServerTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Server;
DefaultBuildSettings = BuildSettingsVersion.V5;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_4;
ExtraModuleNames.Add("SampleProject");
bUseAdaptiveUnityBuild = false; //追加
}
}