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

OSS でフレンドを見つける - プレイヤーを検索する - (Unreal Engine モジュール)

Last updated on February 4, 2026

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

サブシステムの展開

このセクションでは、AccelByte Gaming Services (AGS) Online Subsystem (OSS) を使用して、表示名とフレンドコードでフレンド候補を検索する機能の実装方法を学びます。Byte Wars プロジェクトには、FriendsSubsystem という名前の Game Instance Subsystem がすでに作成されています。このサブシステムには、フレンド候補の検索を含むフレンド関連の機能が含まれています。このチュートリアルでは、そのサブシステムのスターター版である FriendsSubsystem_Starter を使用して、フレンド機能をゼロから実装します。

スターターパックの内容

このチュートリアルに従うために、FriendsSubsystem_Starter という名前のスターターサブシステムクラスが用意されています。このクラスは リソース セクションで入手でき、以下のファイルで構成されています。

  • ヘッダーファイル: /Source/AccelByteWars/TutorialModules/Social/FriendsEssentials/FriendsSubsystem_Starter.h
  • CPP ファイル: /Source/AccelByteWars/TutorialModules/Social/FriendsEssentials/FriendsSubsystem_Starter.cpp

FriendsSubsystem_Starter クラスには、すでにいくつかの機能が含まれています。

  • FriendsInterfaceUserInterface という名前の AGS OSS インターフェースの宣言。これらのインターフェースは、後でフレンド関連の機能を実装する際に使用します。

    protected:
    // ...
    FOnlineUserAccelBytePtr UserInterface;
    FOnlineFriendsAccelBytePtr FriendsInterface;
  • PlayerController から UniqueNetId を取得するヘルパー関数。上記の AGS OSS インターフェースで必要になります。

    FUniqueNetIdPtr UFriendsSubsystem_Starter::GetUniqueNetIdFromPlayerController(const APlayerController* PC) const
    {
    if (!PC)
    {
    return nullptr;
    }

    ULocalPlayer* LocalPlayer = PC->GetLocalPlayer();
    if (!LocalPlayer)
    {
    return nullptr;
    }

    return LocalPlayer->GetPreferredUniqueNetId().GetUniqueNetId();
    }
  • PlayerController から LocalUserNum を取得するヘルパー関数。これも後で AGS OSS インターフェースで必要になります。

    int32 UFriendsSubsystem_Starter::GetLocalUserNumFromPlayerController(const APlayerController* PC) const
    {
    int32 LocalUserNum = 0;

    if (!PC)
    {
    return LocalUserNum;
    }

    const ULocalPlayer* LocalPlayer = PC->GetLocalPlayer();
    if (LocalPlayer)
    {
    LocalUserNum = LocalPlayer->GetControllerId();
    }

    return LocalUserNum;
    }

