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

オンラインサブシステムの実装 - フレンドとプレイする - (Unreal Engine モジュール)

Last updated on February 4, 2026

注釈:本資料はAI技術を用いて翻訳されています。

ゲームセッション招待フロー

フレンド招待フローの動作を以下に示します。サブグラフ Match Session モジュールで処理 に記載されている機能は、Joinable sessions(専用サーバーまたはピアツーピア)モジュールを完了した際に既に実装されています。Byte Wars 固有ではないコードは機能するために必要ありません。これら2つのサブグラフについて追加の手順を実行する必要はありません。

サブシステムの展開

すぐに実装を開始できるように、サブシステムスタータークラスが提供されています。PlayingWithFriendsSubsystem_Starter クラスファイルは以下の場所にあります:

  • ヘッダーファイル: /Source/AccelByteWars/TutorialModules/Play/PlayingWithFriends/PlayingWithFriendsSubsystem_Starter.h
  • CPP ファイル: /Source/AccelByteWars/TutorialModules/Play/PlayingWithFriends/PlayingWithFriendsSubsystem_Starter.cpp

クラスで提供されている内容を確認してください。

  • PlayingWithFriendsSubsystem_Starter ヘッダーファイルには、Helper という領域に囲まれたいくつかの関数があります。

    public:
    // ...
    bool IsInMatchSessionGameSession() const;
    private:
    // ...
    bool IsMatchSessionGameSessionReceivedServer() const;

    FUniqueNetIdRef GetSessionOwnerUniqueNetId(const FName SessionName) const;
    UPromptSubsystem* GetPromptSubsystem() const;

    FOnlineSessionV2AccelBytePtr GetSessionInterface() const;
    FOnlineIdentityAccelBytePtr GetIdentityInterface() const;

    void JoinGameSessionConfirmation(const int32 LocalUserNum, const FOnlineSessionInviteAccelByte& Invite);
    void OnQueryUserInfoOnGameSessionParticipantChange(
    const FOnlineError& Error,
    const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo,
    FName SessionName,
    const bool bJoined);
    • IsInMatchSessionGameSession は、現在のゲームセッションが Match Session であることを確認する関数で、Joinable sessions(専用サーバーまたはピアツーピア)モジュールで扱われています。
    • GetSessionOwnerUniqueNetId は、セッション名からセッションのオーナーのユニークネット ID を取得するシンプルな関数です。
    • GetPromptSubsystemByte Wars 固有の関数で、ポップアップ通知、ポップアップローディング画面、ポップアップ確認画面、プッシュ通知などのグローバルプロンプト UI を処理するサブシステムを取得します。
    • OnQueryUserInfoOnGameSessionParticipantChangeByte Wars 固有の関数で、OnGameSessionParticipantChange 通知に含まれるユニークネット ID からユーザー名を取得するために必要です。
  • 引き続きヘッダーファイルで、以下の変数宣言が表示されます:

    private:
    // ...
    UPROPERTY()
    UAccelByteWarsOnlineSessionBase* OnlineSession;

    FUniqueNetIdPtr LeaderId;
    bool bLeaderChanged = false;
    • OnlineSession は、Joinable sessions(専用サーバーまたはピアツーピア)モジュールで設定したオンラインセッションクラスへのポインタです。
    • LeaderId は、名前が示すように、ゲームセッションのリーダーの ID をキャッシュしたものです。OnGameSessionParticipantChange にはリーダーが変更されたかどうかを判断する方法がないため、現在のリーダー ID を保存し、オンラインサブシステム(OSS)に保存されているセッション情報と手動で比較する必要があります。
    • bLeaderChanged は、リーダーが変更されたかどうかを示すフラグです。

