概要
このページの内容
このページでは、ゲームの Online Services の機能の構成と実装について説明します。このチュートリアルでは、Online Services プラグインがサポートするすべてのインターフェースの実装について説明するのではなく、Online Services プラグインの一般的なプログラミング パターンを示す、よりシンプルなインターフェースである Online Services Title File インターフェースについて説明します。このページでは、以下の方法について説明します。
- Online Services からローカル プレイヤーの情報を取得する。
- バックエンド サービスからタイトル ファイルを取得する。
- ゲームでタイトル ファイルの内容を表示する。
このガイドでは、OnlineSample というタイトルのプロジェクトと Online Services Null プラグインを使用します。
開始する前に
ゲームで使用するために Online Services プラグインを有効にして設定していることを確認してください。まだ設定していない場合は、「Online Services プラグインをセットアップおよび設定する」ドキュメント ページを参照してください。
作業内容
まず、現在ログインしているローカル プレイヤーの情報を取得します。このローカル プレイヤーのアカウント ID は、他のすべての Online Services プラグイン操作のパラメータとして使用されます。この情報を取得する方法を理解すると、他のすべての Online Services の機能を実行できます。このガイドでは、Online Services Null プラグインを使用します。ユーザーは自動的に Null サービスに登録されるため、ログイン関数を呼び出す必要はありません。そのため、明示的にログイン呼び出しを行う必要はありませんが、Online Services からローカル ユーザーのオンライン情報を取得する必要があります。
次に、バックエンドの Online Services からタイトル ファイルを取得します。このチュートリアルでは Online Services Null プラグインを使用するため、タイトル ファイルとその内容は エンジンのコンフィギュレーション に追加されます。Title File インターフェースは、バックエンドの Online Services からのタイトル ファイルのクエリと読み取りを処理します。
最後に、タイトル ファイルを画面に表示します。これは、ファイルが正常に取得されたことを視覚的に確認するものです。
設定
前述のとおり、このガイドでは Online Services Null プラグインを使用します。このプラグインは、Online Services の実装をテストおよびデバッグするために設計されています。Null サービスでは、タイトル ファイルを保存するバックエンド サービスを提供しません。そのため、エンジン設定を使用してこのストレージをシミュレートします。
Null プラグインにタイトル ファイルを追加するには、次の手順を実行します。
- プロジェクトを Visual Studio で開きます。これを行うには、Unreal Editor 内で [Tools (ツール)] > [Open Visual Studio (Visual Studio を開く)] に移動します。
- プロジェクトの「
DefaultEngine.ini」ファイルを Visual Studio の [Solution Explorer (ソリューション エクスプローラー)] で開くには、[Games (ゲーム)] > [YOUR_GAME] > [Config (コンフィグ)] > DefaultEngine.ini に移動します。 -
以下をプロジェクトの「
DefaultEngine.ini」に追加します。DefaultEngine.ini
; Null Platform Configuration [OnlineServices.Null.TitleFile] +Files=(Name=StatusFile, Contents="Explore this virtual world with me!")
構造
ゲーム インスタンスを追加する
Online Services プラグインの機能を使用するには、使用したい Online Services を実装する C++ クラスを作成する必要があります。このチュートリアルでは、Game Instance クラスを使用します。ほとんどのゲーム フレームワーク クラスは、レベルまたはマップ間で再インスタンス化されます。これは、ゲーム フレームワーク クラスに含まれる情報が、レベルやマップ間でリセットされたり、失われたりすることを意味します。ゲーム インスタンスとそのサブシステムは、初期化からシャットダウンまで、ゲームの存続期間全体にわたって保持されます。その結果、ゲーム インスタンスとそのサブシステムは、あるマップやレベルから別のマップやレベルに情報を渡すための永続的な構造として機能します。このウォークスルーでは、ゲーム インスタンス クラスの名前は OnlineSampleGameInstance です。
ゲーム インスタンス クラスを追加するには、Unreal Editor の C++ クラス ウィザード を使用して、以下の情報を使用した新しい C++ クラスを作成します。
- クラス:Game Instance
- 名前:OnlineSampleGameInstance
- パス:../OnlineSample/Source/OnlineSample/GameInstance
Unreal Editor は、Unreal Engine のプロジェクト コードに新しいクラスを追加し、ライブ コーディング を初期化します。これにより Visual Studio コードが再コンパイルされ、Unreal Editor が引き続き開いている間に新しいクラスが表示されます。新しいクラス ファイルにコードを追加できるように、新しい .cpp ファイルと .h ファイルも Visual Studio で開きます。
Visual Studio に、ソリューション ファイルが更新されたためプロジェクトをリロードする必要があることを示すポップアップ ウィンドウが表示される場合があります。[Reload All (すべてリロード)] を選択して、Visual Studio でプロジェクトをリロードします。インクルード ファイル「GameInstance/OnlineSampleGameInstance.h」を開くことができず、ビルドに失敗した場合は、「OnlineSampleGameInstance.cpp」を開き、以下の行を編集します。
#include "GameInstance/OnlineSampleGameInstance.h"
次のように変更します。
#include "OnlineSample/GameInstance/OnlineSampleGameInstance.h"
ここで、OnlineSample はプロジェクトの名前です。これを変更したら、Unreal Editor に戻り、ライブ コーディングを使用してプロジェクトを再コンパイルします。これで、プロジェクトで適切にヘッダ ファイルが特定され、正常にビルドされます。
プロジェクトのゲーム インスタンス クラスを指定する
プロジェクト用のゲーム インスタンス クラスを作成したら、デフォルトのゲーム インスタンス クラスではなく、新しく作成したゲーム インスタンス クラスを使用するようにエンジンに指示する必要があります。
プロジェクトのゲーム インスタンス クラスを指定するには、次の手順を実行します。
- Unreal Editor に移動します。
- メニューバーから [Edit (編集)] > [Project Settings (プロジェクト設定)] を選択します。
- 左側で [Project (プロジェクト)] > [Maps & Modes (マップ & モード)] を選択します。
- [Game Instance (ゲーム インスタンス)] セクションを見つけ、上記の ゲーム インスタンスを追加する セクションで作成した ゲーム インスタンス クラス を選択します。このガイドと同じ名前を使用している場合は、OnlineSampleGameInstance を選択してください。
これで、プロジェクトでカスタム ゲーム インスタンス クラスがデフォルトで使用できるようになります。
ゲーム インスタンス サブシステムを追加する
このウォークスルーでは、ゲーム インスタンス サブシステム を使用して、ゲーム インスタンス構造内のオンライン コードを整理します。プログラミング サブシステムは、それぞれが特定のフォーカスを持つモジュラー システムにコードを整理するのに役立ちます。
Game Instance Subsystem クラスを追加するには、Unreal Editor の C++ クラス ウィザード を使用して、次の情報を使用した新しい C++ クラスを作成します。
- クラス:Game Instance Subsystem
- 名前:OnlineSampleOnlineSubsystem
- パス:../OnlineSample/Source/OnlineSample/GameInstance
Unreal Editor は、Unreal Engine のプロジェクト コードに新しいクラスを追加し、ライブ コーディングを初期化して、プロジェクトを再コンパイルします。
プレイヤー コントローラーを追加する
Player Controller クラスは、ゲーム コード内でゲームをプレイする物理的な人物を抽象化したものです。このクラスは、オンライン ユーザー登録とタイトル ファイルの読み取りがゲーム内で呼び出されるBeginPlay 関数を提供します。
Player Controller クラスを追加するには、Unreal Editor の C++ クラス ウィザード を使用して、次の情報を使用した新しい C++ クラスを作成します。
- クラス:Player Controller
- 名前:OnlineSamplePlayerController
- パス:../OnlineSample/Source/OnlineSample/Player
先ほどと同様に、Unreal Editor は、Unreal Engine のプロジェクト コードに新しいクラスを追加し、ライブ コーディング を初期化して、プロジェクトを再コンパイルします。
コードを追加する
プロジェクトの構造と必要な各種ファイルの設定が完了したので、次は機能を実装します。このセクションには、プロジェクトを構成する セクションで作成した各 C++ クラスのヘッダとソース ファイルのサンプル コードと、既存のプロジェクト ファイルを編集するためのサブセクションが含まれています。
これらのファイルには、コードのコメント、ログ、エラー処理が含まれており、関係するオブジェクトの詳細、ログ内のアクティビティや問題の追跡、エラーの診断に役立ちます。
ライブ コーディングを使用しており、ライブ コーディング コンソールに [Build failed (ビルドに失敗しました)] というログ メッセージが表示され、失敗の理由を診断できない場合は、Unreal Editor を終了し、代わりに Visual Studio でビルドしてみてください。
ゲーム インスタンス
ゲーム インスタンスとそのサブシステムは、初期化からシャットダウンまで、ゲームの存続期間全体にわたって保持されます。つまり、ゲームの初期化からシャットダウンまで、同じゲーム インスタンス オブジェクトとサブシステム オブジェクト、およびそれらの関数とフィールドが存在します。これは、UI メニューや、シングル プレイヤー モードでフレンドとチャットしたり、マルチプレイヤー モードで他のユーザーとオンライン セッションに参加するなど、オンライン機能にアクセスしたい場合があるため、Online Services プラグインの機能には適した場所です。ゲーム インスタンスは、ゲーム インスタンスのサブシステムのマネージャーとしても機能します。特に、ゲーム インスタンスは、このチュートリアルで実装されている Online Services のゲーム インスタンス サブシステム (OnlineSampleOnlineSubsystem) のマネージャーとして動作します。
OnlineSampleGameInstance.h
OnlineSampleGameInstance.h
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "OnlineSampleGameInstance.generated.h"
// 前方宣言クラス
class AOnlineSamplePlayerController;
class UObject;
DECLARE_LOG_CATEGORY_EXTERN(LogGameInstance, Log, All);
/**
* ゲーム インスタンス サブシステムを管理するカスタム ゲーム インスタンス クラス
*/
UCLASS()
class ONLINESAMPLE_API UOnlineSampleGameInstance : public UGameInstance
{
GENERATED_BODY()
protected:
/** ゲーム起動時にゲーム インスタンスを初期化するために呼び出されます*/
virtual void Init() override;
/** ゲーム終了時にゲーム インスタンスをシャットダウンするために呼び出されます*/
virtual void Shutdown() override;
public:
/** ゲーム インスタンス オブジェクトを初期化するために呼び出されます*/
UOnlineSampleGameInstance(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
/** プライマリ プレイヤー コントローラーを取得するために呼び出されます*/
AOnlineSamplePlayerController* GetPrimaryPlayerController() const;
};
OnlineSampleGameInstance.cpp
OnlineSampleGameInstance.cpp
#include "OnlineSampleGameInstance.h"
#include "OnlineSample/Player/OnlineSamplePlayerController.h"
DEFINE_LOG_CATEGORY(LogGameInstance);
/// <summary>
/// Initialize Game Instance object
/// </summary>
void UOnlineSampleGameInstance::Init()
{
UE_LOG(LogGameInstance, Log, TEXT("OnlineSampleGameInstance initialized."));
Super::Init();
}
/// <summary>
/// Shutdown Game Instance object
/// </summary>
void UOnlineSampleGameInstance::Shutdown()
{
UE_LOG(LogGameInstance, Log, TEXT("OnlineSampleGameInstance shutdown."));
Super::Shutdown();
}
/// <summary>
/// Initialize Game Instance object
/// </summary>
/// <param name="ObjectInitializer"></param>
UOnlineSampleGameInstance::UOnlineSampleGameInstance(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
/// <summary>
/// Retrieve a reference to the primary player controller
/// </summary>
/// <returns>AOnlineSamplePlayerController pointer</returns>
AOnlineSamplePlayerController* UOnlineSampleGameInstance::GetPrimaryPlayerController() const
{
return Cast<AOnlineSamplePlayerController>(Super::GetPrimaryPlayerController(false));
}
ゲーム インスタンス サブシステム
Online Services プラグイン機能の実装の詳細は、すべてゲーム インスタンス サブシステム内にあります。前述のように、ゲーム インスタンスは初期化からシャットダウンまで、ゲームの存続期間全体にわたって保持されます。ゲーム インスタンス サブシステムは、ゲーム インスタンスの存続期間全体にわたってアクセスしたいコードを個別のシステムに整理するのに役立ちます。このプロジェクトでは、Online Services プラグインに関連するコードを「OnlineSampleOnlineSubsystem」というゲーム インスタンス サブシステムに整理します。このサンプルは、Title File インターフェース の機能を実装します。
実装の詳細
Online Services プラグインには、このページが OnlineSampleOnlineSubsystem で実装する一般的なパターンが含まれています。
クエリして取得
Online Services プラグインの一般的なパターンは、まず情報についてインターフェースにクエリを実行します。この情報はインターフェースにキャッシュされ、情報を取得したいときにキャッシュから情報を取得します。クエリは、クエリして取得操作の非同期の一部を構成します。OnlineSampleOnlineSubsystem サンプル コードには、このパターンの例がいくつかあります。特に以下を確認することができます。
RetrieveTitleFileとHandleEnumerateFilesへのその呼び出し
OnlineSampleOnlineSubsystem.h
OnlineSampleOnlineSubsystem.h
#pragma once
#include "CoreMinimal.h"
#include "Online/OnlineServices.h"
#include "Online/OnlineAsyncOpHandle.h"
#include "Online/TitleFile.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "OnlineSampleOnlineSubsystem.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogOnlineSampleOnlineSubsystem, Log, All);
/**
*
*/
UCLASS()
class ONLINESAMPLE_API UOnlineSampleOnlineSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
////////////////////////////////////////////////////////
/// OnlineSampleOnlineSubsystem を初期化する/初期化しない
/** サブシステムを作成するかどうかを決定するために呼び出されます*/
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
/** ゲーム インスタンス サブシステムを初期化するために呼び出されます*/
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
/** ゲーム インスタンス サブシステムをデイニシャライズするために呼び出されます*/
virtual void Deinitialize() override;
/** ゲーム インスタンス サブシステムにローカル オンライン ユーザーを登録するために呼び出されます*/
void RegisterLocalOnlineUser(FPlatformUserId PlatformUserId);
/** このプラットフォーム ユーザー ID のオンライン ユーザー情報を取得するために呼び出されます*/
TObjectPtr<UOnlineUserInfo> GetOnlineUserInfo(FPlatformUserId PlatformUserId);
/** バックエンド サービスからゲームのタイトル ファイルを読み取り、その内容を返すために呼び出されます*/
FString ReadTitleFile(FString Filename, FPlatformUserId PlatformUserId);
protected:
struct FOnlineServicesInfo
{
/** Online Services ポインタ - このポインタを通してインターフェースにアクセスします*/
UE::Online::IOnlineServicesPtr OnlineServices = nullptr;
/** インターフェース ポインタ*/
UE::Online::IAuthPtr AuthInterface = nullptr;
UE::Online::ITitleFilePtr TitleFileInterface = nullptr;
/** Online Services の実装*/
UE::Online::EOnlineServices OnlineServicesType = UE::Online::EOnlineServices::None;
/** タイトル ファイルの内容*/
UE::Online::FTitleFileContents TitleFileContent;
/** 構造体を初期設定にリセットします*/
void Reset()
{
OnlineServices.Reset();
AuthInterface.Reset();
TitleFileInterface.Reset();
OnlineServicesType = UE::Online::EOnlineServices::None;
}
};
////////////////////////////////////////////////////////
/// Online Services を初期化する
/** 関連する Online Services のポインタを含む内部構造体へのポインタ*/
FOnlineServicesInfo* OnlineServicesInfoInternal = nullptr;
/** Online Services とインターフェース ポインタを初期化するために呼び出されます*/
void InitializeOnlineServices();
////////////////////////////////////////////////////////
/// タイトル ファイル
/** Online Services からタイトル ファイルを取得するために呼び出されます*/
void RetrieveTitleFile(FString Filename, FPlatformUserId PlatformUserId);
////////////////////////////////////////////////////////
/// イベント
/** EnumerateFiles 非同期イベントを処理するために呼び出されます*/
void HandleEnumerateFiles(const UE::Online::TOnlineResult<UE::Online::FTitleFileEnumerateFiles>& EnumerateFilesResult, TObjectPtr<UOnlineUserInfo> OnlineUser, FString Filename);
/** ReadFile 非同期イベントを処理するために呼び出されます*/
void HandleReadFile(const UE::Online::TOnlineResult<UE::Online::FTitleFileReadFile>& ReadFileResult, FString Filename);
////////////////////////////////////////////////////////
/// オンライン ユーザー情報
/** このユーザーの UOnlineUserInfo オブジェクトを作成するために呼び出されます*/
TObjectPtr<UOnlineUserInfo> CreateOnlineUserInfo(int32 LocalUserIndex, FPlatformUserId PlatformUserId, UE::Online::FAccountId AccountId, UE::Online::EOnlineServices Services);
/** OnlineUserInfos マップにユーザーを登録し、CreateOnlineUserInfo で作成した後にユーザーを追加するために呼び出されます*/
TObjectPtr<UOnlineUserInfo> CreateAndRegisterUserInfo(int32 LocalUserIndex, FPlatformUserId PlatformUserId, UE::Online::FAccountId AccountId, UE::Online::EOnlineServices Services);
/** 各ローカル ユーザーに関する情報*/
TMap<FPlatformUserId, TObjectPtr<UOnlineUserInfo>> OnlineUserInfos;
/** UOnlineUserInfo クラスをフレンドにしてアクセスします*/
friend UOnlineUserInfo;
};
UCLASS()
class ONLINESAMPLE_API UOnlineUserInfo : public UObject
{
GENERATED_BODY()
public:
UOnlineUserInfo();
////////////////////////////////////////////////////////
/// オンライン ユーザー フィールド
int32 LocalUserIndex = -1;
FPlatformUserId PlatformUserId;
UE::Online::FAccountId AccountId;
UE::Online::EOnlineServices Services = UE::Online::EOnlineServices::None;
////////////////////////////////////////////////////////
/// オンライン ユーザー ログ/デバッグ関数
/** OnlineUserInfo を文字列として取得するために呼び出されます*/
const FString DebugInfoToString();
friend UOnlineSampleOnlineSubsystem;
};
OnlineSampleOnlineSubsystem.cpp
OnlineSampleOnlineSubsystem.cpp
#include "OnlineSampleOnlineSubsystem.h"
#include "Online/CoreOnline.h"
#include "Online/OnlineResult.h"
#include "Online/OnlineAsyncOpHandle.h"
#include "Online/OnlineError.h"
#include "Online/OnlineServices.h"
#include "Online/Auth.h"
#include "Online/TitleFile.h"
DEFINE_LOG_CATEGORY(LogOnlineSampleOnlineSubsystem);
/// <summary>
/// このサブシステムを作成するかどうか。簡単にするため、このサブシステムは
/// クライアントおよびスタンドアローン ゲームでのみ作成され、サーバーでは作成されません。この関数は、多くの場合、
/// サブシステムの作成をサーバーまたはクライアントに限定するために使用されます。
/// サブシステムを使用する前に、必ずヌルチェックを実行します!
/// </summary>
/// <param name="Outer"></param>
/// <returns>Boolean whether or not to create this subsystem</returns>
bool UOnlineSampleOnlineSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
#if UE_SERVER
return false;
#else
return Super::ShouldCreateSubsystem(Outer);
#endif
}
/// <summary>
/// Initialize はゲーム インスタンスが初期化された後に呼び出されます
/// </summary>
/// <param name="Collection">ゲーム インスタンスに初期化されるサブシステムのコレクション</param>
void UOnlineSampleOnlineSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
UE_LOG(LogTemp, Log, TEXT("OnlineSampleOnlineSubsystem initialized."));
Super::Initialize(Collection);
// Online Services を初期化する
InitializeOnlineServices();
}
/// <summary>
/// Deinitialize はゲーム インスタンスが未初期化/シャットダウンされる前に呼び出されます
/// </summary>
void UOnlineSampleOnlineSubsystem::Deinitialize()
{
UE_LOG(LogTemp, Log, TEXT("OnlineSampleOnlineSubsystem deinitialized."));
// イベント ハンドルのバインドを解除し、構造体情報をリセットします
OnlineServicesInfoInternal->Reset();
// 親クラスをデイニシャライズします
Super::Deinitialize();
}
/// <summary>
/// 非同期の EnumerateFiles 関数を処理します。失敗がある場合はログを記録します。
/// 成功すると、HandleReadFile が処理する非同期の ReadFile 関数が呼び出されます。
/// </summary>
/// <param name="EnumerateFilesResult">Result of Enumerate Files attempt</param>
/// <param name="OnlineUser">User that queried for title file</param>
/// <param name="Filename">Name of file user queried</param>
void UOnlineSampleOnlineSubsystem::HandleEnumerateFiles(const UE::Online::TOnlineResult<UE::Online::FTitleFileEnumerateFiles>& EnumerateFilesResult, TObjectPtr<UOnlineUserInfo> OnlineUser, FString Filename)
{
using namespace UE::Online;
if (EnumerateFilesResult.IsOk())
{
FTitleFileGetEnumeratedFiles::Params GetParams;
GetParams.LocalAccountId = OnlineUser->AccountId;
TOnlineResult<FTitleFileGetEnumeratedFiles> GetResult = OnlineServicesInfoInternal->TitleFileInterface->GetEnumeratedFiles(MoveTemp(GetParams));
if (GetResult.IsOk())
{
FTitleFileGetEnumeratedFiles::Result& CachedFiles = GetResult.GetOkValue();
int32 FileIndex = CachedFiles.Filenames.Find(Filename);
if (FileIndex == INDEX_NONE)
{
UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Title File \"%s\" not found!"), *Filename);
}
else
{
FTitleFileReadFile::Params ReadParams;
ReadParams.LocalAccountId = OnlineUser->AccountId;
ReadParams.Filename = Filename;
OnlineServicesInfoInternal->TitleFileInterface->ReadFile(MoveTemp(ReadParams)).OnComplete(this, &ThisClass::HandleReadFile, Filename);
}
}
else
{
UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Get Title File Error: %s"), *GetResult.GetErrorValue().GetLogString());
}
}
else
{
UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Enum Title File Error: %s"), *EnumerateFilesResult.GetErrorValue().GetLogString());
}
}
/// <summary>
/// 非同期の ReadFile 関数を処理します。失敗がある場合はログを記録します。
/// 成功すると、ユーザーの TitleFileContent が入力されます。
/// </summary>
/// <param name="ReadFileResult">Result of ReadFile attempt</param>
/// <param name="Filename">Name of file to read</param>
void UOnlineSampleOnlineSubsystem::HandleReadFile(const UE::Online::TOnlineResult<UE::Online::FTitleFileReadFile>& ReadFileResult, FString Filename)
{
using namespace UE::Online;
if (ReadFileResult.IsOk())
{
const FTitleFileReadFile::Result& ReadFileResultValue = ReadFileResult.GetOkValue();
OnlineServicesInfoInternal->TitleFileContent = *ReadFileResultValue.FileContents;
}
else
{
UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Read Title File Error: %s"), *ReadFileResult.GetErrorValue().GetLogString());
}
}
/// <summary>
/// Online Services を初期化します。
/// サービスへのポインタを取得する
/// インターフェースへのポインタを取得する
/// イベント ハンドラを追加する
/// ポインタの有効性をチェックする
/// </summary>
void UOnlineSampleOnlineSubsystem::InitializeOnlineServices()
{
OnlineServicesInfoInternal = new FOnlineServicesInfo();
// サービス ポインタを初期化します
OnlineServicesInfoInternal->OnlineServices = UE::Online::GetServices();
check(OnlineServicesInfoInternal->OnlineServices.IsValid());
// サービス タイプを検証します
OnlineServicesInfoInternal->OnlineServicesType = OnlineServicesInfoInternal->OnlineServices->GetServicesProvider();
if (OnlineServicesInfoInternal->OnlineServices.IsValid())
{
// インターフェース ポインタを初期化します
OnlineServicesInfoInternal->AuthInterface = OnlineServicesInfoInternal->OnlineServices->GetAuthInterface();
check(OnlineServicesInfoInternal->AuthInterface.IsValid());
OnlineServicesInfoInternal->TitleFileInterface = OnlineServicesInfoInternal->OnlineServices->GetTitleFileInterface();
check(OnlineServicesInfoInternal->TitleFileInterface.IsValid());
}
else {
UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Error: Failed to initialize services."));
}
}
/// <summary>
/// EnumerateFiles、GetEnumeratedFiles、および ReadFile。この実装では、ラムダ関数を使用して
/// OnComplete コールバックを処理しています。
/// </summary>
/// <param name="Filename">File to read</param>
/// <param name="PlatformUserId">User to retrieve file for</param>
void UOnlineSampleOnlineSubsystem::RetrieveTitleFile(FString Filename, FPlatformUserId PlatformUserId)
{
using namespace UE::Online;
FTitleFileEnumerateFiles::Params EnumParams;
FAccountId LocalAccountId;
TObjectPtr<UOnlineUserInfo> OnlineUser;
if (OnlineUserInfos.Contains(PlatformUserId))
{
OnlineUser = *OnlineUserInfos.Find(PlatformUserId);
LocalAccountId = OnlineUser->AccountId;
EnumParams.LocalAccountId = LocalAccountId;
if (OnlineServicesInfoInternal->TitleFileInterface.IsValid())
{
(OnlineServicesInfoInternal->TitleFileInterface)->EnumerateFiles(MoveTemp(EnumParams)).OnComplete(this, &ThisClass::HandleEnumerateFiles, OnlineUser, Filename);
}
else
{
UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Title File Interface pointer invalid."));
}
}
else
{
UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Could not find user with Platform User Id: %d"), PlatformUserId.GetInternalId());
}
}
/// <summary>
/// オンライン ユーザーをローカル レジストリ OnlineUserInfos に登録します
/// </summary>
/// <param name="PlatformUserId">Platform user id of user to register</param>
void UOnlineSampleOnlineSubsystem::RegisterLocalOnlineUser(FPlatformUserId PlatformUserId)
{
using namespace UE::Online;
FAuthGetLocalOnlineUserByPlatformUserId::Params GetUserParams;
GetUserParams.PlatformUserId = PlatformUserId;
if (OnlineServicesInfoInternal->AuthInterface.IsValid())
{
TOnlineResult<FAuthGetLocalOnlineUserByPlatformUserId> AuthGetResult = OnlineServicesInfoInternal->AuthInterface->GetLocalOnlineUserByPlatformUserId(MoveTemp(GetUserParams));
if (AuthGetResult.IsOk())
{
FAuthGetLocalOnlineUserByPlatformUserId::Result& LocalOnlineUser = AuthGetResult.GetOkValue();
TSharedRef<FAccountInfo> UserAccountInfo = LocalOnlineUser.AccountInfo;
FAccountInfo UserAccountInfoContent = *UserAccountInfo;
if (!OnlineUserInfos.Contains(UserAccountInfoContent.PlatformUserId))
{
UOnlineUserInfo* NewUser = CreateAndRegisterUserInfo(UserAccountInfoContent.AccountId.GetHandle(), PlatformUserId, UserAccountInfoContent.AccountId, UserAccountInfoContent.AccountId.GetOnlineServicesType());
UE_LOG(LogOnlineSampleOnlineSubsystem, Log, TEXT("Local User Registered: %s"), *(NewUser->DebugInfoToString()));
}
else
{
UE_LOG(LogOnlineSampleOnlineSubsystem, Log, TEXT("Local User with platform user id %d already registered."), PlatformUserId.GetInternalId());
}
}
else
{
FOnlineError ErrorResult = AuthGetResult.GetErrorValue();
UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Get Local Online User Error: %s"), *ErrorResult.GetLogString());
}
}
else
{
UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Auth Interface pointer invalid."));
}
}
/// <summary>
/// タイトル ファイルを取得し、その内容を読み取ります。
/// </summary>
/// <param name="Filename">File to read</param>
/// <param name="PlatformUserId">User to read file for</param>
/// <returns>FileString with contents of Filename</returns>
FString UOnlineSampleOnlineSubsystem::ReadTitleFile(FString Filename, FPlatformUserId PlatformUserId)
{
using namespace UE::Online;
RetrieveTitleFile(Filename, PlatformUserId);
FTitleFileContents FileContents = OnlineServicesInfoInternal->TitleFileContent;
FString FileString = FString(FileContents.Num(), UTF8_TO_TCHAR(FileContents.GetData()));
UE_LOG(LogOnlineSampleOnlineSubsystem, Log, TEXT("Reading Title File: %s"), *Filename);
return FileString;
}
/// <summary>
/// 提供された情報から UOnlineUserInfo オブジェクトを作成します。
/// </summary>
/// <param name="LocalUserIndex"></param>
/// <param name="PlatformUserId"></param>
/// <param name="AccountId"></param>
/// <param name="Services">Online services user is registered with</param>
/// <returns>Object pointer to the NewUser</returns>
TObjectPtr<UOnlineUserInfo> UOnlineSampleOnlineSubsystem::CreateOnlineUserInfo(int32 LocalUserIndex, FPlatformUserId PlatformUserId, UE::Online::FAccountId AccountId, UE::Online::EOnlineServices Services)
{
TObjectPtr<UOnlineUserInfo> NewUser = NewObject<UOnlineUserInfo>(this);
NewUser->LocalUserIndex = LocalUserIndex;
NewUser->PlatformUserId = PlatformUserId;
NewUser->AccountId = AccountId;
NewUser->Services = Services;
return NewUser;
}
/// <summary>
/// CreateOnlineUserInfo を呼び出して UOnlineUserInfo オブジェクトを作成し、
/// ローカル レジストリ OnlineUserInfos にユーザーを登録します
/// </summary>
/// <param name="LocalUserIndex"></param>
/// <param name="PlatformUserId"></param>
/// <param name="AccountId"></param>
/// <param name="Services">Online services user is registered with</param>
/// <returns>Object pointer to the NewUser</returns>
TObjectPtr<UOnlineUserInfo> UOnlineSampleOnlineSubsystem::CreateAndRegisterUserInfo(int32 LocalUserIndex, FPlatformUserId PlatformUserId, UE::Online::FAccountId AccountId, UE::Online::EOnlineServices Services)
{
TObjectPtr<UOnlineUserInfo> NewUser = CreateOnlineUserInfo(LocalUserIndex, PlatformUserId, AccountId, Services);
OnlineUserInfos.Add(PlatformUserId, NewUser);
return NewUser;
}
/// <summary>
/// 指定されたプラットフォーム ユーザー ID の UOnlineUserInfo を取得します。
/// </summary>
/// <param name="PlatformUserId">id of user to retrieve</param>
/// <returns>Object pointer to OnlineUser</returns>
TObjectPtr<UOnlineUserInfo> UOnlineSampleOnlineSubsystem::GetOnlineUserInfo(FPlatformUserId PlatformUserId)
{
TObjectPtr<UOnlineUserInfo> OnlineUser;
if (OnlineUserInfos.Contains(PlatformUserId))
{
OnlineUser = *OnlineUserInfos.Find(PlatformUserId);
}
else
{
UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Could not find user with Platform User Id: %d"), PlatformUserId.GetInternalId());
OnlineUser = nullptr;
}
return OnlineUser;
}
/// <summary>
/// UOnlineUserInfo オブジェクトのコンストラクタ
/// </summary>
UOnlineUserInfo::UOnlineUserInfo()
{
}
/// <summary>
/// UOnlineUserInfo のデバッグ文字列を返します
/// </summary>
/// <returns>String representation of UOnlineUserInfo</returns>
const FString UOnlineUserInfo::DebugInfoToString()
{
int32 UserIndex = this->LocalUserIndex;
int32 PlatformId = this->PlatformUserId;
TArray<FStringFormatArg> FormatArgs;
FormatArgs.Add(FStringFormatArg(UserIndex));
FormatArgs.Add(FStringFormatArg(PlatformId));
return FString::Format(TEXT("LocalUserNumber: {0}, PlatformUserId: {1}"), FormatArgs);
}
プレイヤー コントローラー
プレイヤー コントローラ クラスでは、プレイヤーを Online Services に登録し、バックエンド サービスからタイトル ファイルを読み取ります。
OnlineSamplePlayerController.h
OnlineSamplePlayerController.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "OnlineSamplePlayerController.generated.h"
/**
*
*/
UCLASS()
class ONLINESAMPLE_API AOnlineSamplePlayerController : public APlayerController
{
GENERATED_BODY()
public:
AOnlineSamplePlayerController();
protected:
/** プレイが開始すると呼び出されます*/
virtual void BeginPlay();
/** プレイが終了すると呼び出されます*/
virtual void EndPlay(EEndPlayReason::Type EndReason);
};
OnlineSamplePlayerController.cpp
OnlineSamplePlayerController.cpp
#include "OnlineSamplePlayerController.h"
#include "OnlineSample/GameInstance/OnlineSampleGameInstance.h"
#include "OnlineSample/GameInstance/OnlineSampleOnlineSubsystem.h"
AOnlineSamplePlayerController::AOnlineSamplePlayerController()
{
}
void AOnlineSamplePlayerController::BeginPlay()
{
Super::BeginPlay();
////////////////////////////////////////////////
/// ONLINE SERVICES
// このプレイヤーを Online Services に登録します
UOnlineSampleGameInstance* GameInstance = Cast<UOnlineSampleGameInstance>(GetWorld()->GetGameInstance());
UOnlineSampleOnlineSubsystem* OnlineSubsystem = GameInstance->GetSubsystem<UOnlineSampleOnlineSubsystem>();
ULocalPlayer* LocalPlayer = Super::GetLocalPlayer();
if (LocalPlayer)
{
FPlatformUserId LocalPlayerPlatformUserId = LocalPlayer->GetPlatformUserId();
if (OnlineSubsystem) // アクセス前にサブシステムのヌル チェックを実行します
{
UE_LOG(LogOnlineSampleOnlineSubsystem, Log, TEXT("Registering PlatformUserId: %d"), LocalPlayerPlatformUserId.GetInternalId());
OnlineSubsystem->RegisterLocalOnlineUser(LocalPlayerPlatformUserId);
// タイトル ファイルを読み取り、内容を画面に表示します
FString TitleFileContent = OnlineSubsystem->ReadTitleFile(FString("StatusFile"), LocalPlayerPlatformUserId);
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Black, TitleFileContent);
}
}
}
///////////////////////////////////////////////
/// 次のセクション...
}
void AOnlineSamplePlayerController::EndPlay(EEndPlayReason::Type EndReason)
{
Super::EndPlay(EndReason);
}
ゲーム モードを編集する
また、前のセクションで作成した Player Controller クラスを使用するために、<PROJECT_NAME>GameMode.cpp というタイトルの Game Mode ソース ファイルを編集する必要があります。
OnlineSampleGameMode.cpp
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OnlineSampleGameMode.h"
#include "Player/OnlineSamplePlayerController.h"
#include "OnlineSampleCharacter.h"
#include "UObject/ConstructorHelpers.h"
AOnlineSampleGameMode::AOnlineSampleGameMode()
{
// デフォルトのポーン クラスをブループリント化したキャラクターに設定します
static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter"));
if (PlayerPawnBPClass.Class != NULL)
{
DefaultPawnClass = PlayerPawnBPClass.Class;
}
// 新しいプレイヤー コントローラー クラスを割り当てます
PlayerControllerClass = AOnlineSamplePlayerController::StaticClass();
}
ビルド
これで、プロジェクトをビルドする準備が整いました。Unreal Editor 内から Visual Studio を開いた場合は、ライブ コーディング を使用してプロジェクトの C++ をコンパイルし、直ちに Unreal Editor でゲームを開始することができます。Unreal Editor が閉じている場合は、Visual Studio を使用してプロジェクトをビルドします。詳細については、「Unreal Engine でのゲーム プロジェクトのコンパイル」を参照してください。
テスト
現時点で、以下の作業が完了しています。
- Online Services プラグインを有効にし、設定しました。
- プロジェクトを適切に構成しました。
- Online Services プラグインの機能を実装するコードを追加しました。
- プロジェクトのコードをコンパイルしました。
プロジェクトのコンパイルが完了したので、プロジェクトをテストする準備が整いました。プロジェクトをテストするには、次の手順を実行します。
- Unreal Editor でプロジェクトを開きます。
- プレイ イン エディタを起動して、プロジェクトをテストします。
プロジェクトのテスト方法の詳細については、「Unreal Engine でのプレイとシミュレート」を参照してください。
プレイを開始する
プレイを開始すると、次のような表示になります。
コンソール コマンド
コンソール コマンドを使用して、現在の Online Services の実装をデバッグしたりテストしたりすることもできます。
Online Services プラグインでコンソール コマンドを使用する方法の詳細については、「Online Services コンソール コマンド」を参照してください。