スターターサブシステムに加えて、/Source/AccelByteWars/TutorialModules/Social/FriendsEssentials/FriendsEssentialsModels.h ファイルには、いくつかの定数、デリゲート、その他のヘルパーが用意されています。そのファイルには、以下のヘルパーがあります。

  • フレンドのステータスを定義する列挙型。これは、フレンドがすでに招待されているか、すでにフレンドとして承認されているかなど、フレンドのタイプを区別するために必要です。

    UENUM()
    enum class EFriendStatus : uint8
    {
    Accepted = 0,
    PendingInbound,
    PendingOutbound,
    Searched,
    Blocked,
    Unknown
    };
  • 表示名、アバター URL、ステータスなどのフレンド情報を含む FriendData という名前のヘルパークラス。このデータはエントリーウィジェットにデータを割り当てるために使用されるため、構造体や通常の CPP クラスではなく UObject を使用しています。

    UCLASS()
    class ACCELBYTEWARS_API UFriendData : public UObject
    {
    GENERATED_BODY()

    public:
    UFriendData() : bIsOnline(false), bCannotBeInvited(false) {}

    FUniqueNetIdPtr UserId;
    FString DisplayName{};
    FString AvatarURL{};
    EFriendStatus Status = EFriendStatus::Unknown;

    bool bIsOnline = false;
    FDateTime LastOnline{};

    bool bCannotBeInvited = false;
    FString ReasonCannotBeInvited{};

    EDataSource DataSource = EDataSource::User;

    static UFriendData* ConvertToFriendData(TSharedRef<FOnlineUser> OnlineUser, const UObject* Context)
    {
    if (!Context || !Context->GetWorld())
    {
    return nullptr;
    }

    IOnlineSubsystem* Subsystem = Online::GetSubsystem(Context->GetWorld());
    if (!Subsystem)
    {
    return nullptr;
    }

    IOnlineUserPtr UserInterface = Subsystem->GetUserInterface();
    if (!UserInterface)
    {
    return nullptr;
    }

    UFriendData* FriendData = NewObject<UFriendData>();
    FriendData->UserId = OnlineUser->GetUserId();
    FriendData->DisplayName = OnlineUser->GetDisplayName();
    OnlineUser->GetUserAttribute(ACCELBYTE_ACCOUNT_GAME_AVATAR_URL, FriendData->AvatarURL);
    FriendData->Status = EFriendStatus::Unknown;
    FriendData->bCannotBeInvited = false;

    // If avatar attribute from online user object is empty, try fetch it from the cache.
    TSharedPtr<FOnlineUser> CachedUser = UserInterface->GetUserInfo(0, OnlineUser->GetUserId().Get());
    if (FriendData->AvatarURL.IsEmpty() && CachedUser)
    {
    CachedUser->GetUserAttribute(ACCELBYTE_ACCOUNT_GAME_AVATAR_URL, FriendData->AvatarURL);
    }

    return FriendData;
    }

    static UFriendData* ConvertToFriendData(TSharedRef<FOnlineFriend> OnlineUser, const UObject* Context)
    {
    UFriendData* FriendData = ConvertToFriendData(StaticCast<TSharedRef<FOnlineUser>>(OnlineUser), Context);

    switch (OnlineUser->GetInviteStatus())
    {
    case EInviteStatus::Accepted:
    FriendData->Status = EFriendStatus::Accepted;
    FriendData->bCannotBeInvited = true;
    FriendData->ReasonCannotBeInvited = ALREADY_FRIEND_REASON_MESSAGE.ToString();
    break;
    case EInviteStatus::PendingInbound:
    FriendData->Status = EFriendStatus::PendingInbound;
    FriendData->bCannotBeInvited = true;
    FriendData->ReasonCannotBeInvited = BEEN_INVITED_REASON_MESSAGE.ToString();
    break;
    case EInviteStatus::PendingOutbound:
    FriendData->Status = EFriendStatus::PendingOutbound;
    FriendData->bCannotBeInvited = true;
    FriendData->ReasonCannotBeInvited = ALREADY_INVITED_REASON_MESSAGE.ToString();
    break;
    case EInviteStatus::Blocked:
    FriendData->Status = EFriendStatus::Blocked;
    FriendData->bCannotBeInvited = true;
    FriendData->ReasonCannotBeInvited = BLOCKED_REASON_MESSAGE.ToString();
    break;
    default:
    FriendData->Status = EFriendStatus::Unknown;
    FriendData->bCannotBeInvited = false;
    }

    return FriendData;
    }

    static UFriendData* ConvertToFriendData(TSharedRef<FOnlineBlockedPlayer> OnlineUser, const UObject* Context)
    {
    UFriendData* FriendData = ConvertToFriendData(StaticCast<TSharedRef<FOnlineUser>>(OnlineUser), Context);

    FriendData->Status = EFriendStatus::Blocked;
    FriendData->bCannotBeInvited = true;
    FriendData->ReasonCannotBeInvited = BLOCKED_REASON_MESSAGE.ToString();

    return FriendData;
    }

    static UFriendData* ConvertToFriendData(TSharedRef<const FAccelByteUserInfo> OnlineUser)
    {
    UFriendData* FriendData = NewObject<UFriendData>();

    FriendData->UserId = OnlineUser->Id;
    FriendData->DisplayName = OnlineUser->DisplayName;
    FriendData->AvatarURL = OnlineUser->GameAvatarUrl;
    FriendData->Status = EFriendStatus::Unknown;
    FriendData->bCannotBeInvited = false;
    FriendData->DataSource = EDataSource::GameSessionPlayer;

    return FriendData;
    }

    static UFriendData* ConvertToFriendData(TSharedRef<FOnlineRecentPlayer> OnlineUser, const UObject* Context)
    {
    UFriendData* FriendData = ConvertToFriendData(StaticCast<TSharedRef<FOnlineUser>>(OnlineUser), Context);

    FriendData->DataSource = EDataSource::RecentPlayer;

    return FriendData;
    }
    };
  • フレンドデータのキャッシュ完了、フレンド検索プロセス完了、フレンドリクエスト送信完了時のコールバックとして使用できるデリゲート。

    DECLARE_DELEGATE_ThreeParams(FOnGetCacheFriendListComplete, bool /*bWasSuccessful*/, TArray<TSharedRef<FOnlineFriend>>& /*CachedFriendList*/, const FString& /*ErrorMessage*/);
    // ...
    DECLARE_DELEGATE_ThreeParams(FOnGetSelfFriendCodeComplete, bool /*bWasSuccessful*/, UFriendData* /*FriendData*/, const FString& /*FriendCode*/);
    DECLARE_DELEGATE_ThreeParams(FOnFindFriendComplete, bool /*bWasSuccessful*/, UFriendData* /*FriendData*/, const FString& /*ErrorMessage*/);
    DECLARE_DELEGATE_ThreeParams(FOnSendFriendRequestComplete, bool /*bWasSuccessful*/, UFriendData* /*FriendData*/, const FString& /*ErrorMessage*/);