セッション招待の送信

  1. PlayingWithFriendsSubsystem_Starter ヘッダーファイルを開き、以下の宣言を追加します:

    public:
    void SendGameSessionInvite(const APlayerController* Owner, const FUniqueNetIdPtr Invitee) const;
  2. PlayingWithFriendsSubsystem_Starter CPP ファイルを開き、以下の実装を追加します。ここでは、単純に SendSessionInvite をすぐに呼び出します。

    void UPlayingWithFriendsSubsystem_Starter::SendGameSessionInvite(const APlayerController* Owner, const FUniqueNetIdPtr Invitee) const
    {
    UE_LOG_PLAYINGWITHFRIENDS(Verbose, TEXT("Called"));

    OnlineSession->SendSessionInvite(
    OnlineSession->GetLocalUserNumFromPlayerController(Owner),
    OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession),
    Invitee);
    }
  3. ヘッダーファイルに戻り、レスポンスを受信したときにユーザーインターフェース(UI)が何かをトリガーする方法として、パブリックデリゲートを追加します。

    public:
    // ...
    FOnSendSessionInviteComplete OnSendGameSessionInviteCompleteDelegates;
  4. 引き続きヘッダーファイルで、SendSessionInvite へのレスポンスを受信したときに呼び出されるこの関数宣言を追加します。

    private:
    void OnSendGameSessionInviteComplete(
    const FUniqueNetId& LocalSenderId,
    FName SessionName,
    bool bSucceeded,
    const FUniqueNetId& InviteeId) const;
  5. CPP ファイルを開き、以下の実装を追加します。これにより、現在のセッションが最初にゲームセッションであることを確認し、リクエストが正常に送信されたことを示すプッシュ通知を表示し、追加したデリゲートをトリガーします。

    void UPlayingWithFriendsSubsystem_Starter::OnSendGameSessionInviteComplete(
    const FUniqueNetId& LocalSenderId,
    FName SessionName,
    bool bSucceeded,
    const FUniqueNetId& InviteeId) const
    {
    // Abort if not a game session.
    if (!OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    // Only handle the event if the game session is in the game server.
    if (!IsMatchSessionGameSessionReceivedServer())
    {
    return;
    }

    UE_LOG_PLAYINGWITHFRIENDS(Verbose, TEXT("Succeedded: %s"), *FString(bSucceeded ? TEXT("TRUE") : TEXT("FALSE")));

    if (!SessionName.IsEqual(OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession)))
    {
    return;
    }

    // show sent push notification
    if (UPromptSubsystem* PromptSubsystem = GetPromptSubsystem())
    {
    PromptSubsystem->PushNotification(TEXT_INVITE_SENT);
    }

    OnSendGameSessionInviteCompleteDelegates.Broadcast(LocalSenderId, SessionName, bSucceeded, InviteeId);
    }
  6. レスポンス関数をオンラインセッションのデリゲートにバインドします。CPP ファイルで Initialize に移動し、以下のコードを追加します:

    void UPlayingWithFriendsSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    // ...
    OnlineSession->GetOnSendSessionInviteCompleteDelegates()->AddUObject(
    this, &ThisClass::OnSendGameSessionInviteComplete);
    // ...
    }
  7. 不要になったらバインドを解除します。引き続き CPP ファイルで Deinitialize に移動し、以下のコードを追加します:

    void UPlayingWithFriendsSubsystem_Starter::Deinitialize()
    {
    // ...
    OnlineSession->GetOnSendSessionInviteCompleteDelegates()->RemoveAll(this);
    // ...
    }

