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

Unity SDK を使用してフレンドサービスを実装する

Last updated on October 23, 2024

Quick Reference

AGS Shared Cloud

This topic is specific to the AccelByte Gaming Services (AGS) Shared Cloud tier.

References
using AccelByte.Api;
using AccelByte.Core;
using AccelByte.Models;
Get Lobby Service
Lobby lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
Friend Notification Events
lobby.FriendsStatusChanged += result =>{};    
lobby.FriendRequestAccepted += result =>{};
lobby.OnIncomingFriendRequest += result =>{};
lobby.OnUnfriend += result =>{};
lobby.FriendRequestCanceled += result =>{};
lobby.FriendRequestRejected += result =>{};
Request Friend
lobby.RequestFriend(userInfo.userId, result =>
{
if (result.IsError)
{
Debug.Log($"Failed to send a friends request: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Sent Friends Request");
}
});
Unfriend
lobby.Unfriend(userId, result =>
{
if (result.IsError)
{
Debug.Log($"Failed to unfriend a friend: code: {result.Error.Code}, message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully unfriend a friend!");
}
});
Load Friend List
lobby.LoadFriendsList(result =>
{
// Check this is not an error
if (!result.IsError)
{
foreach (string friendID in result.Value.friendsId)
{
Debug.Log($"Friend : {friendID}");
}
}
else
{
Debug.LogWarning("Error in Getting Friends");
}
});
List Incoming Friend Requests
// Get all Incoming Friend Requests
lobby.ListIncomingFriends(result =>
{
if (result.IsError)
{
Debug.LogWarning($"Unable to get Incoming Requests Code: {result.Error.Code}, Message: {result.Error.Message}");
}
else
{
// List incoming friends
Debug.Log("Get list incoming friends is successfully");
}
});
List Outgoing Friend Requests
// Get all Outgoing Friend Requests
lobby.ListOutgoingFriends(result =>
{
// Check for an Error
if (result.IsError)
{
Debug.LogWarning($"Unable to get Outgoing Friend Lists Code: {result.Error.Code}, Message: {result.Error.Message}");
}
else
{
// List outgoing friends
Debug.Log("Get list outgoing friends is successfully");
}
});
Get UserData by UserID
AccelByteSDK.GetClientRegistry().GetApi().GetUser().GetUserByUserId(friendID, x =>
{
// Do something with the user Data
});
Get List of players by searching
AccelByteSDK.GetClientRegistry().GetApi().GetUser().SearchUsers(query, result =>
{
if (!result.IsError)
{
// List UserID's Found using Query
}
else
{
Debug.LogWarning($"Unable to Query Users Code: {result.Error.Code}, Message: {result.Error.Message}");
}
});

Quick Start Guide

In this tutorial, you will learn how to use the Friend service. This guide assumes that you have already implemented the Lobby services.

This guide will be more abstract than the previous guides as the specifics for how each game will implement Friends can vary dramatically. Skip to the Step by Step section to see how we implemented each feature in our tutorial project.

There are a handful of concepts and classes to familiarize yourself with (you can find most of these classes within the UserModels.cs file inside the plugin SDK).

The AGS SDK uses a mixture of Websocket and HTTP requests, generally presented to you through a wrapper layer that uses callbacks and actions to return your data. Because of this, you must design your UI to accommodate delayed callbacks, and possibly several layers of callbacks (e.g., requesting a list of Friends > Requesting specific data about friends > Requesting the avatar of a friend).

We will start by adding simple friend logic into the game.

  1. Create a new script called FriendManagementHandler.cs and attach it to the AccelByteHandler game object.

  2. Add the following AccelByte Libraries to the top of the script:

    using AccelByte.Api;
    using AccelByte.Models;
    using AccelByte.Core;
  3. In the AGS SDK, the UserID is used for most functions. For example, to add friends, the function needs a user ID as a parameter. In the real UI, the player rarely uses a user ID to add a friend because a user ID is not easily memorized. Instead, you can use the Search function to find a user ID by using a display name, and then add that player as a friend, as in the example below:

    void AddFriend(string displayName)
    {
    AccelByteSDK.GetClientRegistry().GetApi().GetUser().SearchUsers(displayName, result =>
    {
    // If not an error
    if (!result.IsError)
    {
    Lobby lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
    foreach (PublicUserInfo userInfo in result.Value.data)
    {
    // Check if display name text has the same value as the result
    if (userInfo.displayName == displayName)
    {
    lobby.RequestFriend(userInfo.userId, addResult =>
    {
    if (addResult.IsError)
    {
    Debug.Log($"Failed to send a friends request: error code: {addResult.Error.Code} message: {addResult.Error.Message}");
    }
    else
    {
    Debug.Log("Sent Friends Request");
    }
    });
    }
    else
    {
    Debug.Log("User is not found");
    }
    }
    }
    else
    {
    Debug.LogWarning($"Unable to Query Users Code: {result.Error.Code}, Message: {result.Error.Message}");
    }
    });
    }
  4. You can see the status of the Outgoing Friend Request by using the ListOutgoingFriends function. Any Incoming Friend Requests can be found by using the ListIncomingFriends function. These two functions will return the UserID, not the display name. Use the following code to obtain the Incoming/Outgoing Friend Request lists with display names shown:

    private void DisplayPending()
    {
    Lobby lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
    // Get all Incoming Friend Requests
    lobby.ListIncomingFriends(result =>
    {
    // Check for an Error
    if (result.IsError)
    {
    Debug.LogWarning($"Unable to get Incoming Requests Code: {result.Error.Code}, Message: {result.Error.Message}");
    }
    else
    {
    User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
    // Loop through all the UserID's returned by the Friends callback and get their PublicUserData
    foreach (string userID in result.Value.friendsId)
    {
    // Request the PublicUserData for the specific Friend
    user.GetUserByUserId(userID, userResult =>
    {
    // If there's an error, report it and do nothing else
    if (userResult.IsError)
    {
    Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
    }
    // If we have valid data, log the display name
    else
    {
    Debug.Log($"Friend request from : {userResult.Value.displayName}");
    }
    });
    }
    }
    });

    // Get all outgoing friend requests
    lobby.ListOutgoingFriends(result =>
    {
    // Check for an error
    if (result.IsError)
    {
    Debug.LogWarning($"Unable to get Outgoing Friend Lists Code: {result.Error.Code}, Message: {result.Error.Message}");
    }
    else
    {
    User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
    // Loop through all the UserID's returned by the Friends callback and get their PublicUserData
    foreach (string userID in result.Value.friendsId)
    {
    // Request the PublicUserData for the specific Friend
    user.GetUserByUserId(userID, userResult =>
    {
    // If it's an error, report it and do nothing else
    if (userResult.IsError)
    {
    Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
    }
    // If we have valid data, log the display name
    else
    {
    Debug.Log($"Pending Friend Request to : {userResult.Value.displayName}");
    }
    });
    }
    }
    });
    }
  5. Use the following code to load the Friend List with the current activity and availability:

    public void GetFriends()
    {
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().LoadFriendsList(result =>
    {
    if (!result.IsError)
    {
    // Check if no friends were returned
    if (result.Value.friendsId.Length <= 0)
    {
    return;
    }

    User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();

    // Send requests to create UI for each friend
    Debug.Log("Loaded Friends List Successfully");

    var userIds = new System.Collections.Generic.List<string>();
    foreach (string friendID in result.Value.friendsId)
    {
    userIds.Add(friendID);
    }

    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().BulkGetUserPresence(userIds.ToArray(), bulkResult =>
    {
    if (!bulkResult.IsError)
    {
    foreach (var friend in bulkResult.Value.data)
    {
    user.GetUserByUserId(friend.userID, x =>
    {
    Debug.Log($"Friend : {x.Value.displayName}, Status : {friend.activity}, and Availability : {friend.availability.ToString()}");
    });
    }
    }
    });
    }
    else
    {
    Debug.LogWarning("Error in Getting Friends");
    }
    });
    }

  6. You can also Unfriend the currently displayed friend with the following code:

    private void Unfriend(string userId)
    {
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().Unfriend(userId, result =>
    {
    if (result.IsError)
    {
    Debug.Log($"Failed to unfriend a friend: code: {result.Error.Code}, message: {result.Error.Message}");
    }
    else
    {
    Debug.Log("Successfully unfriend a friend!");
    }
    });
    }
  7. The Friends service features some notification events that will help make your Friends List more responsive. In the following steps, we will show you how to add events in LobbyHandler.cs and on the ConnectToLobby() function, and then how to use the Debug.Log to check that each notification is working correctly.

    • FriendsStatusChanged This event is triggered if a friend's activity or availability status changes.
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().FriendsStatusChanged += result =>
    {
    Debug.Log($"Friend status change into status : {result.Value.activity} and availability : {result.Value.availability} from user : {result.Value.userID}");
    };
    • FriendRequestAccepted This event is triggered if a pending Friend Request is accepted by another player.
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().FriendRequestAccepted += result =>
    {
    Debug.Log($"Accepted a Friend Request from user {result.Value.friendId}");
    };
    • OnIncomingFriendRequest This event is triggered if someone adds the player as a Friend.
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().OnIncomingFriendRequest += result =>
    {
    Debug.Log($"Received a Friend Request from user {result.Value.friendId}");
    };
    • OnUnfriend This event is triggered if the player is unfriended by a Friend.
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().OnUnfriend += result =>
    {
    Debug.Log($"Unfriended User {result.Value.friendId}");
    };

    • FriendRequestCanceled This event is triggered if an Incoming Friend Request is canceled by the requesting player.
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().FriendRequestCanceled += result =>
    {
    Debug.Log($"Cancelled a Friend Request from user {result.Value.userId}");
    };

    • FriendRequestRejected This event is triggered if the player's Friend Request is rejected by another player.
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().FriendRequestRejected += result =>
    {
    Debug.Log($"Rejected a Friend Request from user {result.Value.userId}");
    };

