Implementing game invitations
Tutorial
·
intermediate
·
+10XP
·
20 mins
·
Unity Technologies

In this tutorial, you will learn how to implement game invitations within your Unity project.
1. Understanding the invite flow
With game lobbies in place, you can now implement invitations powered by Rich Presence in the Discord client. Implementing invitations allows players to invite their friends to join them in their game.
The Discord Social SDK provides multiple ways for players to join each other’s game sessions:
- Direct game invites: Players can send invites directly from within your game to specific friends.
- Discord client invites: Players can send invites through the Discord client application itself, such as in a DM or server.
- Join requests via Rich Presence: Friends can request to join a lobby by clicking on a player’s Rich Presence activity card, and the player can accept or decline in-game or through Discord.
- Manual lobby secret sharing: Players can write a custom lobby secret and share it through Discord messages, voice chat, or other means.
In this tutorial, you’ll implement direct game invites for when both players are already in the game. For information about implementing other invite methods, check out Discord’s Managing Game Invites documentation.
Direct game invite flow
When using direct game invites with the Discord Social SDK, the flow will work like this in your project:
1. Both player A and player B are already in the game.
2. Player A creates a lobby.
3. Player A clicks the invite button next to player B’s name in the friends list.
4. Your game calls SendActivityInvite() to send the invite to player B.
5. Player B’s receives the invite through the callback registered under client.SetActivityInviteCreatedCallback.
6. Player B’s game automatically calls AcceptActivityInvite() to retrieve the lobby secret.
7. Player B joins player A's lobby using the lobby secret.
Note: For the purpose of this tutorial and to learn how to send invites the player being invited to the lobby will automatically accept the invite. In a full game you will want to give the player the choice to accept or reject the invitation.
2. Update FriendUI
To start the invite flow, the player needs access to some UI to invite a friend to their lobby. The FriendUI script in the friends list is the perfect place to connect UI as it already has the RelationshipHandle which has the friend’s ID. To add an invite button to each player in the friends list, follow these instructions:
1. Open the FriendUI script and add the following variable to the top of the class:
[SerializeField]
private Button inviteButton;In order to have the invite button function, you need to add an invite button click listener to your FriendUI script.
2. At the end of the Initialize function, add the following line of code:
inviteButton.onClick.AddListener(OnInviteButtonClick);Next, you’ll need to add the code that allows OnInviteButtonClick to send the invite.
3. Add the following function at the end of the FriendUI class to handle the invite button click:
private void OnInviteButtonClick()
{
if (relationshipHandle != null)
{
DiscordManager discordManager = FindFirstObjectByType<DiscordManager>();
if (discordManager != null)
{
discordManager.SendInvite(relationshipHandle.User().Id());
}
else
{
Debug.LogError("DiscordManager not found!");
}
}
}The OnInviteButtonClick() function finds the DiscordManager GameObject in the scene and calls SendInvite() with the friend's user ID. This keeps the UI logic simple while delegating the actual invite sending to the DiscordManager GameObject.
3. Update DiscordManager
The DiscordManager script needs to handle both sending invites and receiving invites when another player sends one.
1. To handle sending invites, add the following functions to the end of the Discord Manager script:
public void SendInvite(ulong targetUserId)
{
client.SendActivityInvite(targetUserId, "Join my game!", OnSendInvite);
}
private void OnSendInvite(ClientResult result)
{
if (result.Successful())
{
Debug.Log("Successfully sent invite");
}
else
{
Debug.LogError($"Failed to send invite: {result.Error()}");
}
}SendInvite() is called when a player clicks the invite button on a friend in the friends list. It uses client.SendActivityInvite() to send an activity invite to the specified user with a custom message. The friend will receive the invite notification in the Discord client.
For this project, you’ll be automatically accepting the invite from within the game session, but in most scenarios you’ll want to give the user a choice to accept or decline the invitation.
You now need to handle receiving the invite.
2. Open the DiscordManager script and add the following line of code at the end of the Awake() function:
client.SetActivityInviteCreatedCallback(OnActivityInvite);client.SetActivityInviteCreatedCallback() registers a callback that's invoked when the game receives an activity invite. This allows your game to detect and respond to invites sent by other players.
3. To handle receiving and accepting invites, add the following functions at the end of the DiscordManager class:
private void OnActivityInvite(ActivityInvite invite)
{
Debug.Log($"Received invite from user {invite.SenderId()}");
client.AcceptActivityInvite(invite, OnAcceptInvite);
}
private void OnAcceptInvite(ClientResult result, string joinSecret)
{
if (result.Successful())
{
Debug.Log($"Accepted invite with lobby secret: {joinSecret}");
lobbyManager.JoinLobby(joinSecret);
}
else
{
Debug.LogError($"Failed to accept invite: {result.Error()}");
}
}- OnActivityInvite() is called when an invite is received. It automatically calls client.AcceptActivityInvite() to retrieve the lobby secret from the invite.
- OnAcceptInvite() receives the lobby secret and passes it to the LobbyManager script's JoinLobby() function, which automatically joins the friend's lobby.
4. Add invite button to FriendUI
The FriendUI prefab now has the invite logic in place. Next you need to add the invite button to the prefab and set the properties in the Inspector window.
1. In the Project window, open the Assets > Prefabs folders and double-click the FriendUI prefab to open it in prefab editing mode.
2. Right-click the FriendUI GameObject, select UI > Button - TextMeshPro, and name it "InviteButton".
3. In the Hierarchy window, use the foldout (triangle) to expand the InviteButton GameObject and select the Text (TMP) GameObject. In the Inspector window, in the Text component box, enter "Invite".
4. In the Hierarchy window, select the InviteButton GameObject. In the Inspector window, in the Rect Transform component, the Width property to 100, and the Height property to 30. Then select the Anchor presets button, hold Alt (macOS: Option) and select middle right. Then set the Pos X property to -60, and the Pos Y property to 5.
5. Select the FriendUI GameObject.
6. In the Inspector window, drag the InviteButton GameObject into the Invite Button property box on the FriendUI script.
7. Save the prefab and exit prefab editing mode.
The invite button is now set up on each friend in the friends list. When clicked, it’ll send an invite to that friend through the DiscordManager.
5. Run the project
To test game invites, you'll need two Discord accounts and two instances of the game running simultaneously.
Note: You can test game invites by launching the game in the Unity Editor for one account and running a built version for another account, or by running two built versions of the game.
To test game invites, follow these instructions:
1. Create a build of your game.
2. Close the Discord Client and log into Discord through your browser with Discord Account A.
3. Launch the built instance of the game and connect it with Discord Account A.
4. Launch the Discord Client and log in with Discord Account B.
5. In Unity press the Play button and connect to Discord.
Now that the client is open this will open authentication for Discord Account B in the Discord Client.
Note: Make sure Account A and Account B are friends on Discord.
6. In the first instance (Discord Account A), select the Create Lobby button to create a game lobby.
7. In the first instance (Discord Account A), find Discord Account B in the friends list and select the Invite button next to their name.
Account B will then automatically join Account A's lobby.
You should see console logs in the Unity Editor confirming the invite was received, accepted, and that Account B joined the lobby successfully.
6. Check your scripts
Before moving on, take a moment to check that your scripts are correct.
The full FriendUI script:
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using TMPro;
using Discord.Sdk;
public class FriendUI : MonoBehaviour
{
[SerializeField]
private TextMeshProUGUI friendNameText;
[SerializeField]
private TextMeshProUGUI friendStatusText;
[SerializeField]
private Image friendAvatarImage;
[SerializeField]
private Button inviteButton;
private Client client;
public RelationshipHandle relationshipHandle { get; private set; }
public void Initialize(Client client, RelationshipHandle relationshipHandle)
{
this.client = client;
this.relationshipHandle = relationshipHandle;
friendNameText.text = relationshipHandle.User().DisplayName();
friendStatusText.text = relationshipHandle.User().Status().ToString();
StartCoroutine(LoadAvatarFromUrl(relationshipHandle.User().AvatarUrl(UserHandle.AvatarType.Png, UserHandle.AvatarType.Png)));
inviteButton.onClick.AddListener(OnInviteButtonClick);
}
public void UpdateFriend()
{
friendNameText.text = relationshipHandle.User().DisplayName();
friendStatusText.text = relationshipHandle.User().Status().ToString();
}
private IEnumerator LoadAvatarFromUrl(string url)
{
using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(url))
{
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
Texture2D texture = DownloadHandlerTexture.GetContent(request);
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
friendAvatarImage.sprite = sprite;
}
else
{
Debug.LogError($"Failed to load profile image from URL: {url}. Error: {request.error}");
}
}
}
private void OnInviteButtonClick()
{
if (relationshipHandle != null)
{
DiscordManager discordManager = FindFirstObjectByType<DiscordManager>();
if (discordManager != null)
{
discordManager.SendInvite(relationshipHandle.User().Id());
}
else
{
Debug.LogError("DiscordManager not found!");
}
}
}
}The full DiscordManager script:
using UnityEngine;
using UnityEngine.UI;
using Discord.Sdk;
public class DiscordManager : MonoBehaviour
{
[SerializeField]
private ulong applicationId;
[SerializeField]
private RichPresence richPresence;
[SerializeField]
private FriendsList friendsList;
[SerializeField]
private LobbyManager lobbyManager;
private Client client;
private string codeVerifier;
void Awake()
{
client = new Client();
client.AddLogCallback(OnLog, LoggingSeverity.Error);
client.SetStatusChangedCallback(OnStatusChanged);
client.SetUserUpdatedCallback(OnUserUpdated);
client.SetActivityInviteCreatedCallback(OnActivityInvite);
}
private void OnDestroy()
{
client.Disconnect();
}
private void OnLog(string message, LoggingSeverity severity)
{
Debug.Log($"Log: {severity} - {message}");
}
private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode)
{
Debug.Log($"Status changed: {status}");
if (error != Client.Error.None)
{
Debug.LogError($"Error: {error}, code: {errorCode}");
}
if (status == Client.Status.Ready)
{
richPresence.UpdateRichPresence(client);
friendsList.LoadFriends(client);
lobbyManager.InitializeLobbyCreation(client);
}
}
public void StartOAuthFlow()
{
var authorizationVerifier = client.CreateAuthorizationCodeVerifier();
codeVerifier = authorizationVerifier.Verifier();
var args = new AuthorizationArgs();
args.SetClientId(applicationId);
args.SetScopes(Client.GetDefaultCommunicationScopes());
args.SetCodeChallenge(authorizationVerifier.Challenge());
client.Authorize(args, OnAuthorizeResult);
}
private void OnAuthorizeResult(ClientResult result, string code, string redirectUri)
{
if (!result.Successful())
{
Debug.Log($"Authorization result: [{result.Error()}]");
return;
}
GetTokenFromCode(code, redirectUri);
}
private void GetTokenFromCode(string code, string redirectUri)
{
client.GetToken(applicationId, code, codeVerifier, redirectUri, OnGetToken);
}
private void OnGetToken(ClientResult result, string token, string refreshToken, AuthorizationTokenType tokenType, int expiresIn, string scope)
{
if (token == null || token == string.Empty)
{
Debug.Log("Failed to retrieve token");
}
else
{
client.UpdateToken(AuthorizationTokenType.Bearer, token, OnUpdateToken);
}
}
private void OnUpdateToken(ClientResult result)
{
if (result.Successful())
{
client.Connect();
}
else
{
Debug.LogError($"Failed to update token: {result.Error()}");
}
}
private void OnUserUpdated(ulong userId)
{
friendsList.UpdateFriends();
friendsList.SortFriends();
}
public void SendInvite(ulong targetUserId)
{
client.SendActivityInvite(targetUserId, "Join my game!", OnSendInvite);
}
private void OnSendInvite(ClientResult result)
{
if (result.Successful())
{
Debug.Log("Successfully sent invite");
}
else
{
Debug.LogError($"Failed to send invite: {result.Error()}");
}
}
private void OnActivityInvite(ActivityInvite invite)
{
Debug.Log($"Received invite from user {invite.SenderId()}");
client.AcceptActivityInvite(invite, OnAcceptInvite);
}
private void OnAcceptInvite(ClientResult result, string joinSecret)
{
if (result.Successful())
{
Debug.Log($"Accepted invite with lobby secret: {joinSecret}");
lobbyManager.JoinLobby(joinSecret);
}
else
{
Debug.LogError($"Failed to accept invite: {result.Error()}");
}
}
}