セッション招待の承諾

  1. PlayingWithFriendsSubsystem_Starter ヘッダーファイルを開き、以下の関数宣言を追加します:

    private:
    // ...
    void JoinGameSession(const int32 LocalUserNum, const FOnlineSessionSearchResult& Session) const;
  2. CPP ファイルに移動し、以下の実装を追加します。ここでは、ゲームセッションに参加することを示す事前定義されたパラメータを使用して、オンラインセッションから JoinSession() 関数を呼び出します。

    void UPlayingWithFriendsSubsystem_Starter::JoinGameSession(const int32 LocalUserNum, const FOnlineSessionSearchResult& Session) const
    {
    UE_LOG_PLAYINGWITHFRIENDS(Verbose, TEXT("Called"));

    OnlineSession->JoinSession(
    LocalUserNum,
    OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession),
    Session);
    }
  3. ヘッダーファイルに戻り、このコールバック関数宣言を追加します。

    private:
    // ...
    void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type CompletionType) const;
  4. CPP ファイルを開き、以下の実装を追加します。ここでは、参加に失敗した場合にエラーポップアップ画面を表示します。

    void UPlayingWithFriendsSubsystem_Starter::OnJoinSessionComplete(
    FName SessionName,
    EOnJoinSessionCompleteResult::Type CompletionType) const
    {
    // Abort if not a game session.
    if (!OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    // Only handle the event if the game session is in the game server.
    if (!IsMatchSessionGameSessionReceivedServer())
    {
    return;
    }

    const bool bSucceeded = CompletionType == EOnJoinSessionCompleteResult::Success;
    FText ErrorMessage;

    UE_LOG_PLAYINGWITHFRIENDS(Verbose, TEXT("Succeedded: %s"), *FString(bSucceeded ? TEXT("TRUE") : TEXT("FALSE")));

    switch (CompletionType)
    {
    case EOnJoinSessionCompleteResult::Success:
    ErrorMessage = FText();
    break;
    case EOnJoinSessionCompleteResult::SessionIsFull:
    ErrorMessage = TEXT_FAILED_SESSION_FULL_PLAYING_WITH_FRIENDS;
    break;
    case EOnJoinSessionCompleteResult::SessionDoesNotExist:
    ErrorMessage = TEXT_FAILED_SESSION_NULL_PLAYING_WITH_FRIENDS;
    break;
    case EOnJoinSessionCompleteResult::CouldNotRetrieveAddress:
    ErrorMessage = TEXT_FAILED_TO_JOIN_SESSION_PLAYING_WITH_FRIENDS;
    break;
    case EOnJoinSessionCompleteResult::AlreadyInSession:
    ErrorMessage = TEXT_FAILED_ALREADY_IN_SESSION_PLAYING_WITH_FRIENDS;
    break;
    case EOnJoinSessionCompleteResult::UnknownError:
    ErrorMessage = TEXT_FAILED_TO_JOIN_SESSION_PLAYING_WITH_FRIENDS;
    break;
    default:
    ErrorMessage = FText();
    }

    if (UPromptSubsystem* PromptSubsystem = GetPromptSubsystem(); !bSucceeded && PromptSubsystem)
    {
    PromptSubsystem->PushNotification(ErrorMessage);
    }
    }
  5. レスポンス関数をオンラインセッションのデリゲートにバインドします。CPP ファイルで Initialize に移動し、以下のコードを追加します:

    void UPlayingWithFriendsSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    // ...
    OnlineSession->GetOnJoinSessionCompleteDelegates()->AddUObject(
    this, &ThisClass::OnJoinSessionComplete);
    // ...
    }
  6. 不要になったらバインドを解除します。引き続き CPP ファイルで Deinitialize に移動し、以下のコードを追加します:

    void UPlayingWithFriendsSubsystem_Starter::Deinitialize()
    {
    // ...
    OnlineSession->GetOnJoinSessionCompleteDelegates()->RemoveAll(this);
    // ...
    }