Congratulations! You have implemented the basics of the Friends service!

Continue on for a step-by-step example of the UI and code implementation. Otherwise, you are now ready to move on to the Party service.

Step by step guide

UI implementation
  1. For the Friends Page, create a new panel or scene with the following objects:

    • Header text
    • Exit button
    • An Empty panel, for Friends Page content (optional)
  2. Since the Friends page will have a lot of items to display, we have divided the page into three sections: Friends, Pending, and Blocked. To do this in your own game, add the following UI objects:

    • 3 Buttons: one each for the Friends tab, Pending tab, and Blocked tab.
    • 3 Panels for each tab's button. To avoid confusion, only set the first panel (Friends panel) as active.

    You can add the UIs into separate panels, such as in the example below.

  3. Move to the tab panels. Here, we will prepare the page to list all the Friends items. Add the following to each tab panel:

    1. Friends tab panel:
      • Friends button which will be used to open the Search Friends panel
      • Friends List panel
      • Friends Display placeholder text
      • Friends Display prefab

    Once completed, create a new panel, parent it to Friends List panel, and add the following UIs:

    • Profile image
    • Status Indicator image
    • Display Name text
    • Online Status text
    • Chat button
    • Invite to Party button
    • Unfriend button
    • Block button
    1. Pending tab panel:

      Incoming Request List:

      • Scroll View with Scrollbar Vertical only
      • Incoming Pending Requests list panel, and parent it under the Scroll View's content
      • Incoming placeholder text
      • Incoming Request Display prefab

    Create a new panel, parent it to the Incoming Pending Requests list panel, and add the following UI elements:

    • Profile image
    • Display Name text
    • Accept button
    • Decline button
    • Block button

    Outgoing Request List:

    • Sent Request List panel, and parent it under the Scroll View's content
    • Outgoing placeholder text
    • Outgoing Request Display prefab

    Create a new panel, parent it to the Incoming Pending Requests list panel, and add the following UI elements:

    • Profile image
    • Display Name text
    • Cancel button
    1. Blocked tab panel:
    • Blocked Players List panel
    • Blocked placeholder text
    • Blocked Player Display prefab

    Create a new panel, parent it to the Incoming Blocked Players list panel, and add the following UI elements:

    • Profile image
    • Display Name text
    • Unblock button
  4. For the Search Friends pop-up, create a new panel, and add the following UI elements:

    • Exit button
    • Friend Search input field
    • Scroll View with Scrollbar Vertical only
    • Search placeholder text
    • Add Friends Display prefab

    Create a new panel, parent it to the Incoming Blocked Players list panel, and add the following UI elements:

    • Profile image
    • Display Name text
    • Add Friend button
  5. To make it easier to move to another panel, create a new menu panel and add the following UI elements:

    • Lobby button
    • Friends button
    • Quit button