表示名でフレンドを検索する実装

このセクションでは、表示名でフレンド候補を検索する機能を実装します。

  1. フレンド候補を検索してフレンド招待リクエストを送信する前に、フレンド候補がすでにプレイヤーのフレンドである可能性があります。そのため、まずプレイヤーのフレンドリストを取得する必要があります。次に、そのリストを使用して、見つかったフレンド候補がすでにフレンドかどうかを確認します。FriendsSubsystem_Starter クラスのヘッダーファイルを開き、以下の関数を宣言します。

    protected:
    // ...
    void GetCacheFriendList(const int32 LocalUserNum, bool bQueryUserInfo, const FOnGetCacheFriendListComplete& OnComplete = FOnGetCacheFriendListComplete());
  2. 上記の関数の定義を作成します。FriendsSubsystem_Starter クラスの CPP ファイルを開き、以下のコードを追加します。この関数はバックエンドからフレンドリストを取得してリストをキャッシュします。リストがキャッシュされると、バックエンドに再度リクエストすることなく直接アクセスできます。

    void UFriendsSubsystem_Starter::GetCacheFriendList(const int32 LocalUserNum, bool bQueryUserInfo, const FOnGetCacheFriendListComplete& OnComplete)
    {
    if (!ensure(FriendsInterface))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot cache friend list. Friends Interface is not valid."));
    return;
    }

    // Try to get cached friend list first.
    TArray<TSharedRef<FOnlineFriend>> CachedFriendList;
    if (FriendsInterface->GetFriendsList(LocalUserNum, TEXT(""), CachedFriendList))
    {
    // Then, update the cached friends' information by querying their user information.
    TPartyMemberArray FriendIds;
    for (const TSharedRef<FOnlineFriend>& CachedFriend : CachedFriendList)
    {
    FriendIds.Add(CachedFriend.Get().GetUserId());
    }

    // Query friends' user information.
    if (UStartupSubsystem* StartupSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<UStartupSubsystem>())
    {
    if(bQueryUserInfo)
    {
    StartupSubsystem->QueryUserInfo(
    LocalUserNum,
    FriendIds,
    FOnQueryUsersInfoCompleteDelegate::CreateWeakLambda(this, [this, OnComplete, LocalUserNum](
    const FOnlineError& Error,
    const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo)
    {
    // Refresh friends data with queried friend's user information.
    TArray<TSharedRef<FOnlineFriend>> NewCachedFriendList{};
    FriendsInterface->GetFriendsList(LocalUserNum, TEXT(""), NewCachedFriendList);
    OnComplete.ExecuteIfBound(true, NewCachedFriendList, TEXT(""));
    }));
    }
    else
    {
    OnComplete.ExecuteIfBound(true, CachedFriendList, TEXT(""));
    }
    }
    }
    // If none, request to backend then get the cached the friend list.
    else
    {
    FriendsInterface->ReadFriendsList(
    LocalUserNum,
    TEXT(""),
    FOnReadFriendsListComplete::CreateWeakLambda(this, [this, OnComplete](int32 LocalUserNum, bool bWasSuccessful, const FString& ListName, const FString& Error)
    {
    TArray<TSharedRef<FOnlineFriend>> CachedFriendList;
    FriendsInterface->GetFriendsList(LocalUserNum, TEXT(""), CachedFriendList);

    OnComplete.ExecuteIfBound(bWasSuccessful, CachedFriendList, Error);
    }
    ));
    }
    }
  3. FriendsSubsystem_Starter クラスのヘッダーファイルに戻り、フレンド候補を検索する関数宣言を作成します。

    public:
    // ...
    void FindFriend(const APlayerController* PC, const FString& InKeyword, const FOnFindFriendComplete& OnComplete = FOnFindFriendComplete());
  4. フレンド候補検索プロセスの完了を処理するコールバックも作成する必要があります。

    protected:
    // ...
    void OnFindFriendComplete(bool bWasSuccessful, const FUniqueNetId& UserId, const FString& DisplayName, const FUniqueNetId& FoundUserId, const FString& Error, int32 LocalUserNum, const FOnFindFriendComplete OnComplete);
  5. 上記の関数の定義を作成します。FriendsSubsystem_Starter クラスの CPP ファイルを開き、FindFriend() 関数を定義します。この関数は、表示名でフレンド候補を検索する前に、GetCacheFriendList() 関数を呼び出してキャッシュされたフレンドリストを取得しようとします。キャッシュされたリストは、見つかったフレンド候補がすでにフレンドかどうかを確認するために使用されます。このチェックは、検索プロセスが完了したときのコールバックである OnFindFriendComplete() 関数によって処理されます。

    備考

    バックエンドは、表示名検索を実行するために 3 文字から 20 文字の長さ(両端を含む)のみを受け入れることができます。この範囲外の入力は結果を返しません。ただし、AGS Private Cloud ユーザーは、環境変数を設定することでキーワード文字範囲を変更できます。

    API リファレンス: IAM/PublicSearchUserV3

    void UFriendsSubsystem_Starter::FindFriend(const APlayerController* PC, const FString& InKeyword, const FOnFindFriendComplete& OnComplete)
    {
    const FIAMPublicSystemConfigResponse& PublicSystemConfig = UTutorialModuleOnlineUtility::GetPublicSystemConfig();
    const int32 MinLen = PublicSystemConfig.SearchQueryMinLength, MaxLen = PublicSystemConfig.SearchQueryMaxLength;
    if (InKeyword.Len() < MinLen || InKeyword.Len() > MaxLen)
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot find a friend. Keyword must be between %d and %d characters long."), MinLen, MaxLen);
    OnComplete.ExecuteIfBound(false, nullptr, FText::Format(INVALID_SEARCH_FRIEND_KEYWORD_LENGTH_MESSAGE, FText::AsNumber(MinLen), FText::AsNumber(MaxLen)).ToString());
    return;
    }

    if (!ensure(FriendsInterface) || !ensure(UserInterface))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot find a friend. Friends Interface or User Interface is not valid."));
    return;
    }

    const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);
    const FUniqueNetIdPtr LocalPlayerId = GetUniqueNetIdFromPlayerController(PC);
    if (!ensure(LocalPlayerId.IsValid()))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot find friends. LocalPlayer NetId is not valid."));
    return;
    }

    GetCacheFriendList(LocalUserNum, true, FOnGetCacheFriendListComplete::CreateWeakLambda(this, [this, LocalPlayerId, LocalUserNum, InKeyword, OnComplete](bool bWasSuccessful, TArray<TSharedRef<FOnlineFriend>>& CachedFriendList, const FString& ErrorMessage)
    {
    if (bWasSuccessful)
    {
    // Find friend by exact display name.
    UserInterface->QueryUserIdMapping(LocalPlayerId.ToSharedRef().Get(), InKeyword, IOnlineUser::FOnQueryUserMappingComplete::CreateUObject(this, &ThisClass::OnFindFriendComplete, LocalUserNum, OnComplete));
    }
    else
    {
    OnComplete.ExecuteIfBound(false, nullptr, ErrorMessage);
    }
    }));
    }
  6. OnFindFriendComplete() 関数を定義します。成功すると、この関数はフレンド候補がすでにフレンドかどうかを確認し、コールバックデリゲートを呼び出して結果を返します。

    void UFriendsSubsystem_Starter::OnFindFriendComplete(bool bWasSuccessful, const FUniqueNetId& UserId, const FString& DisplayName, const FUniqueNetId& FoundUserId, const FString& Error, int32 LocalUserNum, const FOnFindFriendComplete OnComplete)
    {
    if (bWasSuccessful)
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Success to find a friend with keyword: %s"), *DisplayName);

    // Check if the found user is the player it self.
    if (UserId == FoundUserId)
    {
    OnComplete.ExecuteIfBound(false, nullptr, CANNOT_INVITE_FRIEND_SELF.ToString());
    return;
    }

    // Check if the found user is already friend.
    TSharedPtr<FOnlineFriend> FoundFriend = FriendsInterface->GetFriend(LocalUserNum, FoundUserId, TEXT(""));
    if (FoundFriend.IsValid())
    {
    OnComplete.ExecuteIfBound(true, UFriendData::ConvertToFriendData(FoundFriend.ToSharedRef(), this), TEXT(""));
    return;
    }

    // Request the found user information to backend (to retrieve avatar URL, display name, etc.)
    if (UStartupSubsystem* StartupSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<UStartupSubsystem>())
    {
    StartupSubsystem->QueryUserInfo(
    LocalUserNum,
    TPartyMemberArray{ FoundUserId.AsShared() },
    FOnQueryUsersInfoCompleteDelegate::CreateWeakLambda(this, [this, OnComplete](
    const FOnlineError& Error,
    const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo)
    {
    if (Error.bSucceeded && !UsersInfo.IsEmpty())
    {
    OnComplete.ExecuteIfBound(true, UFriendData::ConvertToFriendData(UsersInfo[0].ToSharedRef(), this), TEXT(""));
    }
    else
    {
    OnComplete.ExecuteIfBound(false, nullptr, Error.ErrorMessage.ToString());
    }
    }));
    }
    }
    else
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Failed to find a friend with keyword: %s"), *DisplayName);
    OnComplete.ExecuteIfBound(false, nullptr, Error);
    }
    }