セッション招待の拒否

  1. PlayingWithFriendsSubsystem_Starter ヘッダーファイルを開き、以下の関数宣言を追加します:

    public:
    // ...
    void RejectGameSessionInvite(const APlayerController* Owner, const FOnlineSessionInviteAccelByte& Invite) const;
  2. CPP ファイルを開き、以下の実装を追加します:

    void UPlayingWithFriendsSubsystem_Starter::RejectGameSessionInvite(
    const APlayerController* Owner,
    const FOnlineSessionInviteAccelByte& Invite) const
    {
    UE_LOG_PLAYINGWITHFRIENDS(Verbose, TEXT("Called"));

    OnlineSession->RejectSessionInvite(
    OnlineSession->GetLocalUserNumFromPlayerController(Owner),
    Invite);
    }
  3. ヘッダーファイルに戻り、レスポンスを受信したときに UI が何かをトリガーする方法として、パブリックデリゲートを追加します。

    public:
    // ...
    FOnRejectSessionInviteCompleteMulticast OnRejectGameSessionInviteCompleteDelegates;
  4. 引き続きヘッダーファイルで、このコールバック関数宣言を追加します:

    private:
    // ...
    void OnRejectGameSessionInviteComplete(bool bSucceeded) const;
  5. CPP ファイルを開き、以下の実装を追加します:

    void UPlayingWithFriendsSubsystem_Starter::OnRejectGameSessionInviteComplete(bool bSucceeded) const
    {
    UE_LOG_PLAYINGWITHFRIENDS(Verbose, TEXT("Succeedded: %s"), *FString(bSucceeded ? TEXT("TRUE") : TEXT("FALSE")));

    OnRejectGameSessionInviteCompleteDelegates.Broadcast(bSucceeded);
    }
  6. レスポンス関数をオンラインセッションのデリゲートにバインドします。CPP ファイルで Initialize に移動し、以下のコードを追加します:

    void UPlayingWithFriendsSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    // ...
    OnlineSession->GetOnRejectSessionInviteCompleteDelegate()->AddUObject(
    this, &ThisClass::OnRejectGameSessionInviteComplete);
    // ...
    }
  7. 不要になったらバインドを解除します。引き続き CPP ファイルで Deinitialize に移動し、以下のコードを追加します:

    void UPlayingWithFriendsSubsystem_Starter::Deinitialize()
    {
    // ...
    OnlineSession->GetOnRejectSessionInviteCompleteDelegate()->RemoveAll(this);
    // ...
    }

セッション招待の受信