Code implementation
  1. Go to the FriendsManagementHandler.cs script. Remove the AddFriend and Unfriend functions that you have previously created. We'll add these functionalities later to another function with additional improvement.

  2. Add the following AccelByte and UnityEngine libraries to the top of the script:

    using AccelByte.Api;
    using AccelByte.Models;
    using AccelByte.Core;
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.Serialization;
    using System.Collections.Generic;

    To avoid ambiguous references, add this code to the top of the script:

    using Button = UnityEngine.UI.Button;
  3. Create a new dictionary that will hold Friend UserIDs.

    private Dictionary<string, FriendStatusPanel> friendUIDictionary = new Dictionary<string, FriendStatusPanel>();
  4. Add the following UI references to FriendsManagementHandler.cs:

    [Header("Panels")]
    public GameObject FriendsManagementWindow;

    [FormerlySerializedAs("FriendsPanel")]
    [SerializeField]
    private RectTransform friendsPanel;
    [SerializeField]
    private RectTransform requestsPanel;
    [SerializeField]
    private RectTransform blockedPanel;

    #region Buttons
    [Header("Buttons")]

    [SerializeField]
    private Button friendsTabButton;
    [SerializeField]
    private Button pendingTabButton;
    [SerializeField]
    private Button blockedTabButton;

    [SerializeField]
    private Button exitButton;

    #endregion

    [Header("Friends Panel")]
    [SerializeField]
    private Transform friendsDisplayPanel;

    [FormerlySerializedAs("FriendsDisplayPrefab")]
    [SerializeField]
    private GameObject friendsDisplayPrefab;

    [SerializeField]
    private Button friendsSearchButton;

    [SerializeField]
    private Transform friendsDisplayPlaceholder;

    #region Search
    [Header("Search Panel")]
    [SerializeField]
    private GameObject addFriendsPrefab;
    [SerializeField]
    private Transform friendsSearchPanel;

    [SerializeField]
    private InputField friendsSearchInputField;
    [SerializeField]
    private RectTransform friendsSearchScrollView;
    [SerializeField]
    private Button friendsSearchQuitButton;

    [SerializeField]
    private Transform friendsSearchPlaceholder;

    #endregion

    #region Pending
    [Header("Pending Panel")]
    [SerializeField]
    private RectTransform pendingIncomingRequestsContent;
    [SerializeField]
    private RectTransform pendingOutgoingRequestsContent;

    [SerializeField]
    private GameObject pendingIncomingRequestsPrefab;
    [SerializeField]
    private GameObject pendingOutgoingRequestsPrefab;

    [SerializeField]
    private Transform pendingIncomingRequestPlaceholder;
    [SerializeField]
    private Transform pendingOutgoingRequestPlaceholder;

    [SerializeField]
    private Text pendingIncomingRequestText;
    [SerializeField]
    private Text pendingOutgoingRequestText;

    #endregion

    #region Blocked
    [Header("Blocked")]
    [SerializeField]
    private RectTransform blockedContent;

    [SerializeField]
    private GameObject blockedUserPrefab;

    [SerializeField]
    private Transform blockedDisplayPlaceholder;

    #endregion
  5. Create a function that will destroy all the children of the parent transform to reset the UI.

    /// A utility function to Destroy all Children of the parent transform. Optionally do not remove a specific Transform
    /// <param name="parent">Parent Object to destroy children</param>
    /// <param name="doNotRemove">Optional specified Transform that should NOT be destroyed</param>
    private static void LoopThroughTransformAndDestroy(Transform parent, Transform doNotRemove = null)
    {
    // Loop through all the children and add them to a List to then be deleted
    List<GameObject> toBeDeleted = new List<GameObject>();
    foreach (Transform t in parent)
    {
    //except the Do Not Remove transform if there is one
    if (t != doNotRemove)
    {
    toBeDeleted.Add(t.gameObject);
    }
    }
    // Loop through list and Delete all Children
    for (int i = 0; i < toBeDeleted.Count; i++)
    {
    Destroy(toBeDeleted[i]);
    }
    }
  6. Create a function to display Blocked players in FriendsManagementHandler.cs:

    private void DisplayBlocked()
    {
    // Cleanup First
    LoopThroughTransformAndDestroy(blockedContent.transform, blockedDisplayPlaceholder);

    // Get Blocked List
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().GetListOfBlockedUser(result =>
    {
    if (result.IsError)
    {
    Debug.LogWarning($"Unable to get Blocked Player List Code: {result.Error.Code}, Message: {result.Error.Message}");

    blockedDisplayPlaceholder.gameObject.SetActive(true);
    }
    else
    {
    blockedDisplayPlaceholder.gameObject.SetActive(false);
    User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
    // Loop through all the UserID's returned by the callback and get their PublicUserData
    foreach (BlockedData blockedUser in result.Value.data)
    {
    // Request the PublicUserData for the specific User
    user.GetUserByUserId(blockedUser.blockedUserId, userResult =>
    {
    // If it's an Error, report it and do nothing else
    if (userResult.IsError)
    {
    Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
    }
    });
    }
    }
    });
    }
  7. To change the tab view, prepare an enum variable that will hold the current tab value.

    public enum FriendsMode
    {
    Default,
    Friends,
    Pending,
    Blocked
    };

    private FriendsMode displayMode = FriendsMode.Default;
  8. Create a function that states what will happen if we change the tab UIs based on the current FriendsMode.

    private FriendsMode DisplayMode
    {
    get => displayMode;
    set
    {
    switch (value)
    {
    case FriendsMode.Default:
    friendsPanel.gameObject.SetActive(true);
    requestsPanel.gameObject.SetActive(false);
    blockedPanel.gameObject.SetActive(false);
    break;

    case FriendsMode.Friends:
    friendsPanel.gameObject.SetActive(true);
    requestsPanel.gameObject.SetActive(false);
    blockedPanel.gameObject.SetActive(false);
    GetFriends();
    break;

    case FriendsMode.Pending:
    requestsPanel.gameObject.SetActive(true);
    friendsPanel.gameObject.SetActive(false);
    blockedPanel.gameObject.SetActive(false);
    DisplayPending();
    break;

    case FriendsMode.Blocked:
    blockedPanel.gameObject.SetActive(true);
    friendsPanel.gameObject.SetActive(false);
    requestsPanel.gameObject.SetActive(false);
    DisplayBlocked();
    break;
    }
    }
    }
  9. Create a function to update the Friends query result in the UI:

    private void ListQueriedusers(PagedPublicUsersInfo pagedInfo)
    {
    // Cleanup First
    LoopThroughTransformAndDestroy(friendsSearchScrollView.transform, friendsSearchPlaceholder);

    if (pagedInfo.data.Length <=0)
    {
    friendsSearchPlaceholder.gameObject.SetActive(true);
    }
    else
    {
    friendsSearchPlaceholder.gameObject.SetActive(false);
    foreach (PublicUserInfo info in pagedInfo.data)
    {
    FriendsAddPanel addPanel = Instantiate(addFriendsPrefab,friendsSearchScrollView).GetComponent<FriendsAddPanel>();
    addPanel.Create(info);
    }
    }
    }
  10. Create a function to search for a player in FriendsManagementHandler.cs:

    private void SearchForFriends(string query)
    {
    AccelByteSDK.GetClientRegistry().GetApi().GetUser().SearchUsers(query, result =>
    {
    if (!result.IsError)
    {
    ListQueriedusers(result.Value);
    }
    else
    {
    Debug.LogWarning($"Unable to Query Users Code: {result.Error.Code}, Message: {result.Error.Message}");
    }
    });
    }
  11. Create a function to open the AddFriendPanel prefab.

    private void DisplaySearch()
    {
    friendsSearchPanel.gameObject.SetActive(true);
    LoopThroughTransformAndDestroy(friendsSearchScrollView.transform, friendsSearchPlaceholder);
    friendsSearchPlaceholder.gameObject.SetActive(true);
    }
  12. Create a function to update the Friends Data in the UI.

    public void UpdateFriends(FriendsStatusNotif notification)
    {
    // Find the friend and update it's UI
    if (friendUIDictionary.ContainsKey(notification.userID))
    {
    friendUIDictionary[notification.userID].SetOnlineStatus(notification);
    }
    // Otherwise we should handle this in some way, possibly creating a Friend UI piece
    else
    {
    Debug.Log("Unregistered Friend received a Notification");
    }
    }
  13. Use the Lobby's notification event to call the UpdateFriends() function every time a friend's status updates. To do this, create a function in NotificationHandler.cs:

    /// Called when friend status is changed
    public void OnFriendsStatusChanged(Result<FriendsStatusNotif> result)
    {
    GetComponent<FriendManagementHandler>().UpdateFriends(result.Value);
    }

    You can create more functions for other events and use the debug log to verify if there are any updates.

    /// Called when friend request is accepted
    public void OnFriendRequestAccepted(Result<Friend> result)
    {
    Debug.Log($"Accepted a Friend Request from user {result.Value.friendId}");
    }

    /// Called when there is an incoming friend request
    public void OnIncomingFriendRequest(Result<Friend> result)
    {
    Debug.Log($"Received a Friend Request from user {result.Value.friendId}");
    }

    /// Called when friend is unfriended
    public void OnUnfriend(Result<Friend> result)
    {
    Debug.Log($"Unfriended User {result.Value.friendId}");
    }

    /// Called when friend request is canceled
    public void OnFriendRequestCanceled(Result<Acquaintance> result)
    {
    Debug.Log($"Cancelled a Friend Request from user {result.Value.userId}");
    }

    /// Called when friend request is rejected
    public void OnFriendRequestRejected(Result<Acquaintance> result)
    {
    Debug.Log($"Rejected a Friend Request from user {result.Value.userId}");
    }
  14. Create a script called MenuHandler.cs to handle the menu panel and attach it to the AccelByteHandler game object, then add the following code to the MenuHandler.cs:

    using UnityEngine;
    using UnityEngine.UI;

    public class MenuHandler : MonoBehaviour
    {
    public Transform Menu;

    public Button FriendsButton;

    private bool initialize = false;

    public void Create()
    {
    if (initialize)
    {
    return;
    }

    initialize = true;
    }
    }
  15. Open LobbyHandler.cs and navigate to the ConnectToLobby() function, then add the following code:

    public void ConnectToLobby()
    {
    ...

    //Init menu handler
    GetComponent<MenuHandler>().Create();
    GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);

    ...

    //Friends
    lobby.FriendsStatusChanged += notificationHandler.OnFriendsStatusChanged;
    lobby.FriendRequestAccepted += notificationHandler.OnFriendRequestAccepted;
    lobby.OnIncomingFriendRequest += notificationHandler.OnIncomingFriendRequest;
    lobby.FriendRequestCanceled += notificationHandler.OnFriendRequestCanceled;
    lobby.FriendRequestRejected += notificationHandler.OnFriendRequestRejected;
    lobby.OnUnfriend += notificationHandler.OnUnfriend;
    ...
    }
  16. While still in LobbyHandler.cs, add the following code to the RemoveLobbyListeners() function to reset the Friends Notification delegate:

    public void RemoveLobbyListeners()
    {
    ...
    //Friends
    lobby.FriendsStatusChanged -= notificationHandler.OnFriendsStatusChanged;
    lobby.FriendRequestAccepted -= notificationHandler.OnFriendRequestAccepted;
    lobby.OnIncomingFriendRequest -= notificationHandler.OnIncomingFriendRequest;
    lobby.FriendRequestCanceled -= notificationHandler.OnFriendRequestCanceled;
    lobby.FriendRequestRejected -= notificationHandler.OnFriendRequestRejected;
    lobby.OnUnfriend -= notificationHandler.OnUnfriend;
    }
  17. Once completed, you can create a function that will set up your Friends UI state and listener in FriendsManagementHandler.cs.

    private bool setupStatus = false;
    ...

    public void Setup()
    {
    // Reset the exit button's listener, then add the listener based on the exit screen type
    exitButton.onClick.RemoveAllListeners();
    exitButton.onClick.AddListener(() =>
    {
    FriendsManagementWindow.SetActive(false);
    GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);
    });

    // Check whether the FriendsPanel is already set up or not
    if (setupStatus)
    {
    DisplayMode = FriendsMode.Default;
    return;
    }
    // Run the setup if it hasn't
    else
    {
    setupStatus = true;

    DisplayMode = FriendsMode.Friends;

    // Reset listeners, so it won't be triggered more than once
    friendsTabButton.onClick.RemoveAllListeners();
    pendingTabButton.onClick.RemoveAllListeners();
    blockedTabButton.onClick.RemoveAllListeners();

    // Add the listeners
    friendsTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Friends);
    pendingTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Pending);
    blockedTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Blocked);
    friendsSearchPanel.gameObject.SetActive(false);
    friendsDisplayPlaceholder.gameObject.SetActive(true);
    friendsSearchQuitButton.onClick.AddListener(() => friendsSearchPanel.gameObject.SetActive(false));
    friendsSearchButton.onClick.AddListener(DisplaySearch);
    friendsSearchInputField.onEndEdit.AddListener(SearchForFriends);
    friendsSearchButton.onClick.AddListener(() => friendsSearchPanel.gameObject.SetActive(true));
    }
    }
  18. To prepare the Friends panel so it can be opened from the Menu, add the following code to the MenuHandler.cs:

    public void Create()
    {
    ...

    FriendsButton.onClick.AddListener(() =>
    {
    GetComponent<FriendsManagementHandler>().Setup();
    Menu.gameObject.SetActive(false);
    GetComponent<FriendsManagementHandler>().FriendsManagementWindow.SetActive(true);
    });
    }
  19. In the Unity Editor, navigate to the AccelByteHandler game object and drag the following objects to their references.

  20. Create a new script called FriendStatusPanel.cs and attach it to the FriendDisplay prefab.

  21. Add the following AccelByte and UnityEngine libraries to the top of the script:

    using AccelByte.Core;
    using AccelByte.Models;
    using UnityEngine;
    using UnityEngine.UI;

    To avoid ambiguous references, add this code to the top of the script:

    using Image = UnityEngine.UI.Image;
  22. Add the following references to the necessary UIs in FriendStatusPanel.cs:

    [SerializeField]
    private Image profilePicture;
    [SerializeField]
    private Image statusDisplay;

    [SerializeField]
    private Button chatButton;
    [SerializeField]
    private Button inviteToPartyButton;
    [SerializeField]
    private Button unfriendButton;
    [SerializeField]
    private Button blockButton;

    [SerializeField]
    private Text displayNameText;
    [SerializeField]
    private Text onlineStatusText;

    Once completed, create a variable that will hold the User Data:

    PublicUserData userData;
  23. Create a function to update the Friends list online statuses UI in FriendStatusPanel.cs.

    public void SetOnlineStatus(FriendsStatusNotif notification)
    {
    switch (notification.availability)
    {
    case UserStatus.Offline:
    onlineStatusText.text = "Offline";
    statusDisplay.color = Color.black;
    break;
    case UserStatus.Online:
    onlineStatusText.text = "Online";
    statusDisplay.color = Color.green;
    break;
    case UserStatus.Busy:
    onlineStatusText.text = "Busy";
    statusDisplay.color = Color.yellow;
    break;
    case UserStatus.Invisible:
    onlineStatusText.text = "Offline";
    statusDisplay.color = Color.black;
    break;
    default:
    onlineStatusText.text = $"INVALID UNHANDLED {notification.availability}";
    statusDisplay.color = Color.magenta;
    break;
    }
    Debug.Log($"Friend Status for {notification.userID} changed to {notification.availability}");
    }
  24. While still in FriendStatusPanel.cs, create functions to update user data in the UI and the player's profile picture (avatar).

    public void Create(PublicUserData publicUserData)
    {
    userData = publicUserData;
    displayNameText.text = userData.displayName;
    }
  25. While still in FriendStatusPanel.cs, create a new function that will handle all the buttons in the display prefab, and then add a listener to Unfriend with the SDK service.

    public void SetupButton()
    {
    unfriendButton.onClick.AddListener(() =>
    {
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().Unfriend(userData.userId, result =>
    {
    if (result.IsError)
    {
    Debug.Log($"Failed to unfriend a friend: code: {result.Error.Code}, message: {result.Error.Message}");
    }
    else
    {
    Debug.Log("Successfully unfriend a friend!");
    LobbyHandler.Instance.GetComponent<FriendsManagementHandler>().GetFriends();
    }
    });
    });
    }
  26. In the Unity Editor, on your FriendsDisplay prefab, drag the following objects to the corresponding references.

  27. Create a script called FriendIncomingPanel.cs.

  28. Add the following AccelByte and UnityEngine Libraries to the top of the script:

    using AccelByte.Core;
    using AccelByte.Models;
    using UnityEngine;
    using UnityEngine.UI;
    using Image = UnityEngine.UI.Image;
  29. Add some UI references and a variable to hold the User Data:

    [SerializeField]
    private Image profilePicture;

    [SerializeField]
    private Text displayNameText;
    [SerializeField]
    private Button acceptFriendButton;
    [SerializeField]
    private Button declineFriendButton;
    [SerializeField]
    private Button blockFriendButton;

    private PublicUserData userData;
  30. Create functions to Accept and Reject a Friend Request.

    /// An initialization Function required to be called to populate the UI appropriately
    public void Create(PublicUserData userData)
    {
    // Cache the PublicUserData
    this.userData = userData;
    // Set the Display Name
    displayNameText.text = userData.displayName;
    // Set up the button to accept a friend request
    acceptFriendButton.onClick.AddListener(() =>
    {
    // Make the call to accept the friend request using the cached PublicUserData UserID
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().AcceptFriend(userData.userId, result =>
    {
    if (result.IsError)
    {
    // Display some user feedback if the request is not successful
    Debug.Log($"Failed to accept a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
    }
    else
    {
    Debug.Log("Accepted Friend Request");
    // Destroy the UI piece if the request was successful to remove it from the list
    Destroy(gameObject);
    }
    });

    });

    // Set up the button to decline a friend request
    declineFriendButton.onClick.AddListener(() =>
    {
    // Make the call to decline the friend request using the cached PublicUserData UserID
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().RejectFriend(userData.userId, result =>
    {
    if (result.IsError)
    {
    // Display some user feedback if the request is not successful
    Debug.Log($"Failed to decline a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
    }
    else
    {
    Debug.Log("Declined Friend Request");
    // Destroy the UI Piece if the request was successful to remove it from the list
    Destroy(gameObject);
    }
    });

    });

    // Set up the button to block a player
    blockFriendButton.onClick.AddListener(() =>
    {
    // Make the call to block a player using the cached PublicUserData UserID
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().BlockPlayer(this.userData.userId, result =>
    {
    if (result.IsError)
    {
    // Display user feedback if the request is not successful
    Debug.Log($"Failed to block a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
    }
    else
    {
    Debug.Log("Block Friend Request");
    // Destroy the UI piece if the request was successful to remove it from the list
    Destroy(gameObject);
    }
    });

    });
    }
  31. In the Unity Editor, in your IncomingRequestDisplay prefab, drag the following objects to the exposed variables in the script.

  32. Create a script called FriendsOutgoingPanel.cs.

  33. Add the following AccelByte and UnityEngine libraries to the top of the script:

    using AccelByte.Core;
    using AccelByte.Models;
    using UnityEngine;
    using UnityEngine.UI;
    using Image = UnityEngine.UI.Image;
  34. Add the following references for the UI and a variable that will hold the User Data:

    [SerializeField]
    private Image profilePicture;

    [SerializeField]
    private Text displayNameText;
    [SerializeField]
    private Button cancelRequestButton;

    private PublicUserData userData;
  35. Create a function to cancel a Friend Request and obtain the player's avatar, then update it in the UI.

    public void Create(PublicUserData userData)
    {
    // Cache the PublicUserData
    this.userData = userData;
    // Set the Display Name
    displayNameText.text = userData.displayName;
    // Set up the button to cancel a friend request
    cancelRequestButton.onClick.AddListener(() =>
    {
    // Make the call to reject the friend request using the cached PublicUserData UserID
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().CancelFriendRequest(this.userData.userId, result =>
    {
    if (result.IsError)
    {
    // Display feedback if the request is not successful
    Debug.Log($"Failed to cancel a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
    }
    else
    {
    Debug.Log("Cancelled Friend Request");
    // Destroy the UI piece if the request was successful to remove it from the list
    Destroy(gameObject);
    }
    });
    });
    }
  36. In the Unity Editor, on your OutgoingRequestDisplay prefab, drag the following objects to the variable references in the script.

  37. Duplicate the FriendsOutgoingPanel.cs and rename the duplicate FriendsBlockedPanel.cs. Revise the UI references in this script as follows:

    [SerializeField]
    private Image profilePicture;
    [SerializeField]
    private Text displayNameText;
    [SerializeField]
    private Button unblockButton;

    private PublicUserData userData;
  38. Delete any button setups for AddListener and add the following code:

    public void Create(PublicUserData userData)
    {
    ...
    // Set up the button to unblock a user
    unblockButton.onClick.AddListener(() =>
    {
    // Make the call to unblock the given user using the cached PublicUserData UserID
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().UnblockPlayer(this.userData.userId, result =>
    {
    if (result.IsError)
    {
    // Display feedback if the request is not successful
    Debug.Log($"Failed to unblock a player: error code: {result.Error.Code} message: {result.Error.Message}");
    }
    else
    {
    Debug.Log("Unblocked a Player");
    // Destroy the UI piece if the request was successful to remove it from the list
    Destroy(gameObject);
    }
    });
    });
    ...
    }
  39. In the Unity Editor, open the BlockedDisplay prefab and drag the following objects to the public variable in the script.

  40. Duplicate the FriendsOutgoingPanel.cs and rename the duplicate to FriendsAddPanel.cs. Revise the UI references in the script as follows:

    [SerializeField]
    private Image profilePicture;
    [SerializeField]
    private Text displayNameText;
    [SerializeField]
    private Button addFriendButton;

    private PublicUserData userData;
  41. Delete any button setups for AddListener and add the following code:

    public void Create(PublicUserData userData)
    {
    ...
    // Set up the button to request a friend
    addFriendButton.onClick.AddListener(() =>
    {
    // Make the call to initiate the friend request
    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().RequestFriend(this.userData.userId, result =>
    {
    if (result.IsError)
    {
    Debug.Log($"Failed to send a friends request: error code: {result.Error.Code} message: {result.Error.Message}");
    }
    else
    {
    Debug.Log("Sent Friends Request");
    // If we were successful, set the button to be non-intractable and change the text
    addFriendButton.interactable = false;
    addFriendButton.GetComponentInChildren<Text>().text = "Request Sent";
    }
    });
    });
    ...
    }
  42. In the Unity Editor, open the AddFriendsDisplay prefab and drag the following objects to the variable in the script component.

  43. Now, in FriendsManagementHandler.cs, create a function that will create a new FriendStatusPanel prefab with the userData.

    private void CreateFriendUI(PublicUserData userData)
    {
    FriendStatusPanel panel = Instantiate(friendsDisplayPrefab, friendsDisplayPanel).GetComponent<FriendStatusPanel>();
    panel.Create(userData);
    if (!friendUIDictionary.ContainsKey(userData.userId))
    {
    friendUIDictionary.Add(userData.userId, panel);
    }

    // Set up the FriendStatusPanel's related button
    panel.SetupButton();
    }
  44. Modify the function in FriendsManagementHandler.cs to obtain all available friend data, such as in the following example:

    /// Get Friends and Display them
    public void GetFriends()
    {

    // Cleanup First
    LoopThroughTransformAndDestroy(friendsDisplayPanel.transform, friendsDisplayPlaceholder);

    AccelByteSDK.GetClientRegistry().GetApi().GetLobby().LoadFriendsList(result =>
    {
    //Check this is not an error
    if (!result.IsError)
    {
    // Check if no friends were returned
    if (result.Value.friendsId.Length <= 0)
    {
    // Display the Placeholder Text
    friendsDisplayPlaceholder.gameObject.SetActive(true);
    return;
    }

    User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();

    // Hide the Placeholder Text
    friendsDisplayPlaceholder.gameObject.SetActive(false);

    // Send requests to create UI for each friend
    Debug.Log("Loaded Friends List Successfully");
    foreach (string friendID in result.Value.friendsId)
    {
    ...
    user.GetUserByUserId(friendID, x =>
    {
    CreateFriendUI(x.Value);
    });
    }
    }
    else
    {
    // Display the Placeholder
    friendsDisplayPlaceholder.gameObject.SetActive(true);
    ...
    }
    });
    }
  45. Once completed, you will need to modify a function to list any pending Incoming/Outgoing Friend Requests. In FriendsManagementHandler.cs, modify the following codes:

    private void DisplayPending()
    {
    // Cleanup First, remove all Children from the contents OTHER than the placeholders
    LoopThroughTransformAndDestroy(pendingIncomingRequestsContent.transform, pendingIncomingRequestPlaceholder);
    LoopThroughTransformAndDestroy(pendingOutgoingRequestsContent.transform, pendingOutgoingRequestPlaceholder);

    Lobby lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
    // Get all Incoming Friend Requests
    lobby.ListIncomingFriends(result =>
    {
    if (result.IsError)
    {
    ...
    // Set the placeholder Text to be active so it doesn't look broken
    pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
    }
    else
    {
    // If there are zero incoming Requests, set the placeholder to be active
    if (result.Value.friendsId.Length <= 0)
    {
    pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
    }
    // Otherwise set the placeholder to be inactive
    else
    {
    pendingIncomingRequestPlaceholder.gameObject.SetActive(false);
    }

    // Loop through all the UserID's returned by the Friends callback and get their PublicUserData
    User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
    foreach (string userID in result.Value.friendsId)
    {
    // Request the PublicUserData for the specific Friend
    user.GetUserByUserId(userID, userResult =>
    {
    if (userResult.IsError)
    ...
    else
    {
    FriendsIncomingPanel incomingPanel = Instantiate(pendingIncomingRequestsPrefab, pendingIncomingRequestsContent).GetComponent<FriendsIncomingPanel>();
    // Pass the PublicUserData into this function
    incomingPanel.Create(userResult.Value);
    }
    });
    }
    }
    });

    lobby.ListOutgoingFriends(result =>
    {
    if (result.IsError)
    ...
    else
    {
    if (result.Value.friendsId.Length <= 0)
    {
    pendingOutgoingRequestPlaceholder.gameObject.SetActive(true);
    }
    else
    {
    pendingOutgoingRequestPlaceholder.gameObject.SetActive(false);
    }

    User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
    // Loop through all the UserID's returned by the Friends callback and get their PublicUserData
    foreach (string userID in result.Value.friendsId)
    {
    user.GetUserByUserId(userID, userResult =>
    {
    if (userResult.IsError)
    ...
    else
    {
    FriendsOutgoingPanel outgoingPanel = Instantiate(pendingOutgoingRequestsPrefab, pendingOutgoingRequestsContent).GetComponent<FriendsOutgoingPanel>();
    outgoingPanel.Create(userResult.Value);
    }
    });
    }
    }
    });
    }
  46. Add the following code in the DisplayBlocked() function to instantiate the display prefab for blocked players in FriendsManagementHandler.cs:

    ...
    user.GetUserByUserId(blockedUser.blockedUserId, userResult =>
    {
    ...
    if (userResult.IsError)
    {
    ...
    }
    else
    {
    // If we have valid data, instantiate the prefab for the specific UI piece and call relevant functions
    FriendsBlockedPanel blockedPanel = Instantiate(blockedUserPrefab, blockedContent).GetComponent<FriendsBlockedPanel>();
    // Pass the PublicUserData into this function
    blockedPanel.Create(userResult.Value);
    }
    });
    ...
  47. In the Unity Editor, navigate to the AccelByteHandler game object and drag the following objects to their references in the FriendsManagementHandler script component:

    • Panel and buttons references
    • Friends panel and Search panel references
    • Pending panel and Blocked panel references

Congratulations! You have now fully implemented the Friends service.

Full code for reference

`FriendsManagementHandler.cs`
using AccelByte.Api;
using AccelByte.Models;
using AccelByte.Core;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Serialization;
using Button = UnityEngine.UI.Button;
using System.Collections.Generic;

public class FriendsManagementHandler : MonoBehaviour
{
private Dictionary<string, FriendStatusPanel> friendUIDictionary = new Dictionary<string, FriendStatusPanel>();

[Header("Panels")]
public GameObject FriendsManagementWindow;

[FormerlySerializedAs("FriendsPanel")]
[SerializeField]
private RectTransform friendsPanel;
[SerializeField]
private RectTransform requestsPanel;
[SerializeField]
private RectTransform blockedPanel;

#region Buttons
[Header("Buttons")]

[SerializeField]
private Button friendsTabButton;
[SerializeField]
private Button pendingTabButton;
[SerializeField]
private Button blockedTabButton;

[SerializeField]
private Button exitButton;

#endregion

[Header("Friends Panel")]
[SerializeField]
private Transform friendsDisplayPanel;

[FormerlySerializedAs("FriendsDisplayPrefab")]
[SerializeField]
private GameObject friendsDisplayPrefab;

[SerializeField]
private Button friendsSearchButton;

[SerializeField]
private Transform friendsDisplayPlaceholder;

#region Search
[Header("Search Panel")]
[SerializeField]
private GameObject addFriendsPrefab;
[SerializeField]
private Transform friendsSearchPanel;

[SerializeField]
private InputField friendsSearchInputField;
[SerializeField]
private RectTransform friendsSearchScrollView;
[SerializeField]
private Button friendsSearchQuitButton;

[SerializeField]
private Transform friendsSearchPlaceholder;

#endregion

#region Pending
[Header("Pending Panel")]
[SerializeField]
private RectTransform pendingIncomingRequestsContent;
[SerializeField]
private RectTransform pendingOutgoingRequestsContent;

[SerializeField]
private GameObject pendingIncomingRequestsPrefab;
[SerializeField]
private GameObject pendingOutgoingRequestsPrefab;

[SerializeField]
private Transform pendingIncomingRequestPlaceholder;
[SerializeField]
private Transform pendingOutgoingRequestPlaceholder;

[SerializeField]
private Text pendingIncomingRequestText;
[SerializeField]
private Text pendingOutgoingRequestText;

#endregion

#region Blocked
[Header("Blocked")]
[SerializeField]
private RectTransform blockedContent;

[SerializeField]
private GameObject blockedUserPrefab;

[SerializeField]
private Transform blockedDisplayPlaceholder;

#endregion

private bool setupStatus = false;

public enum FriendsMode
{
Default,
Friends,
Pending,
Blocked
};

private FriendsMode displayMode = FriendsMode.Default;

private FriendsMode DisplayMode
{
get => displayMode;
set
{
switch (value)
{
case FriendsMode.Default:
friendsPanel.gameObject.SetActive(true);
requestsPanel.gameObject.SetActive(false);
blockedPanel.gameObject.SetActive(false);
break;

case FriendsMode.Friends:
friendsPanel.gameObject.SetActive(true);
requestsPanel.gameObject.SetActive(false);
blockedPanel.gameObject.SetActive(false);
GetFriends();
break;

case FriendsMode.Pending:
requestsPanel.gameObject.SetActive(true);
friendsPanel.gameObject.SetActive(false);
blockedPanel.gameObject.SetActive(false);
DisplayPending();
break;

case FriendsMode.Blocked:
blockedPanel.gameObject.SetActive(true);
friendsPanel.gameObject.SetActive(false);
requestsPanel.gameObject.SetActive(false);
DisplayBlocked();
break;
}
}
}

public void Setup()
{
// Reset the exit button's listener, then add the listener based on the exit screen type
exitButton.onClick.RemoveAllListeners();
exitButton.onClick.AddListener(() =>
{
FriendsManagementWindow.SetActive(false);
GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);
});

// Check whether the FriendsPanel is already set up or not
if (setupStatus)
{
DisplayMode = FriendsMode.Default;
return;
}
// Run the setup if it hasn't
else
{
setupStatus = true;

DisplayMode = FriendsMode.Friends;

// Reset listeners, so it won't be triggered more than once
friendsTabButton.onClick.RemoveAllListeners();
pendingTabButton.onClick.RemoveAllListeners();
blockedTabButton.onClick.RemoveAllListeners();

// Add the listeners
friendsTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Friends);
pendingTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Pending);
blockedTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Blocked);
friendsSearchPanel.gameObject.SetActive(false);
friendsDisplayPlaceholder.gameObject.SetActive(true);
friendsSearchQuitButton.onClick.AddListener(() => friendsSearchPanel.gameObject.SetActive(false));
friendsSearchButton.onClick.AddListener(DisplaySearch);
friendsSearchInputField.onEndEdit.AddListener(SearchForFriends);
friendsSearchButton.onClick.AddListener(() => friendsSearchPanel.gameObject.SetActive(true));
}
}

/// A utility function to Destroy all Children of the parent transform. Optionally do not remove a specific Transform
/// <param name="parent">Parent Object to destroy children</param>
/// <param name="doNotRemove">Optional specified Transform that should NOT be destroyed</param>
private static void LoopThroughTransformAndDestroy(Transform parent, Transform doNotRemove = null)
{
// Loop through all the children and add them to a List to then be deleted
List<GameObject> toBeDeleted = new List<GameObject>();
foreach (Transform t in parent)
{
//except the Do Not Remove transform if there is one
if (t != doNotRemove)
{
toBeDeleted.Add(t.gameObject);
}
}
// Loop through list and Delete all Children
for (int i = 0; i < toBeDeleted.Count; i++)
{
Destroy(toBeDeleted[i]);
}
}

private void DisplayPending()
{
LoopThroughTransformAndDestroy(pendingIncomingRequestsContent.transform, pendingIncomingRequestPlaceholder);
LoopThroughTransformAndDestroy(pendingOutgoingRequestsContent.transform, pendingOutgoingRequestPlaceholder);

Lobby lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
// Get all Incoming Friend Requests
lobby.ListIncomingFriends(result =>
{
// Check for an Error
if (result.IsError)
{
Debug.LogWarning($"Unable to get Incoming Requests Code: {result.Error.Code}, Message: {result.Error.Message}");

// Set the placeholder Text to be active so it doesn't look broken
pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
}
else
{
if (result.Value.friendsId.Length <= 0)
{
pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
}
else
{
pendingIncomingRequestPlaceholder.gameObject.SetActive(false);
}

// Loop through all the UserID's returned by the Friends callback and get their PublicUserData
User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
foreach (string userID in result.Value.friendsId)
{
// Request the PublicUserData for the specific Friend
user.GetUserByUserId(userID, userResult =>
{
// If there's an error, report it and do nothing else
if (userResult.IsError)
{
Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
}
// If we have valid data, log the display name
else
{
Debug.Log($"Friend request from : {userResult.Value.displayName}");
FriendsOutgoingPanel outgoingPanel = Instantiate(pendingOutgoingRequestsPrefab, pendingOutgoingRequestsContent).GetComponent<FriendsOutgoingPanel>();
outgoingPanel.Create(userResult.Value);
}
});
}
}
});

// Get all outgoing friend requests
lobby.ListOutgoingFriends(result =>
{
// Check for an error
if (result.IsError)
{
Debug.LogWarning($"Unable to get Outgoing Friend Lists Code: {result.Error.Code}, Message: {result.Error.Message}");
}
else
{
if (result.Value.friendsId.Length <= 0)
{
pendingOutgoingRequestPlaceholder.gameObject.SetActive(true);
}
else
{
pendingOutgoingRequestPlaceholder.gameObject.SetActive(false);
}

User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
// Loop through all the UserID's returned by the Friends callback and get their PublicUserData
foreach (string userID in result.Value.friendsId)
{
// Request the PublicUserData for the specific Friend
user.GetUserByUserId(userID, userResult =>
{
// If it's an error, report it and do nothing else
if (userResult.IsError)
{
Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
}
// If we have valid data, log the display name
else
{
Debug.Log($"Pending Friend Request to : {userResult.Value.displayName}");
FriendsOutgoingPanel outgoingPanel = Instantiate(pendingOutgoingRequestsPrefab, pendingOutgoingRequestsContent).GetComponent<FriendsOutgoingPanel>();
outgoingPanel.Create(userResult.Value);
}
});
}
}
});
}

private void DisplayBlocked()
{
// Cleanup First
LoopThroughTransformAndDestroy(blockedContent.transform, blockedDisplayPlaceholder);

// Get Blocked List
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().GetListOfBlockedUser(result =>
{
if (result.IsError)
{
Debug.LogWarning($"Unable to get Blocked Player List Code: {result.Error.Code}, Message: {result.Error.Message}");

blockedDisplayPlaceholder.gameObject.SetActive(true);
}
else
{
blockedDisplayPlaceholder.gameObject.SetActive(false);
User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
// Loop through all the UserID's returned by the callback and get their PublicUserData
foreach (BlockedData blockedUser in result.Value.data)
{
// Request the PublicUserData for the specific User
user.GetUserByUserId(blockedUser.blockedUserId, userResult =>
{
// If it's an Error, report it and do nothing else
if (userResult.IsError)
{
Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
}
else
{
// If we have valid data, instantiate the prefab for the specific UI piece and call relevant functions
FriendsBlockedPanel blockedPanel = Instantiate(blockedUserPrefab, blockedContent).GetComponent<FriendsBlockedPanel>();
// Pass the PublicUserData into this function
blockedPanel.Create(userResult.Value);
}
});
}
}
});
}

public void GetFriends()
{
// Cleanup First
LoopThroughTransformAndDestroy(friendsDisplayPanel.transform, friendsDisplayPlaceholder);

AccelByteSDK.GetClientRegistry().GetApi().GetLobby().LoadFriendsList(result =>
{
if (!result.IsError)
{
// Check if no friends were returned
if (result.Value.friendsId.Length <= 0)
{
// Display the Placeholder Text
friendsDisplayPlaceholder.gameObject.SetActive(true);
return;
}

User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();

// Send requests to create UI for each friend
Debug.Log("Loaded Friends List Successfully");

var userIds = new System.Collections.Generic.List<string>();
foreach (string friendID in result.Value.friendsId)
{
userIds.Add(friendID);

user.GetUserByUserId(friendID, x =>
{
CreateFriendUI(x.Value);
});
}

AccelByteSDK.GetClientRegistry().GetApi().GetLobby().BulkGetUserPresence(userIds.ToArray(), bulkResult =>
{
if (!bulkResult.IsError)
{
foreach (var friend in bulkResult.Value.data)
{
user.GetUserByUserId(friend.userID, x =>
{
Debug.Log($"Friend : {x.Value.displayName}, Status : {friend.activity}, and Availability : {friend.availability.ToString()}");
});
}
}
});
}
else
{
Debug.LogWarning("Error in Getting Friends");
}
});
}

private void AddEvents()
{
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().FriendsStatusChanged += result =>
{
Debug.Log($"Friend status change into status : {result.Value.activity} and availability : {result.Value.availability} from user : {result.Value.userID}");
};

AccelByteSDK.GetClientRegistry().GetApi().GetLobby().FriendRequestAccepted += result =>
{
Debug.Log($"Accepted a Friend Request from user {result.Value.friendId}");
};

AccelByteSDK.GetClientRegistry().GetApi().GetLobby().OnIncomingFriendRequest += result =>
{
Debug.Log($"Received a Friend Request from user {result.Value.friendId}");
};

AccelByteSDK.GetClientRegistry().GetApi().GetLobby().OnUnfriend += result =>
{
Debug.Log($"Unfriended User {result.Value.friendId}");
};

AccelByteSDK.GetClientRegistry().GetApi().GetLobby().FriendRequestCanceled += result =>
{
Debug.Log($"Cancelled a Friend Request from user {result.Value.userId}");
};

AccelByteSDK.GetClientRegistry().GetApi().GetLobby().FriendRequestRejected += result =>
{
Debug.Log($"Rejected a Friend Request from user {result.Value.userId}");
};
}

private void ListQueriedusers(PagedPublicUsersInfo pagedInfo)
{
// Cleanup First
LoopThroughTransformAndDestroy(friendsSearchScrollView.transform, friendsSearchPlaceholder);

if (pagedInfo.data.Length <= 0)
{
friendsSearchPlaceholder.gameObject.SetActive(true);
}
else
{
friendsSearchPlaceholder.gameObject.SetActive(false);
foreach (PublicUserInfo info in pagedInfo.data)
{
FriendsAddPanel addPanel = Instantiate(addFriendsPrefab, friendsSearchScrollView).GetComponent<FriendsAddPanel>();
addPanel.Create(info);
}
}
}

private void SearchForFriends(string query)
{
AccelByteSDK.GetClientRegistry().GetApi().GetUser().SearchUsers(query, result =>
{
if (!result.IsError)
{
ListQueriedusers(result.Value);
}
else
{
Debug.LogWarning($"Unable to Query Users Code: {result.Error.Code}, Message: {result.Error.Message}");
}
});
}

private void DisplaySearch()
{
friendsSearchPanel.gameObject.SetActive(true);
LoopThroughTransformAndDestroy(friendsSearchScrollView.transform, friendsSearchPlaceholder);
friendsSearchPlaceholder.gameObject.SetActive(true);
}

public void UpdateFriends(FriendsStatusNotif notification)
{
// Find the friend and update it's UI
if (friendUIDictionary.ContainsKey(notification.userID))
{
friendUIDictionary[notification.userID].SetOnlineStatus(notification);
}
// Otherwise we should handle this in some way, possibly creating a Friend UI piece
else
{
Debug.Log("Unregistered Friend received a Notification");
}
}

private void CreateFriendUI(PublicUserData userData)
{
FriendStatusPanel panel = Instantiate(friendsDisplayPrefab, friendsDisplayPanel).GetComponent<FriendStatusPanel>();
panel.Create(userData);
if (!friendUIDictionary.ContainsKey(userData.userId))
{
friendUIDictionary.Add(userData.userId, panel);
}

// Set up the FriendStatusPanel's related button
panel.SetupButton();
}
}
FriendStatusPanel.cs
using AccelByte.Models;
using AccelByte.Core;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;

