Use the Online Subsystem to get recent players - Recent players - (Unreal Engine module)
注釈:本資料はAI技術を用いて翻訳されています。
サブシステムの展開
このチュートリアルでは、AccelByte Gaming Services (AGS) Online Subsystem (OSS) を使用して、最近のプレイヤーリストとゲームセッションプレイヤーリストを取得する関数を実装する方法を説明します。Byte Wars プロジェクトには、完成した実装を持つ RecentPlayersSubsystem という名前のゲームインスタンスサブシステムがすでに作成されています。ただし、このチュートリアルでは、そのサブシステムのスターターバージョンを使用して、関数をゼロから実装します。
スターターパックの内容
このチュートリアルに従うために、RecentPlayersSubsystem_Starter という名前のスターターサブシステムクラスが用意されています。このクラスは リソース セクションで入手でき、以下のファイルで構成されています。
- ヘッダーファイル:
/Source/AccelByteWars/TutorialModules/Social/RecentPlayers/RecentPlayersSubsystem_Starter.h - CPP ファイル:
/Source/AccelByteWars/TutorialModules/Social/RecentPlayers/RecentPlayersSubsystem_Starter.cpp
RecentPlayersSubsystem_Starter クラスには、いくつかのヘルパーが含まれています。
-
AGS OSS インターフェース宣言:
FriendsInterfaceとSessionInterface。これらのインターフェースを使用して、最近のプレイヤー機能を実装します。protected:
FOnlineFriendsAccelBytePtr FriendsInterface;
IOnlineSessionPtr SessionInterface; -
PlayerControllerからUniqueNetIdを取得するヘルパー関数。上記の AGS OSS インターフェースにこの関数が必要になります。FUniqueNetIdPtr URecentPlayersSubsystem_Starter::GetUniqueNetIdFromPlayerController(const APlayerController* PlayerController)
{
if (!PlayerController)
{
return nullptr;
}
const ULocalPlayer* LocalPlayer = PlayerController->GetLocalPlayer();
if (!LocalPlayer)
{
return nullptr;
}
return LocalPlayer->GetPreferredUniqueNetId().GetUniqueNetId();
}
ヘルパーデリゲートを含むモデルファイルもあります。このファイルは /Source/AccelByteWars/TutorialModules/Social/RecentPlayers/RecentPlayersModels.h にあります。これらのデリゲートを使用してコールバックを処理します。
DECLARE_DELEGATE_TwoParams(FOnGetRecentPlayersComplete, bool /*bWasSuccessful*/, TArray<UFriendData*> /*RecentPlayersData*/)
DECLARE_DELEGATE_TwoParams(FOnGetGameSessionPlayerListComplete, bool /*bWasSuccessful*/, TArray<UFriendData*> /*GameSessionPlayersData*/)
ヘルパーの準備
このセクションでは、RecentPlayersSubsystem_Starter 実装のヘルパーとして使用する関数を準備します。
-
RecentPlayersSubsystem_Starterクラスのヘッダーファイルを開き、以下の関数を宣言します。この関数は、プレイヤーのフレンド招待ステータスを更新します。private:
void UpdatePlayersInviteStatus(const APlayerController* PlayerController, const FOnGetGameSessionPlayerListComplete& OnComplete, TArray<UFriendData*>& PlayersData); -
RecentPlayersSubsystem_Starterクラスの CPP ファイルを開き、以下のコードを追加してUpdatePlayersInviteStatus()関数を定義します。この関数は、プレイヤーのフレンドリストとブロックされたプレイヤーリストを比較することで、プレイヤーのフレンド招待ステータスを更新します。この関数が必要な理由は、ブロックされたプレイヤーリストが AGS OSS の別のインターフェースによって処理されるためです。したがって、プレイヤーがブロックされていないプレイヤーにのみフレンドリクエストを送信できるように、ステータスを同期する必要があります。void URecentPlayersSubsystem_Starter::UpdatePlayersInviteStatus(const APlayerController* PlayerController, const FOnGetGameSessionPlayerListComplete& OnComplete, TArray<UFriendData*>& PlayersData)
{
UAccelByteWarsGameInstance* GameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance());
ensure(GameInstance);
UFriendsSubsystem* FriendsSubsystem = GameInstance->GetSubsystem<UFriendsSubsystem>();
ensure(FriendsSubsystem);
FriendsSubsystem->GetFriendsInviteStatus(PlayerController, PlayersData, FOnGetPlayersInviteStatusComplete::CreateWeakLambda(this, [this, PlayerController, OnComplete, &PlayersData, GameInstance](bool bWasSuccessful, const FString& ErrorMessage)
{
if(bWasSuccessful)
{
//check blocked player list, as blocked player returned from Friends subsystem has friend status as EFriendStatus::Unknown
UManagingFriendsSubsystem* ManagingFriendsSubsystem = GameInstance->GetSubsystem<UManagingFriendsSubsystem>();
ensure(ManagingFriendsSubsystem);
ManagingFriendsSubsystem->GetBlockedPlayerList(
PlayerController,
false,
FOnGetBlockedPlayerListComplete::CreateWeakLambda(this, [this, OnComplete, &PlayersData](bool bWasSuccessful, TArray<UFriendData*> BlockedPlayers, const FString& ErrorMessage)
{
if(bWasSuccessful)
{
for(UFriendData* PlayerData: PlayersData)
{
if(BlockedPlayers.ContainsByPredicate([PlayerData](const UFriendData* Data)
{
return Data->UserId == PlayerData->UserId;
}))
{
PlayerData->Status = EFriendStatus::Blocked;
PlayerData->bCannotBeInvited = true;
PlayerData->ReasonCannotBeInvited = NSLOCTEXT("AccelByteWars", "Blocked", "Blocked").ToString();
}
}
}
OnComplete.ExecuteIfBound(bWasSuccessful, PlayersData);
}));
}
else
{
OnComplete.ExecuteIfBound(false, GameSessionPlayersData);
UE_LOG_RECENTPLAYERS(Warning, TEXT("Failed get invite status. Error message: %s"), *ErrorMessage);
}
}));
}
最近のプレイヤーの取得を実装する
このセクションでは、最近のプレイヤーリストを取得する関数を実装します。
-
RecentPlayersSubsystem_Starterクラスのヘッダーファイルを開き、以下のヘルパー変数を宣言します。この変数は、最近のプレイヤーリストをキャッシュするために使用されます。private:
// ...
UPROPERTY()
TArray<UFriendData*> RecentPlayersData{}; -
引き続きヘッダーファイルで、以下の関数を宣言します。これらの関数は、最近のプレイヤーリストをクエリして取得するために使用されます。
public:
// ...
void GetRecentPlayers(const APlayerController* PlayerController, const FOnGetRecentPlayersComplete& OnComplete = FOnGetRecentPlayersComplete());
void QueryRecentPlayers(const APlayerController* PlayerController);
FDelegateHandle BindRecentPlayerDelegate(FOnQueryRecentPlayersCompleteDelegate& Delegate);
void UnBindRecentPlayerDelegate(FDelegateHandle& Delegate); -
引き続きヘッダーファイルで、以下の関数を宣言します。この関数は、ゲームセッションが終了したときに呼び出されます。
private:
// ...
void OnSessionDestroyed(FName SessionName, bool bWasSuccessful); -
RecentPlayersSubsystem_Starterクラスの CPP ファイルを開き、上記の関数を定義します。以下のコードを追加してGetRecentPlayers()関数を定義します。この関数は、AGS OSS キャッシュから最近のプレイヤーリストを収集し、フレンド招待ステータスを更新します。void URecentPlayersSubsystem_Starter::GetRecentPlayers(const APlayerController* PlayerController,
const FOnGetRecentPlayersComplete& OnComplete)
{
const FUniqueNetIdPtr LocalPlayerId = GetUniqueNetIdFromPlayerController(PlayerController);
if (!ensure(LocalPlayerId.IsValid()))
{
UE_LOG_RECENTPLAYERS(Warning, TEXT("Cannot get recent player. LocalPlayer NetId is not valid."));
return;
}
TArray<TSharedRef<FOnlineRecentPlayer>> RecentPlayers;
bool bSuccess = FriendsInterface->GetRecentPlayers(LocalPlayerId.ToSharedRef().Get(), TEXT(""), RecentPlayers);
if(bSuccess)
{
UE_LOG_RECENTPLAYERS(Log, TEXT("Success to get recent player list."));
RecentPlayersData.Empty();
for(const TSharedRef<FOnlineRecentPlayer>& Player: RecentPlayers)
{
RecentPlayersData.Add(UFriendData::ConvertToFriendData(Player, this));
}
UpdatePlayersInviteStatus(PlayerController, OnComplete, RecentPlayersData);
}
} -
次に、以下のコードを追加して
QueryRecentPlayers()関数を定義します。この関数は、最近のプレイヤーリストをクエリするリクエストをバックエンドに送信します。void URecentPlayersSubsystem_Starter::QueryRecentPlayers(const APlayerController* PlayerController)
{
const FUniqueNetIdPtr LocalPlayerId = GetUniqueNetIdFromPlayerController(PlayerController);
if (!ensure(LocalPlayerId.IsValid()))
{
UE_LOG_RECENTPLAYERS(Warning, TEXT("Cannot query recent player. LocalPlayer NetId is not valid."));
return;
}
FriendsInterface->QueryRecentPlayers(LocalPlayerId.ToSharedRef().Get(), TEXT(""));
} -
以下のコードを追加して
BindRecentPlayerDelegate()関数を定義します。この関数は、最近のプレイヤーリストのクエリが完了したときにトリガーされるように、渡されたデリゲートを追加します。この関数を UI と一緒に使用して、表示される最近のプレイヤーリストを更新します。FDelegateHandle URecentPlayersSubsystem_Starter::BindRecentPlayerDelegate(FOnQueryRecentPlayersCompleteDelegate& Delegate)
{
return FriendsInterface->AddOnQueryRecentPlayersCompleteDelegate_Handle(Delegate);
} -
次に、以下のコードを追加して
UnBindRecentPlayerDelegate()関数を定義します。この関数は、最近のプレイヤーリストのクエリデリゲートから渡されたデリゲートを削除します。この関数を UI と一緒に使用して、表示される最近のプレイヤーリストの更新を停止します。void URecentPlayersSubsystem_Starter::UnBindRecentPlayerDelegate(FDelegateHandle& Delegate)
{
FriendsInterface->ClearOnQueryRecentPlayersCompleteDelegate_Handle(Delegate);
} -
次に、以下のコードを追加して
OnSessionDestroyed()関数を定義します。この関数は、ゲームセッションが終了するたびに最近のプレイヤーリストをクエリします。void URecentPlayersSubsystem_Starter::OnSessionDestroyed(FName SessionName, bool bWasSuccessful)
{
// refresh recent player if a game session is destroyed
if(bWasSuccessful && SessionName.IsEqual(NAME_GameSession))
{
const UAccelByteWarsGameInstance* GameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance());
ensure(GameInstance);
QueryRecentPlayers(GameInstance->GetFirstLocalPlayerController());
}
} -
事前定義された
Initialize()関数を見つけます。これは、サブシステムが初期化されるときに呼び出される関数です。次に、以下のコードを追加して、ゲームセッションが終了するたびにOnSessionDestroyed()関数が呼び出されるようにバインドします。void URecentPlayersSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
SessionInterface->AddOnDestroySessionCompleteDelegate_Handle(FOnDestroySessionCompleteDelegate::CreateUObject(this, &ThisClass::OnSessionDestroyed));
} -
最後に、事前定義された
Deinitialize()関数を見つけます。これは、サブシステムが非初期化されるときに呼び出される関数です。次に、以下のコードを追加して、ゲームセッション終了時のデリゲートをクリアします。void URecentPlayersSubsystem_Starter::Deinitialize()
{
// ...
SessionInterface->ClearOnDestroySessionCompleteDelegates(this);
}
ゲームセッションプレイヤーリストの取得を実装する
このセクションでは、ゲームセッションプレイヤーリストを取得する機能を実装します。
-
RecentPlayersSubsystem_Starterクラスのヘッダーファイルを開き、以下のヘルパー変数を宣言します。この変数は、ゲームセッションプレイヤーリストをキャッシュするために使用されます。private:
// ...
UPROPERTY()
TArray<UFriendData*> GameSessionPlayersData{}; -
引き続きヘッダーファイルで、以下の関数を宣言します。これらの関数は、ゲームセッションプレイヤーリストを取得するために使用されます。
public:
// ...
void GetGameSessionPlayerList(const APlayerController* PlayerController, const FOnGetGameSessionPlayerListComplete& OnComplete = FOnGetGameSessionPlayerListComplete());
FString GetGameSessionPlayerStatus(UFriendData* Player); -
RecentPlayersSubsystem_Starterクラスの CPP ファイルを開き、上記の関数を定義します。以下のコードを追加してGetGameSessionPlayerList()関数を定義します。この関数は、ゲームセッションプレイヤーリストを収集し、渡されたデリゲートパラメータを介して返します。void URecentPlayersSubsystem_Starter::GetGameSessionPlayerList(const APlayerController* PlayerController, const FOnGetGameSessionPlayerListComplete& OnComplete)
{
const FNamedOnlineSession* GameSession = SessionInterface->GetNamedSession(NAME_GameSession);
if(!GameSession)
{
return;
}
const TSharedPtr<FOnlineSessionInfo> SessionInfo = GameSession->SessionInfo;
if (!SessionInfo.IsValid())
{
return;
}
const TSharedPtr<FOnlineSessionInfoAccelByteV2> AbSessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(SessionInfo);
if (!AbSessionInfo.IsValid())
{
return;
}
const TSharedPtr<FAccelByteModelsV2BaseSession> AbBaseSessionInfo = AbSessionInfo->GetBackendSessionData();
if (!AbBaseSessionInfo.IsValid())
{
return;
}
const FUniqueNetIdAccelByteUserPtr TargetUserABId = StaticCastSharedPtr<const FUniqueNetIdAccelByteUser>(GetUniqueNetIdFromPlayerController(PlayerController));
if(!TargetUserABId.IsValid())
{
UE_LOG_RECENTPLAYERS(Warning, TEXT("Local userid invalid"));
return;
}
GameSessionPlayersData.Empty();
TArray<FAccelByteModelsV2SessionUser> AbMembers = AbBaseSessionInfo->Members;
TArray<FUniqueNetIdRef> UniqueNetIds;
for (const FAccelByteModelsV2SessionUser& AbMember : AbMembers)
{
// skip local player
if(TargetUserABId->GetAccelByteId().Equals(AbMember.ID))
{
continue;
}
FAccelByteUniqueIdComposite CompositeId;
CompositeId.Id = AbMember.ID;
FUniqueNetIdAccelByteUserRef AccelByteUser = FUniqueNetIdAccelByteUser::Create(CompositeId);
UniqueNetIds.Add(AccelByteUser);
const FOnlineSubsystemAccelByte* Subsystem = static_cast<FOnlineSubsystemAccelByte*>(Online::GetSubsystem(GetWorld()));
if (!ensure(Subsystem))
{
UE_LOG_RECENTPLAYERS(Warning, TEXT("The online subsystem is invalid."));
return;
}
const TSharedPtr<const FAccelByteUserInfo> User = Subsystem->GetUserCache()->GetUser(AccelByteUser.Get());
if(User.IsValid())
{
UFriendData* PlayerData = UFriendData::ConvertToFriendData(User.ToSharedRef());
GameSessionPlayersData.Add(PlayerData);
}
else
{
UE_LOG_RECENTPLAYERS(Warning, TEXT("User is invalid"));
}
}
UE_LOG_RECENTPLAYERS(Log, TEXT("Success to get game session player list."));
UpdatePlayersInviteStatus(PlayerController, OnComplete, GameSessionPlayersData);
} -
次に、以下のコードを追加して
GetGameSessionPlayerStatus()関数を定義します。この関数は、ゲームセッション内のプレイヤーステータス(例: ゲームセッションに参加または退出)を返します。FString URecentPlayersSubsystem_Starter::GetGameSessionPlayerStatus(UFriendData* Player)
{
FString StatusAsString = TEXT("");
const FNamedOnlineSession* GameSession = SessionInterface->GetNamedSession(NAME_GameSession);
const TSharedPtr<FOnlineSessionInfo> SessionInfo = GameSession->SessionInfo;
if (!SessionInfo.IsValid())
{
return StatusAsString;
}
const TSharedPtr<FOnlineSessionInfoAccelByteV2> AbSessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(SessionInfo);
if (!AbSessionInfo.IsValid())
{
return StatusAsString;
}
const TSharedPtr<FAccelByteModelsV2BaseSession> AbBaseSessionInfo = AbSessionInfo->GetBackendSessionData();
if (!AbBaseSessionInfo.IsValid())
{
return StatusAsString;
}
TArray<FAccelByteModelsV2SessionUser> AbMembers = AbBaseSessionInfo->Members;
const FUniqueNetIdAccelByteUserPtr TargetUserABId = StaticCastSharedPtr<const FUniqueNetIdAccelByteUser>(Player->UserId);
FAccelByteModelsV2SessionUser* OnlineUser = AbMembers.FindByPredicate([TargetUserABId](FAccelByteModelsV2SessionUser User)
{
return User.ID.Equals(TargetUserABId->GetAccelByteId());
});
if(OnlineUser != nullptr)
{
UEnum::GetValueAsString<EAccelByteV2SessionMemberStatus>(OnlineUser->StatusV2, StatusAsString);
StatusAsString = StatusAsString.RightChop(StatusAsString.Find(TEXT("::")) + 2);
}
return StatusAsString;
}備考すべてのプレイヤーゲームセッションステータスタイプの詳細なリストについては、
EAccelByteV2SessionMemberStatus列挙型を参照してください。この列挙型は、ゲームセッション中にプレイヤーが持つ可能性のあるさまざまなステータスを示しています。
リソース
- このチュートリアルセクションで使用されるファイルは、Unreal Byte Wars GitHub リポジトリで入手できます。
- AccelByteWars/Source/AccelByteWars/TutorialModules/Social/RecentPlayers/RecentPlayersModels.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Social/RecentPlayers/RecentPlayersSubsystem_Starter.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Social/RecentPlayers/RecentPlayersSubsystem_Starter.cpp