Direct messaging implementation
Tutorial
·
intermediate
·
+10XP
·
20 mins
·
Unity Technologies

In this tutorial, you will learn how to set up direct messaging within your Unity project.
1. Set up messaging code
Giving your players a way to interact with their friends will keep them in your game longer and give players a way to interact. The Discord Social SDK lets you use Discord’s text chat in your game. Discord’s text chat works across your game and the Discord client. This means that a player in your game can chat with their friends without having to leave the game, and the messages persist in their direct messages on the Discord client. It doesn’t matter if their friends are in game or on Discord, chat will work across both.
In order to add messaging to your game, you’ll need to add new UI and code.
To set up messaging in your Unity project, follow these instructions:
1. In the Unity Editor, in the Project window, open the Assets folder.
2. Right-click and select Create > MonoBehavior Script and name it “MessageManager”.
Note: Depending on your Unity version, the path to create a MonoBehaviour script might be Create > C# Script or some other variation.
3. Open the MessageManager script and add the following lines of code:
using Discord.Sdk;
using UnityEngine;
using TMPro;
public class MessageManager : MonoBehaviour
{
[SerializeField]
private GameObject messagePanel;
private Client client;
private ulong currentUserId;
void Start()
{
messagePanel.SetActive(false);
}
public void InitializeMessageManager(Client client)
{
this.client = client;
}
public void OpenMessageUI(ulong userId)
{
currentUserId = userId;
messagePanel.SetActive(true);
}
}This code is the base of what is needed to show the UI for sending messages.
- messagePanel is the UI that all messaging will happen through and messageUIPrefab is the text that will be shown for each message. You’ll create this UI and attach it to this script in the Inspector window in the next part of this section.
- When the MessageManager script initializes, it hides the messagePanel because you don’t have anyone to message yet.
- The OpenMessageUI function is used to open the messaging panel when the player wants to message one of their friends.
Next you’ll add the code needed to send a message.
4. In the MessageManager class, add the following functions to the bottom of the class:
private void SendDirectMessage(string message)
{
if(message == string.Empty)
{
return;
}
client.SendUserMessage(currentUserId, message, OnMessageSent);
}
private void OnMessageSent(ClientResult result, ulong messageId)
{
if (result.Successful())
{
Debug.Log("Message sent successfully.");
}
else
{
Debug.LogError("Failed to send message: " + result.Error());
}
}This code allows the player to send a message to the currently selected friend.
- The MessageManager class stores the friend the player is speaking to in currentUserId.
- client.SendUserMessage() sends a message directly to another Discord user and will trigger a callback with the result of that message.
Next you’ll need to add a UI for the player to enter text into and the functionality to press the Enter key to send the message. Unity’s TMP_InputField is perfect for this.
5. In the MessageManager script, at the top of the file under the serialized private variables, add the following lines of code:
[SerializeField]
private TMP_InputField messageInputField;6. At the end of the Start() method, add the following line of code:
messageInputField.onEndEdit.AddListener(SendDirectMessage);7. At the end of the SendDirectMessage() method, add the following line of code:
messageInputField.text = string.Empty;Now the game will call SendDirectMessage() when the player enters text in the text box and presses the Enter key. The text written will then be cleared out so the player can write a new message.
Now that you have a way for the player to open a direct message with a friend and send a message, you need a way to receive messages and display them.
8. In the MessageManager script, at the top of the file under the serialized private variables, add the following lines of code:
[SerializeField]
private GameObject messageUIPrefab;
[SerializeField]
private Transform messageScrollContainer;9. At the end of the OpenMessageUI() method, add the following lines of code:
for (int i = messageScrollContainer.childCount - 1; i >= 0; i--)
{
Destroy(messageScrollContainer.GetChild(i).gameObject);
}10. At the end of the MessageManager class, add the following function:
public void MessageReceived(ulong messageId)
{
MessageHandle message = client.GetMessageHandle(messageId);
if (message != null && (message.Author().Id() == currentUserId || message.Author().Id() == client.GetCurrentUserV2().Id()))
{
GameObject messageUI = Instantiate(messageUIPrefab, messageScrollContainer);
TextMeshProUGUI messageUIText = messageUI.GetComponent<TextMeshProUGUI>();
messageUIText.text = $"{message.Author().DisplayName()}: {message.Content()}";
}
}- The messageScrollContainer is a scrollable area in the UI that displays messages using the messageUIPrefab. When the player chooses a new friend to message, the code added to OpenMessageUI() will clear out all the previous messages.
- MessageReceived() is where the code instantiates UI for each message as they’re received. The Discord Social SDK has a callback whenever the player receives a message that you’ll connect to MessageReceived() later in this section, providing an ID for the message. In order to get the message content and author to display it we will call client.GetMessageHandle().
Now that you have the code to receive messages, you need to connect the Social SDK in the DiscordManager.
11. In the DiscordManager script, at the top of the file under the serialized private variables, add the following lines of code:
[SerializeField]
private MessageManager messageManager;12. At the end of the Awake() method, add the following line of code:
client.SetMessageCreatedCallback(MessageCreated);13. At the end of the OnStatusChanged() method, inside the if (status == Client.Status.Ready) code block, add the following line of code:
messageManager.InitializeMessageManager(client);14. At the end of the DiscordManager class, add the following function:
private void MessageCreated(ulong messageId)
{
messageManager.MessageReceived(messageId);
}This code connects the Social SDK to the MessageManager and adds a callback to client.SetMessageCreatedCallback(), which calls MessageCreated().
In order to let players send and receive messages, you need to create a way for them to open a direct message with a friend from their friends list.
2. Set up friend UI code
Before you create the messaging UI, you need one more piece of code to open a direct message with a friend from the friends list.
To add the functionality to open direct messages, follow these instructions:
1. In the FriendUI script, at the top of the file under the serialized private variables, add the following lines of code:
[SerializeField]
private Button messageButton;2. At the end of the Initialize() method, add the following line of code:
messageButton.onClick.AddListener(OnMessageButtonClick);3. At the end of the FriendUI class, add the following function:
private void OnMessageButtonClick()
{
if (relationshipHandle != null)
{
MessageManager messageManager = FindFirstObjectByType<MessageManager>();
if (messageManager != null)
{
messageManager.OpenMessageUI(relationshipHandle.User().Id());
}
}
}With all the code in place to start a direct message, you can now create the UI and connect everything.
3. Set up the messaging UI in the scene
To set up the messaging UI the scene, follow these instructions:
1. In the Project window, go to Assets > Prefabs and double-click the FriendUI prefab to open it in prefab editing mode.
2. In the Hierarchy window, right-click the FriendUI GameObject, select Create > UI > Button - TextMeshPro, then rename the Button GameObject “MessageButton”.
3. In the Hierarchy window, use the foldout (triangle) to expand the MessageButton GameObject.
4. In the Inspector window, in the TextMeshPro - Text (UI) component, replace “Button” with “Message” in the text box.
5. In the Hierarchy window, select the MessageButton GameObject.
6. In the Inspector window, in the Rect Transform component, set the Width property to 140. Then select the Anchor presets button, hold Alt (macOS: Option) and select middle right. Then set the Pos X property to -80, and the Pos Y property to -28.
7. In the Hierarchy window, select the FriendUI GameObject.
8. Click and drag the MessageButton GameObject into the FriendUI script component’s Message Button property box.
9. Exit prefab editing mode.
10. Right-click in the Hierarchy window and select Create Empty, then rename the empty GameObject “MessageManager”.
11. In the Inspector window, select the Add Component button and search for and add the Message Manager script component.
12. In the Hierarchy window, select the DiscordManager GameObject.
13. Click and drag the MessageManager GameObject into the Discord Manager script component’s Message Manager property box.
14. In the Hierarchy window, right-click the Canvas GameObject, select UI > Panel, and rename the Panel GameObject “MessagePanel”.
15. In the Inspector window, in the Rect Transform component, set the Width property to 600 and the Height property to 400. Then select the Anchor presets button, hold Alt (macOS: Option) and select bottom center.
16. In the Hierarchy window, right-click the MessagePanel GameObject and select UI > Input Field - TextMeshPro.
17. In the Inspector window, in the Rect Transform component, set the Height property to 60. Then select the Anchor presets button, hold Alt (macOS: Option) and select bottom stretch.
18. In the Hierarchy window, use the foldout (triangle) to expand the InputField (TMP) > Text Area GameObjects, then select the Placeholder GameObject.
19. In the Inspector window, in the TextMeshPro - Text (UI) component, delete “Enter text…” in the text box.
20. In the Hierarchy window, right-click the MessagePanel GameObject, select UI > Scroll view.
21. In the Inspector window, in the Scroll Rect component, open the Movement Type dropdown and select Clamped, disable the Horizontal property, and open the Visibility property dropdown in the Horizontal Scrollbar section and select Permanent.
22. In the Rect Transform component, set the Height property to 340. Then select the Anchor presets button, hold Alt (macOS: Option) and select top stretch.
23. In the Hierarchy window, use the foldout (triangle) to expand the Scroll View > Viewport GameObjects, then select the Content GameObject.
24. In the Inspector window, select the Add Component button, and search for and add the Vertical Layout Group component.
25. Set the Spacing property to 10, enable the Control Child Size Width and Height properties, and disable the Child Force Expand Height property.
26. Select the Add Component button, search for and add the Content Size Fitter component, open the Vertical Fit property dropdown, and select Preferred Size.
27. In the Hierarchy window, right-click the Content GameObject, select UI > Text - TextMeshPro, and rename the Text (TMP) GameObject “MessageUI”.
28. In the Inspector window, in the TextMeshPro - Text (UI) component, set the Font Size property to 22.
29. Click and drag the MessageUI GameObject from the Hierarchy window into the Prefabs folder to make it a prefab, then delete the MessageUI GameObject from the Hierarchy window.
30. In the Hierarchy window, select the MessageManager GameObject, then click and drag the MessagePanel GameObject from the Hierarchy window into the Message Manager script component’s Message Panel property box.
31. Click and drag the MessageUI prefab from the Prefabs folder into the Message Manager script component’s Message UI Prefab property box.
32. Click and drag the InputField (TMP) GameObject from the Hierarchy window into the Message Manager script component’s Message Input Field property box.
33. Click and drag the Content GameObject from the Hierarchy window into the Message Manager script component’s Message Scroll Container property box.
4. Run the project
To test messaging in the project, follow these instructions:
1. Select the Play button to enter Play mode.
2. In the Game view, select the Connect to Discord button.
3. In your browser, scroll to the bottom of the dialog and select the Authorize button.
4. Minimize the browser window and return to the Unity Editor.
5. In the Game view, locate Account B that you’ll test messaging with in the friends list and select the Message button.
6. In the message box, enter “Hello” and press Enter.
You’ll see the “Hello” message appear in the message manager.
7. In the Discord App or webpage, open the Direct Messages on the account you sent the test message to.
You’ll see the message “Hello” sent from Account A.
8. In the Discord App or webpage message box, enter “Hey” and press Enter.
9. Switch back to the Game view in the Unity Editor.
You’ll see the message “Hey” sent from Account B.
5. Check your scripts
Before moving on, take a moment to check that your scripts are correct.
The full MessageManager script:
using Discord.Sdk;
using UnityEngine;
using TMPro;
public class MessageManager : MonoBehaviour
{
[SerializeField]
private GameObject messagePanel;
[SerializeField]
private GameObject messageUIPrefab;
[SerializeField]
private TMP_InputField messageInputField;
[SerializeField]
private Transform messageScrollContainer;
private Client client;
private ulong currentUserId;
void Start()
{
messagePanel.SetActive(false);
messageInputField.onEndEdit.AddListener(SendDirectMessage);
}
public void InitializeMessageManager(Client client)
{
this.client = client;
}
public void OpenMessageUI(ulong userId)
{
currentUserId = userId;
messagePanel.SetActive(true);
for (int i = messageScrollContainer.childCount - 1; i >= 0; i--)
{
Destroy(messageScrollContainer.GetChild(i).gameObject);
}
}
private void SendDirectMessage(string message)
{
if(message == string.Empty)
{
return;
}
client.SendUserMessage(currentUserId, message, OnMessageSent);
messageInputField.text = string.Empty;
}
private void OnMessageSent(ClientResult result, ulong messageId)
{
if (result.Successful())
{
Debug.Log("Message sent successfully.");
}
else
{
Debug.LogError("Failed to send message: " + result.Error());
}
}
public void MessageReceived(ulong messageId)
{
MessageHandle message = client.GetMessageHandle(messageId);
if (message != null && (message.Author().Id() == currentUserId || message.Author().Id() == client.GetCurrentUserV2().Id()))
{
GameObject messageUI = Instantiate(messageUIPrefab, messageScrollContainer);
TextMeshProUGUI messageUIText = messageUI.GetComponent<TextMeshProUGUI>();
messageUIText.text = $"{message.Author().DisplayName()}: {message.Content()}";
}
}
}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;
[SerializeField]
private Button messageButton;
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);
messageButton.onClick.AddListener(OnMessageButtonClick);
}
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!");
}
}
}
private void OnMessageButtonClick()
{
if (relationshipHandle != null)
{
MessageManager messageManager = FindFirstObjectByType<MessageManager>();
if (messageManager != null)
{
messageManager.OpenMessageUI(relationshipHandle.User().Id());
}
}
}
}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;
[SerializeField]
private MessageManager messageManager;
private Client client;
private string codeVerifier;
void Awake()
{
client = new Client();
client.AddLogCallback(OnLog, LoggingSeverity.Verbose);
client.SetStatusChangedCallback(OnStatusChanged);
client.SetUserUpdatedCallback(OnUserUpdated);
client.RegisterLaunchCommand(applicationId, string.Empty);
client.SetActivityInviteCreatedCallback(OnActivityInvite);
client.SetMessageCreatedCallback(MessageCreated);
}
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);
messageManager.InitializeMessageManager(client);
}
}
private void OnUserUpdated(ulong userId)
{
friendsList.UpdateFriends();
friendsList.SortFriends();
}
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()}");
}
}
public void SendInvite(ulong targetUserId)
{
client.SendActivityInvite(targetUserId, "Join my game!", OnSendInvite);
}
public 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 MessageCreated(ulong messageId)
{
messageManager.MessageReceived(messageId);
}
private void OnAcceptInvite(ClientResult result, string joinSecret)
{
if (result.Successful())
{
Debug.Log($"Successfully accepted invite with join secret: {joinSecret}");
lobbyManager.JoinLobby(joinSecret);
}
}
}6. Wrap up and next steps
Now that you understand the core concepts of the Discord Social SDK, there’s even more you can explore to bring Discord’s social features to your game! Below are some ideas on what you can implement from here:
- Voice Chat: Voice calls are a core feature of the Discord Social SDK that enable real-time voice communication between players in your game within lobbies.
- Linked Channels: Linked Channels let players connect in-game lobbies with Discord text channels, enabling chat between your game and Discord servers.
Discord also has a prebuilt sample project with prefabs that cover everything in this course. The sample is built with best design practices in place and can be applied to any commercial game.
For help with the Discord Social SDK, there’s a #social-sdk-dev-help channel in the Discord Developers server
Resources
Tutorial complete!
+5 XP
You gained 5 XP points towards Systems Design
+5 XP
You gained 5 XP points towards Programming
Next up in The Discord Social SDK for Unity