public class FriendStatusPanel : MonoBehaviour
{
PublicUserData userData;

[SerializeField]
private Image profilePicture;
[SerializeField]
private Image statusDisplay;

[SerializeField]
private Button chatButton;
[SerializeField]
private Button inviteToPartyButton;
[SerializeField]
private Button unfriendButton;
[SerializeField]
private Button blockButton;

[SerializeField]
private Text displayNameText;
[SerializeField]
private Text onlineStatusText;

public void SetOnlineStatus(FriendsStatusNotif notification)
{
switch (notification.availability)
{
case UserStatus.Offline:
onlineStatusText.text = "Offline";
statusDisplay.color = Color.black;
break;
case UserStatus.Online:
onlineStatusText.text = "Online";
statusDisplay.color = Color.green;
break;
case UserStatus.Busy:
onlineStatusText.text = "Busy";
statusDisplay.color = Color.yellow;
break;
case UserStatus.Invisible:
onlineStatusText.text = "Offline";
statusDisplay.color = Color.black;
break;
default:
onlineStatusText.text = $"INVALID UNHANDLED {notification.availability}";
statusDisplay.color = Color.magenta;
break;
}
Debug.Log($"Friend Status for {notification.userID} changed to {notification.availability}");
}

public void Create(PublicUserData publicUserData)
{
userData = publicUserData;
displayNameText.text = userData.displayName;
}

public void SetupButton()
{
unfriendButton.onClick.AddListener(() =>
{
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().Unfriend(userData.userId, result =>
{
if (result.IsError)
{
Debug.Log($"Failed to unfriend a friend: code: {result.Error.Code}, message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully unfriend a friend!");
LobbyHandler.Instance.GetComponent<FriendsManagementHandler>().GetFriends();
}
});
});
}
}
FriendsIncomingPanel.cs
using AccelByte.Core;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;