Byte Wars は招待を受信したときに招待者のユーザー名を表示する必要がありますが、招待通知には招待者のユニークネット ID のみが含まれているため、QueryUserInfo が必要です。このセクションでは、Byte Wars が必要とする方法でこの実装を説明します。

  1. PlayingWithFriendsSubsystem_Starter ヘッダーファイルを開き、以下の関数宣言を追加します。この関数は、プレイヤーが招待されたときに呼び出されるエントリ関数として機能します。

    private:
    // ...
    void OnGameSessionInviteReceived(
    const FUniqueNetId& UserId,
    const FUniqueNetId& FromId,
    const FOnlineSessionInviteAccelByte& Invite);
  2. CPP ファイルを開き、以下の実装を追加します。ここでは、実装のほとんどが QueryUserInfo に対応するためのものです。注目すべきは ShowInviteReceivedPopup の呼び出しで、この時点ではエラーが表示されます。これは次のステップで宣言と実装を行います。

    void UPlayingWithFriendsSubsystem_Starter::OnGameSessionInviteReceived(
    const FUniqueNetId& UserId,
    const FUniqueNetId& FromId,
    const FOnlineSessionInviteAccelByte& Invite)
    {
    /* Make sure it is a game session.
    * Also check if the invite is not from party leader.
    * Since the party members will automatically join, there is no need to show game session invitation notification.*/
    if (UserId == FromId ||
    Invite.SessionType != EAccelByteV2SessionType::GameSession ||
    OnlineSession->IsPartyLeader(FromId.AsShared()))
    {
    return;
    }

    UE_LOG_PLAYINGWITHFRIENDS(Verbose, TEXT("Invite received from: %s"), *FromId.ToDebugString());

    const APlayerController* PlayerController = OnlineSession->GetPlayerControllerByUniqueNetId(UserId.AsShared());
    if (!PlayerController)
    {
    return;
    }

    const int32 LocalUserNum = OnlineSession->GetLocalUserNumFromPlayerController(PlayerController);
    if (LocalUserNum == INDEX_NONE)
    {
    return;
    }

    const FUniqueNetIdAccelByteUserRef FromABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(FromId.AsShared());
    if (UStartupSubsystem* StartupSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<UStartupSubsystem>())
    {
    StartupSubsystem->QueryUserInfo(
    0,
    TPartyMemberArray{FromABId},
    FOnQueryUsersInfoCompleteDelegate::CreateWeakLambda(this, [this, Invite, LocalUserNum](
    const FOnlineError& Error,
    const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo)
    {
    /**
    * For some reason, calling ShowInviteReceivedPopup through CreateUObject crashes the game.
    * WeakLambda used in place of that.
    */
    if (Error.bSucceeded)
    {
    ShowInviteReceivedPopup(UsersInfo, LocalUserNum, Invite);
    }
    }));
    }
    }
  3. ヘッダーファイルに戻り、以下の関数宣言を追加します:

    private:
    // ...
    void ShowInviteReceivedPopup(
    const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo,
    const int32 LocalUserNum, const FOnlineSessionInviteAccelByte Invite);
  4. CPP ファイルに移動し、以下の実装を追加します。PromptSubsystemByte Wars システムであり、Unreal Engine システムではないことを覚えておいてください。この実装は、承諾と拒否の2つのボタンを持つポップアッププロンプト画面を表示します。以下のハイライトされた行に注目してください。これらは承諾ボタンと拒否ボタンを対応する機能にバインドします。また、この実装では JoinGameSessionConfirmation を呼び出していることに注意してください。これはヘルパー関数の一部です。

    void UPlayingWithFriendsSubsystem_Starter::ShowInviteReceivedPopup(
    const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo,
    const int32 LocalUserNum,
    const FOnlineSessionInviteAccelByte Invite)
    {
    UPromptSubsystem* PromptSubsystem = GetPromptSubsystem();
    if (UsersInfo.IsEmpty() || !PromptSubsystem)
    {
    return;
    }

    const TSharedPtr<FUserOnlineAccountAccelByte> User = UsersInfo[0];
    const FText Message = FText::Format(
    TEXT_FORMAT_INVITED,
    FText::FromString(User->GetDisplayName().IsEmpty() ?
    UTutorialModuleOnlineUtility::GetUserDefaultDisplayName(User->GetUserId().Get()) :
    User->GetDisplayName()));

    FString AvatarURL;
    User->GetUserAttribute(ACCELBYTE_ACCOUNT_GAME_AVATAR_URL, AvatarURL);

    PromptSubsystem->PushNotification(
    Message,
    AvatarURL,
    true,
    TEXT_ACCEPT_INVITE,
    TEXT_REJECT_INVITE,
    FText(),
    FPushNotificationDelegate::CreateWeakLambda(
    this,
    [this, Invite, LocalUserNum](EPushNotificationActionResult ActionButtonResult)
    {
    switch (ActionButtonResult)
    {
    case EPushNotificationActionResult::Button1:
    JoinGameSessionConfirmation(LocalUserNum, Invite);
    break;
    case EPushNotificationActionResult::Button2:
    OnlineSession->RejectSessionInvite(LocalUserNum, Invite);
    break;
    default: /* Do nothing */;
    }
    }));
    }
  5. JoinGameSessionConfirmation() 関数に移動し、以下のコードからハイライトされた行を追加します。これにより、プレイヤーが既にセッションに参加している場合に、別のセッションに参加する前に確認ポップアップ画面を表示する機能が完成します。

    void UPlayingWithFriendsSubsystem_Starter::JoinGameSessionConfirmation(
    const int32 LocalUserNum,
    const FOnlineSessionInviteAccelByte& Invite)
    {
    if (OnlineSession->GetSession(OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession)))
    {
    UPromptSubsystem* PromptSubsystem = GetPromptSubsystem();
    if (!PromptSubsystem)
    {
    return;
    }

    PromptSubsystem->ShowDialoguePopUp(
    TEXT_LEAVING_SESSION,
    TEXT_JOIN_NEW_SESSION_CONFIRMATION,
    EPopUpType::ConfirmationConfirmCancel,
    FPopUpResultDelegate::CreateWeakLambda(this, [this, LocalUserNum, Invite](EPopUpResult Result)
    {
    switch (Result)
    {
    case Confirmed:
    JoinGameSession(LocalUserNum, Invite.Session);
    break;
    case Declined:
    OnlineSession->RejectSessionInvite(LocalUserNum, Invite);
    break;
    }
    }));
    }
    else
    {
    UPromptSubsystem* PromptSubsystem = GetPromptSubsystem();
    if (!PromptSubsystem)
    {
    return;
    }

    PromptSubsystem->ShowLoading(TEXT_JOINING_SESSION);
    JoinGameSession(LocalUserNum, Invite.Session);
    }
    }
  6. 関数をオンラインセッションのデリゲートにバインドします。CPP ファイルで Initialize に移動し、以下のコードを追加します:

    void UPlayingWithFriendsSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    // ...
    OnlineSession->GetOnSessionInviteReceivedDelegates()->AddUObject(
    this, &ThisClass::OnGameSessionInviteReceived);
    // ...
    }
  7. 不要になったらバインドを解除します。引き続き CPP ファイルで Deinitialize に移動し、以下のコードを追加します:

    void UPlayingWithFriendsSubsystem_Starter::Deinitialize()
    {
    // ...
    OnlineSession->GetOnSessionInviteReceivedDelegates()->RemoveAll(this);
    // ...
    }