フレンドリクエスト送信の実装

このセクションでは、フレンド招待リクエストの送信を実装します。

  1. FriendsSubsystem_Starter クラスのヘッダーファイルを開き、以下の関数を宣言します。

    public:
    // ...
    void SendFriendRequest(const APlayerController* PC, const FUniqueNetIdRepl FriendUserId, const FOnSendFriendRequestComplete& OnComplete = FOnSendFriendRequestComplete());
  2. フレンドリクエスト送信プロセスが完了したときに処理するコールバック関数を作成します。

    protected:
    // ...
    void OnSendFriendRequestComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& FriendId, const FString& ListName, const FString& ErrorStr, const FOnSendFriendRequestComplete OnComplete);
  3. 上記の関数を定義します。FriendsSubsystem_Starter クラスの CPP ファイルを開き、まず SendFriendRequest() 関数を定義します。この関数はフレンドリクエストを送信し、OnSendFriendRequestComplete() 関数を呼び出してコールバックを処理します。

    void UFriendsSubsystem_Starter::SendFriendRequest(const APlayerController* PC, const FUniqueNetIdRepl FriendUserId, const FOnSendFriendRequestComplete& OnComplete)
    {
    if (!ensure(FriendsInterface) || !ensure(PromptSubsystem))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot send friend request. Friends Interface or Prompt Subsystem is not valid."));
    return;
    }

    PromptSubsystem->ShowLoading(SEND_FRIEND_REQUEST_MESSAGE);

    const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);

    // Send friend requests by friend's user ID. We leave the ListName argument empty since the AccelByte OSS does not require it.
    FriendsInterface->SendInvite(LocalUserNum, *FriendUserId.GetUniqueNetId().Get(), TEXT(""), FOnSendInviteComplete::CreateUObject(this, &ThisClass::OnSendFriendRequestComplete, OnComplete));
    }
  4. OnSendFriendRequestComplete() 関数を定義します。この関数は、リクエストが正常に送信されたことをプレイヤーに通知するポップアップ通知を表示し、OnComplete デリゲートをトリガーして、この完了関数がトリガーされたときに呼び出し元が何かを実行できるようにします。

    void UFriendsSubsystem_Starter::OnSendFriendRequestComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& FriendId, const FString& ListName, const FString& ErrorStr, const FOnSendFriendRequestComplete OnComplete)
    {
    PromptSubsystem->HideLoading();

    TSharedPtr<FOnlineFriend> FoundFriend = FriendsInterface->GetFriend(LocalUserNum, FriendId, TEXT(""));
    if (bWasSuccessful && FoundFriend.IsValid())
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Success to send a friend request."));

    PromptSubsystem->ShowMessagePopUp(MESSAGE_PROMPT_TEXT, SUCCESS_SEND_FRIEND_REQUEST);
    OnComplete.ExecuteIfBound(true, UFriendData::ConvertToFriendData(FoundFriend.ToSharedRef(), this), TEXT(""));
    }
    else
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Failed to send a friend request. Error: %s"), *ErrorStr);

    PromptSubsystem->ShowMessagePopUp(ERROR_PROMPT_TEXT, FText::FromString(ErrorStr));
    OnComplete.ExecuteIfBound(false, nullptr, ErrorStr);
    }
    }