public class FriendIncomingPanel : MonoBehaviour
{
[SerializeField]
private Image profilePicture;

[SerializeField]
private Text displayNameText;
[SerializeField]
private Button acceptFriendButton;
[SerializeField]
private Button declineFriendButton;
[SerializeField]
private Button blockFriendButton;

private PublicUserData userData;

/// An initialization Function required to be called to populate the UI appropriately
public void Create(PublicUserData userData)
{
// Cache the PublicUserData
this.userData = userData;
// Set the Display Name
displayNameText.text = userData.displayName;
// Set up the button to accept a friend request
acceptFriendButton.onClick.AddListener(() =>
{
// Make the call to accept the friend request using the cached PublicUserData UserID
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().AcceptFriend(this.userData.userId, result =>
{
if (result.IsError)
{
// Display some user feedback if the request is not successful
Debug.Log($"Failed to accept a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Accepted Friend Request");
// Destroy the UI piece if the request was successful to remove it from the list
Destroy(gameObject);
}
});
});

// Set up the button to decline a friend request
declineFriendButton.onClick.AddListener(() =>
{
// Make the call to decline the friend request using the cached PublicUserData UserID
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().RejectFriend(userData.userId, result =>
{
if (result.IsError)
{
// Display some user feedback if the request is not successful
Debug.Log($"Failed to decline a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Declined Friend Request");
// Destroy the UI Piece if the request was successful to remove it from the list
Destroy(gameObject);
}
});
});

// Set up the button to block a player
blockFriendButton.onClick.AddListener(() =>
{
// Make the call to block a player using the cached PublicUserData UserID
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().BlockPlayer(userData.userId, result =>
{
if (result.IsError)
{
// Display user feedback if the request is not successful
Debug.Log($"Failed to block a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Block Friend Request");
// Destroy the UI piece if the request was successful to remove it from the list
Destroy(gameObject);
}
});
});
}
}
FriendsOutgoingPanel.cs
using AccelByte.Core;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;