ゲームセッションメンバー変更通知

すべてのセッションメンバーは、別のプレイヤーがセッションに参加または退出したときにそれを知る方法が必要です。そこでこの実装が登場します。ただし、Byte Wars ではプレイヤーのユーザー名を取得するために QueryUserInfo の呼び出しが必要であることを覚えておいてください。

  1. PlayingWithFriendsSubsystem_Starter ヘッダーファイルを開き、以下の関数宣言を追加します:

    private:
    // ...
    void OnGameSessionParticipantJoined(FName SessionName, const FUniqueNetId& Member);
    void OnGameSessionParticipantLeft(FName SessionName, const FUniqueNetId& Member, EOnSessionParticipantLeftReason Reason);
  2. PlayingWithFriendsSubsystem_Starter CPP ファイルを開き、以下の実装を追加します。この実装では、ユーザー情報をクエリしてユーザー名を取得し、ユーザーがセッションを退出したか参加したかのプッシュ通知を表示します。リーダーが変更されたかどうかを判断するロジックもあります。この実装の詳細については、OnQueryUserInfoOnGameSessionParticipantChange() 関数で確認できます。

    void UPlayingWithFriendsSubsystem_Starter::OnGameSessionParticipantJoined(
    FName SessionName,
    const FUniqueNetId& Member)
    {
    // Abort if not a game session.
    if (!OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    // Only handle the event if the game session is in the game server.
    if (!IsMatchSessionGameSessionReceivedServer())
    {
    return;
    }

    UE_LOG_PLAYINGWITHFRIENDS(Verbose, TEXT("Member JOINED: %s"), *Member.ToDebugString());

    const int32 LocalUserNum = OnlineSession->GetLocalUserNumFromPlayerController(
    OnlineSession->GetPlayerControllerByUniqueNetId(GetSessionOwnerUniqueNetId(SessionName)));
    if (LocalUserNum == INDEX_NONE)
    {
    return;
    }

    TArray<FUniqueNetIdRef> UsersToQuery = {Member.AsShared()};

    // check if leader changed
    if (const FNamedOnlineSession* Session = OnlineSession->GetSession(SessionName))
    {
    if (const TSharedPtr<FOnlineSessionInfoAccelByteV2> AbSessionInfo =
    StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(Session->SessionInfo))
    {
    const FUniqueNetIdPtr NewLeaderId = AbSessionInfo->GetLeaderId();
    if (LeaderId.IsValid())
    {
    if (!OnlineSession->CompareAccelByteUniqueId(
    FUniqueNetIdRepl(LeaderId),
    FUniqueNetIdRepl(NewLeaderId)))
    {
    UE_LOG_PLAYINGWITHFRIENDS(VeryVerbose, TEXT("Leader changed to: %s"), *LeaderId->ToDebugString());

    UsersToQuery.Add(NewLeaderId->AsShared());
    bLeaderChanged = true;
    }
    }
    LeaderId = NewLeaderId;
    }
    }

    if (UStartupSubsystem* StartupSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<UStartupSubsystem>())
    {
    StartupSubsystem->QueryUserInfo(
    0,
    UsersToQuery,
    FOnQueryUsersInfoCompleteDelegate::CreateUObject(
    this,
    &ThisClass::OnQueryUserInfoOnGameSessionParticipantChange,
    SessionName,
    true));
    }
    }
    void UPlayingWithFriendsSubsystem_Starter::OnGameSessionParticipantLeft(
    FName SessionName,
    const FUniqueNetId& Member,
    EOnSessionParticipantLeftReason Reason)
    {
    // Abort if not a game session.
    if (!OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    // Only handle the event if the game session is in the game server.
    if (!IsMatchSessionGameSessionReceivedServer())
    {
    return;
    }

    UE_LOG_PLAYINGWITHFRIENDS(Verbose, TEXT("Member LEFT: %s. Reason: %s"), *Member.ToDebugString(), ToLogString(Reason));

    const int32 LocalUserNum = OnlineSession->GetLocalUserNumFromPlayerController(
    OnlineSession->GetPlayerControllerByUniqueNetId(GetSessionOwnerUniqueNetId(SessionName)));
    if (LocalUserNum == INDEX_NONE)
    {
    return;
    }

    TArray<FUniqueNetIdRef> UsersToQuery = {Member.AsShared()};

    // check if leader changed
    if (const FNamedOnlineSession* Session = OnlineSession->GetSession(SessionName))
    {
    if (const TSharedPtr<FOnlineSessionInfoAccelByteV2> AbSessionInfo =
    StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(Session->SessionInfo))
    {
    const FUniqueNetIdPtr NewLeaderId = AbSessionInfo->GetLeaderId();
    if (LeaderId.IsValid())
    {
    if (!OnlineSession->CompareAccelByteUniqueId(
    FUniqueNetIdRepl(LeaderId),
    FUniqueNetIdRepl(NewLeaderId)))
    {
    UE_LOG_PLAYINGWITHFRIENDS(VeryVerbose, TEXT("Leader changed to: %s"), *LeaderId->ToDebugString());

    UsersToQuery.Add(NewLeaderId->AsShared());
    bLeaderChanged = true;
    }
    }
    LeaderId = NewLeaderId;
    }
    }

    if (UStartupSubsystem* StartupSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<UStartupSubsystem>())
    {
    StartupSubsystem->QueryUserInfo(
    0,
    UsersToQuery,
    FOnQueryUsersInfoCompleteDelegate::CreateUObject(
    this,
    &ThisClass::OnQueryUserInfoOnGameSessionParticipantChange,
    SessionName,
    false));
    }
    }
  3. 関数をオンラインセッションのデリゲートにバインドします。Initialize に移動し、以下のコードを追加します:

    void UPlayingWithFriendsSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    // ...
    OnlineSession->GetOnSessionParticipantJoined()->AddUObject(
    this, &ThisClass::OnGameSessionParticipantJoined);
    OnlineSession->GetOnSessionParticipantLeft()->AddUObject(
    this, &ThisClass::OnGameSessionParticipantLeft);
    // ...
    }
  4. そのデリゲートのバインドを解除します。引き続き CPP ファイルで Deinitialize に移動し、以下のコードを追加します:

    void UPlayingWithFriendsSubsystem_Starter::Deinitialize()
    {
    // ...
    OnlineSession->GetOnSessionParticipantJoined()->RemoveAll(this);
    OnlineSession->GetOnSessionParticipantLeft()->RemoveAll(this);
    // ...
    }

リソース