メインコンテンツまでスキップ

Use the Online Subsystem to get recent players - Recent players - (Unreal Engine module)

Last updated on February 4, 2026

注釈:本資料は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 インターフェース宣言: FriendsInterfaceSessionInterface。これらのインターフェースを使用して、最近のプレイヤー機能を実装します。

    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 実装のヘルパーとして使用する関数を準備します。

  1. RecentPlayersSubsystem_Starter クラスのヘッダーファイルを開き、以下の関数を宣言します。この関数は、プレイヤーのフレンド招待ステータスを更新します。

    private:
    void UpdatePlayersInviteStatus(const APlayerController* PlayerController, const FOnGetGameSessionPlayerListComplete& OnComplete, TArray<UFriendData*>& PlayersData);
  2. 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);
    }
    }));
    }

最近のプレイヤーの取得を実装する

このセクションでは、最近のプレイヤーリストを取得する関数を実装します。

  1. RecentPlayersSubsystem_Starter クラスのヘッダーファイルを開き、以下のヘルパー変数を宣言します。この変数は、最近のプレイヤーリストをキャッシュするために使用されます。

    private:
    // ...
    UPROPERTY()
    TArray<UFriendData*> RecentPlayersData{};
  2. 引き続きヘッダーファイルで、以下の関数を宣言します。これらの関数は、最近のプレイヤーリストをクエリして取得するために使用されます。

    public:
    // ...
    void GetRecentPlayers(const APlayerController* PlayerController, const FOnGetRecentPlayersComplete& OnComplete = FOnGetRecentPlayersComplete());
    void QueryRecentPlayers(const APlayerController* PlayerController);
    FDelegateHandle BindRecentPlayerDelegate(FOnQueryRecentPlayersCompleteDelegate& Delegate);
    void UnBindRecentPlayerDelegate(FDelegateHandle& Delegate);
  3. 引き続きヘッダーファイルで、以下の関数を宣言します。この関数は、ゲームセッションが終了したときに呼び出されます。

    private:
    // ...
    void OnSessionDestroyed(FName SessionName, bool bWasSuccessful);
  4. 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);
    }
    }
  5. 次に、以下のコードを追加して 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(""));
    }
  6. 以下のコードを追加して BindRecentPlayerDelegate() 関数を定義します。この関数は、最近のプレイヤーリストのクエリが完了したときにトリガーされるように、渡されたデリゲートを追加します。この関数を UI と一緒に使用して、表示される最近のプレイヤーリストを更新します。

    FDelegateHandle URecentPlayersSubsystem_Starter::BindRecentPlayerDelegate(FOnQueryRecentPlayersCompleteDelegate& Delegate)
    {
    return FriendsInterface->AddOnQueryRecentPlayersCompleteDelegate_Handle(Delegate);
    }
  7. 次に、以下のコードを追加して UnBindRecentPlayerDelegate() 関数を定義します。この関数は、最近のプレイヤーリストのクエリデリゲートから渡されたデリゲートを削除します。この関数を UI と一緒に使用して、表示される最近のプレイヤーリストの更新を停止します。

    void URecentPlayersSubsystem_Starter::UnBindRecentPlayerDelegate(FDelegateHandle& Delegate)
    {
    FriendsInterface->ClearOnQueryRecentPlayersCompleteDelegate_Handle(Delegate);
    }
  8. 次に、以下のコードを追加して 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());
    }
    }
  9. 事前定義された Initialize() 関数を見つけます。これは、サブシステムが初期化されるときに呼び出される関数です。次に、以下のコードを追加して、ゲームセッションが終了するたびに OnSessionDestroyed() 関数が呼び出されるようにバインドします。

    void URecentPlayersSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    // ...
    SessionInterface->AddOnDestroySessionCompleteDelegate_Handle(FOnDestroySessionCompleteDelegate::CreateUObject(this, &ThisClass::OnSessionDestroyed));
    }
  10. 最後に、事前定義された Deinitialize() 関数を見つけます。これは、サブシステムが非初期化されるときに呼び出される関数です。次に、以下のコードを追加して、ゲームセッション終了時のデリゲートをクリアします。

    void URecentPlayersSubsystem_Starter::Deinitialize()
    {
    // ...
    SessionInterface->ClearOnDestroySessionCompleteDelegates(this);
    }

ゲームセッションプレイヤーリストの取得を実装する

このセクションでは、ゲームセッションプレイヤーリストを取得する機能を実装します。

  1. RecentPlayersSubsystem_Starter クラスのヘッダーファイルを開き、以下のヘルパー変数を宣言します。この変数は、ゲームセッションプレイヤーリストをキャッシュするために使用されます。

    private:
    // ...
    UPROPERTY()
    TArray<UFriendData*> GameSessionPlayersData{};
  2. 引き続きヘッダーファイルで、以下の関数を宣言します。これらの関数は、ゲームセッションプレイヤーリストを取得するために使用されます。

    public:
    // ...
    void GetGameSessionPlayerList(const APlayerController* PlayerController, const FOnGetGameSessionPlayerListComplete& OnComplete = FOnGetGameSessionPlayerListComplete());
    FString GetGameSessionPlayerStatus(UFriendData* Player);
  3. 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);
    }
  4. 次に、以下のコードを追加して 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 列挙型を参照してください。この列挙型は、ゲームセッション中にプレイヤーが持つ可能性のあるさまざまなステータスを示しています。

リソース