public class FriendsOutgoingPanel : MonoBehaviour
{
[SerializeField]
private Image profilePicture;

[SerializeField]
private Text displayNameText;
[SerializeField]
private Button cancelRequestButton;

private PublicUserData userData;

public void Create(PublicUserData userData)
{
// Cache the PublicUserData
this.userData = userData;
// Set the Display Name
displayNameText.text = userData.displayName;
// Set up the button to cancel a friend request
cancelRequestButton.onClick.AddListener(() =>
{
// Make the call to reject the friend request using the cached PublicUserData UserID
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().CancelFriendRequest(this.userData.userId, result =>
{
if (result.IsError)
{
// Display feedback if the request is not successful
Debug.Log($"Failed to cancel a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Cancelled Friend Request");
// Destroy the UI piece if the request was successful to remove it from the list
Destroy(gameObject);
}
});
});
}
}
FriendsBlockedPanel.cs
using AccelByte.Core;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;

public class FriendsBlockedPanel : MonoBehaviour
{
[SerializeField]
private Image profilePicture;
[SerializeField]
private Text displayNameText;
[SerializeField]
private Button unblockButton;

private PublicUserData userData;

public void Create(PublicUserData userData)
{
// Cache the PublicUserData
this.userData = userData;
// Set the Display Name
displayNameText.text = userData.displayName;
// Set up the button to cancel a friend request
unblockButton.onClick.AddListener(() =>
{
// Make the call to unblock the given user using the cached PublicUserData UserID
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().UnblockPlayer(this.userData.userId, result =>
{
if (result.IsError)
{
// Display feedback if the request is not successful
Debug.Log($"Failed to unblock a player: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Unblocked a Player");
// Destroy the UI piece if the request was successful to remove it from the list
Destroy(gameObject);
}
});
});
}
}
FriendsAddPanel.cs
using AccelByte.Core;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;