フレンドコードによるフレンドリクエスト送信の実装

このセクションでは、フレンドコードを使用してフレンド招待を送信する実装を行います。

  1. FriendsSubsystem_Starter クラスのヘッダーファイルを開き、以下の関数を宣言します。

    public:
    // ...
    void GetSelfFriendCode(const APlayerController* PC, const FOnGetSelfFriendCodeComplete& OnComplete = FOnGetSelfFriendCodeComplete());
  2. FriendsSubsystem_Starter クラスの CPP ファイルを開き、上記の関数を定義します。この関数は、現在ログインしているプレイヤーのフレンドコードを取得します。

    void UFriendsSubsystem_Starter::GetSelfFriendCode(const APlayerController* PC, const FOnGetSelfFriendCodeComplete& OnComplete)
    {
    if (!ensure(UserInterface))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot get self friend code. User Interface is not valid."));
    return;
    }

    const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);
    const FUniqueNetIdPtr LocalPlayerId = GetUniqueNetIdFromPlayerController(PC);
    if (!ensure(LocalPlayerId.IsValid()))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot get self friend code. LocalPlayer NetId is not valid."));
    return;
    }

    // Try to get friend code from cache.
    if (const TSharedPtr<FOnlineUser> UserInfo = UserInterface->GetUserInfo(LocalUserNum, LocalPlayerId.ToSharedRef().Get()))
    {
    if (const TSharedPtr<FUserOnlineAccountAccelByte> UserAccount = StaticCastSharedPtr<FUserOnlineAccountAccelByte>(UserInfo))
    {
    const FString FriendCode = UserAccount->GetPublicCode();
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Successfully obtained self friend code: %s"), *FriendCode);
    OnComplete.ExecuteIfBound(true, UFriendData::ConvertToFriendData(UserInfo.ToSharedRef(), this), FriendCode);
    return;
    }
    }

    // If not available on cache then query the user info to get friend code.
    if (UStartupSubsystem* StartupSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<UStartupSubsystem>())
    {
    StartupSubsystem->QueryUserInfo(
    LocalUserNum,
    TPartyMemberArray{ LocalPlayerId.ToSharedRef() },
    FOnQueryUsersInfoCompleteDelegate::CreateWeakLambda(this, [this, OnComplete](
    const FOnlineError& Error,
    const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo)
    {
    if (!Error.bSucceeded || UsersInfo.IsEmpty())
    {
    UE_LOG_FRIENDS_ESSENTIALS(
    Warning, TEXT("Failed to get self friend code: User info query has failed."));
    OnComplete.ExecuteIfBound(false, nullptr, TEXT(""));
    return;
    }

    const FString FriendCode = UsersInfo[0]->GetPublicCode();
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Successfully obtained self friend code: %s"), *FriendCode);
    OnComplete.ExecuteIfBound(true, UFriendData::ConvertToFriendData(UsersInfo[0].ToSharedRef(), this), FriendCode);
    }));
    }
    }
  3. 次に、FriendsSubsystem_Starter クラスのヘッダーファイルを開き、フレンドコードでフレンドリクエストを送信する以下の関数を宣言します。

    public:
    // ...
    void SendFriendRequest(const APlayerController* PC, const FString& FriendCode, const FOnSendFriendRequestComplete& OnComplete = FOnSendFriendRequestComplete());
  4. リクエストが完了したときに処理するコールバック関数を作成します。

    protected:
    // ...
    void OnSendFriendRequestByFriendCodeComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& FriendId, const FString& ListName, const FString& ErrorStr, const FOnSendFriendRequestComplete OnComplete);
  5. FriendsSubsystem_Starter クラスの CPP ファイルを開き、SendFriendRequest() 関数を定義します。この関数はフレンドコードを使用して直接フレンドリクエストを送信し、OnSendFriendRequestByFriendCodeComplete() 関数を呼び出してコールバックを処理します。

    void UFriendsSubsystem_Starter::SendFriendRequest(const APlayerController* PC, const FString& FriendCode, const FOnSendFriendRequestComplete& OnComplete)
    {
    if (!ensure(FriendsInterface))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot send friend request by friend code. Friends Interface or Prompt Subsystem is not valid."));
    return;
    }

    const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);

    // Send friend requests by friend code. We leave the ListName argument empty since the AccelByte OSS does not require it.
    FriendsInterface->SendInvite(LocalUserNum, FriendCode, TEXT(""), FOnSendInviteComplete::CreateUObject(this, &ThisClass::OnSendFriendRequestByFriendCodeComplete, OnComplete));
    }
  6. OnSendFriendRequestByFriendCodeComplete() 関数を定義します。この関数は、招待が正常に送信されたことを示すポップアップ通知を表示し、OnComplete デリゲートをトリガーして、この関数がトリガーされたときに呼び出し元が何かを実行できるようにします。

    void UFriendsSubsystem_Starter::OnSendFriendRequestByFriendCodeComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& FriendId, const FString& ListName, const FString& ErrorStr, const FOnSendFriendRequestComplete OnComplete)
    {
    // Get cached friend info to retrieve the updated friend data to be returned to the callback.
    FUniqueNetIdPtr FriendUserId = FriendId.AsShared();
    GetCacheFriendList(
    LocalUserNum,
    true,
    FOnGetCacheFriendListComplete::CreateWeakLambda(this, [this, LocalUserNum, bWasSuccessful, FriendUserId, ErrorStr, OnComplete]
    (bool bQueryWasSuccessful, TArray<TSharedRef<FOnlineFriend>>& CachedFriendList, const FString& ErrorMessage)
    {
    UFriendData* FriendData = nullptr;
    if (FriendUserId)
    {
    TSharedPtr<FOnlineFriend> FoundFriend = FriendsInterface->GetFriend(LocalUserNum, FriendUserId.ToSharedRef().Get(), TEXT(""));
    if (FoundFriend)
    {
    FriendData = UFriendData::ConvertToFriendData(FoundFriend.ToSharedRef(), this);
    }
    }

    if (bWasSuccessful)
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Success to send a friend request by friend code."));

    PromptSubsystem->ShowMessagePopUp(MESSAGE_PROMPT_TEXT, SUCCESS_SEND_FRIEND_REQUEST_BY_FRIEND_CODE);
    OnComplete.ExecuteIfBound(true, FriendData, TEXT(""));
    }
    else
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Failed to send a friend request by friend code. Error: %s"), *ErrorStr);
    OnComplete.ExecuteIfBound(false, FriendData, ErrorStr);
    }
    }
    ));
    }

リソース