public class FriendsAddPanel : MonoBehaviour
{
[SerializeField]
private Image profilePicture;
[SerializeField]
private Text displayNameText;
[SerializeField]
private Button addFriendButton;

private PublicUserInfo userData;

public void Create(PublicUserInfo userData)
{
// Cache the PublicUserData
this.userData = userData;
// Set the Display Name
displayNameText.text = userData.displayName;
// Set up the button to cancel a friend request
addFriendButton.onClick.AddListener(() =>
{
// Make the call to initiate the friend request
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().RequestFriend(this.userData.userId, result =>
{
if (result.IsError)
{
Debug.Log($"Failed to send a friends request: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Sent Friends Request");
// If we were successful, set the button to be non-intractable and change the text
addFriendButton.interactable = false;
addFriendButton.GetComponentInChildren<Text>().text = "Request Sent";
}
});
});
}
}
MenuHandler.cs
using UnityEngine;
using UnityEngine.UI;

public class MenuHandler : MonoBehaviour
{
public Transform Menu;

public Button FriendsButton;

private bool initialize = false;

public void Create()
{
if (initialize)
{
return;
}

FriendsButton.onClick.AddListener(() =>
{
GetComponent<FriendsManagementHandler>().Setup();
Menu.gameObject.SetActive(false);
GetComponent<FriendsManagementHandler>().FriendsManagementWindow.SetActive(true);
});

initialize = true;
}
}
LobbyHandler.cs
using UnityEngine;
using AccelByte.Api;
using AccelByte.Core;

public class LobbyHandler : MonoBehaviour
{
/// <summary>
/// Private Instance
/// </summary>
static LobbyHandler instance;

/// <summary>
/// The Instance Getter
/// </summary>
public static LobbyHandler Instance => instance;

/// <summary>
/// The Instance Getter
/// </summary>
private Lobby lobby;

[HideInInspector]
public NotificationHandler notificationHandler;

private void Awake()
{
// Check if another Instance is already created, and if so delete this one, otherwise destroy the object
if (instance != null && instance != this)
{
Destroy(this);
return;
}
else
{
instance = this;
}

// Get the the object handler
notificationHandler = gameObject.GetComponent<NotificationHandler>();
}

/// <summary>
/// Connect to the <see cref="Lobby"/> and setup CallBacks
/// </summary>
public void ConnectToLobby()
{
//Get a reference to the instance of the Lobby
lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();

//Init menu handler
GetComponent<MenuHandler>().Create();
GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);

//Connection
lobby.Connected += notificationHandler.OnConnected;
lobby.Disconnecting += notificationHandler.OnDisconnecting;
lobby.Disconnected += notificationHandler.OnDisconnected;
lobby.FriendsStatusChanged += notificationHandler.OnFriendsStatusChanged;
lobby.FriendRequestAccepted += notificationHandler.OnFriendRequestAccepted;
lobby.OnIncomingFriendRequest += notificationHandler.OnIncomingFriendRequest;
lobby.FriendRequestCanceled += notificationHandler.OnFriendRequestCanceled;
lobby.FriendRequestRejected += notificationHandler.OnFriendRequestRejected;
lobby.OnUnfriend += notificationHandler.OnUnfriend;

//Connect to the Lobby
if (!lobby.IsConnected)
{
lobby.Connect();
}
}

public void RemoveLobbyListeners()
{
//Remove delegate from Lobby
//Connection
lobby.Connected -= notificationHandler.OnConnected;
lobby.Disconnecting -= notificationHandler.OnDisconnecting;
lobby.Disconnected -= notificationHandler.OnDisconnected;

lobby.FriendsStatusChanged -= notificationHandler.OnFriendsStatusChanged;
lobby.FriendRequestAccepted -= notificationHandler.OnFriendRequestAccepted;
lobby.OnIncomingFriendRequest -= notificationHandler.OnIncomingFriendRequest;
lobby.FriendRequestCanceled -= notificationHandler.OnFriendRequestCanceled;
lobby.FriendRequestRejected -= notificationHandler.OnFriendRequestRejected;
lobby.OnUnfriend -= notificationHandler.OnUnfriend;
}

public void DisconnectFromLobby()
{
if (lobby != null && lobby.IsConnected)
{
lobby.Disconnect();
}
}

private void OnApplicationQuit()
{
// Attempt to Disconnect from the Lobby when the Game Quits
DisconnectFromLobby();
}
}
NotificationHandler.cs
using UnityEngine;
using AccelByte.Models;
using AccelByte.Core;

public class NotificationHandler : MonoBehaviour
{
#region Notifications

// Collection of connection notifications
#region Connections
/// <summary>
/// Called when lobby is connected
/// </summary>
public void OnConnected()
{
Debug.Log("Lobby Connected");
}

/// <summary>
/// Called when connection is disconnecting
/// </summary>
/// <param name="result"> Contains data of message</param>
public void OnDisconnecting(Result<DisconnectNotif> result)
{
Debug.Log($"Lobby Disconnecting {result.Value.message}");
}

/// <summary>
/// Called when connection is being disconnected
/// </summary>
/// <param name="result"> Contains data of websocket close code</param>
public void OnDisconnected(WsCloseCode result)
{
Debug.Log($"Lobby Disconnected: {result}");
}
#endregion

// Collection of friend notifications
#region Friends
/// <summary>
/// Called when friend status is changed
/// </summary>
/// <param name="result"> Contains data of user id, availability, status, etc</param>
public void OnFriendsStatusChanged(Result<FriendsStatusNotif> result)
{
GetComponent<FriendsManagementHandler>().UpdateFriends(result.Value);
}

/// <summary>
/// Called when friend request is accepted
/// </summary>
/// <param name="result"> Contains data of friend's user id</param>
public void OnFriendRequestAccepted(Result<Friend> result)
{
Debug.Log($"Accepted a Friend Request from user {result.Value.friendId}");
}

/// <summary>
/// Called when there is incomming friend request
/// </summary>
/// <param name="result"> Contains data of friend's user id</param>
public void OnIncomingFriendRequest(Result<Friend> result)
{
Debug.Log($"Received a Friend Request from user {result.Value.friendId}");
}

/// <summary>
/// Called when friend is unfriend
/// </summary>
/// <param name="result"> Contains data of friend's user id</param>
public void OnUnfriend(Result<Friend> result)
{
Debug.Log($"Unfriended User {result.Value.friendId}");
}

/// <summary>
/// Called when friend request is canceled
/// </summary>
/// <param name="result"> Contains data of sender user id</param>
public void OnFriendRequestCanceled(Result<Acquaintance> result)
{
Debug.Log($"Cancelled a Friend Request from user {result.Value.userId}");
}

/// <summary>
/// Called when friend request is rejected
/// </summary>
/// <param name="result"> Contains data of rejector user id</param>
public void OnFriendRequestRejected(Result<Acquaintance> result)
{
Debug.Log($"Rejected a Friend Request from user {result.Value.userId}");
}
#endregion

#endregion
}