Adding private chat UI - Private Chat - (Unreal Engine module)
What's on the Menu
In this tutorial, you will learn how to prepare widgets that you will use to display and send private chats. The widgets are defined in the following classes.
PrivateChatWidget_Starter
- A C++ class you will use to display and send chat messages. This class is defined in the following files:- Header file can be found in
/Source/AccelByteWars/TutorialModules/Social/PrivateChat/UI/PrivateChatWidget_Starter.h
. - CPP file can be found in
/Source/AccelByteWars/TutorialModules/Social/PrivateChat/UI/PrivateChatWidget_Starter.cpp
. - Blueprint widget can be found in
/Content/TutorialModules/Social/PrivateChat/UI/W_PrivateChat_Starter.uasset
.
- Header file can be found in
ChatWidget
- A C++ class used withinPrivateChatWidget_Starter
to display all chat entries and take user input as the chat message to be sent. This class is defined in the following files:- Header file can be found in
/Source/AccelByteWars/TutorialModules/Social/ChatEssentials/UI/ChatWidget.h
. - CPP file can be found in
/Source/AccelByteWars/TutorialModules/Social/ChatEssentials/UI/ChatWidget.cpp
. - Blueprint widget can be found in
/Content/TutorialModules/Social/ChatEssentials/UI/W_Chat.uasset
.
- Header file can be found in
ChatWidgetEntry
- A C++ class used withinChatWidget
to display individual chat message entries. This class is defined in the following files:- Header file can be found in
/Source/AccelByteWars/TutorialModules/Social/ChatEssentials/UI/ChatWidgetEntry.h
. - CPP file can be found in
/Source/AccelByteWars/TutorialModules/Social/ChatEssentials/UI/ChatWidgetEntry.cpp
. - Blueprint widget can be found in
/Content/TutorialModules/Social/ChatEssentials/UI/W_ChatEntry.uasset
.
- Header file can be found in
Now, take a look at more details on how these widgets are constructed.
PrivateChat widget
This widget mainly consists of W_Chat
, which handles the input and viewing of the chat. This is where you will manipulate W_Chat
to show and send the actual chat.
The widget component is declared in the Header file:
protected:
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional, BlueprintProtected = true, AllowPrivateAccess = true))
UChatWidget* W_Chat;
ChatWidget
This widget contains an AccelByteWarsWidgetSwitcher
, which is a custom Widget Switcher that has a predefined state, an editable text as the chat input, a list view to display chat message entries, and a button to send the message.
The widget components are declared in the Header file:
private:
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional, BlueprintProtected = true, AllowPrivateAccess = true))
UAccelByteWarsWidgetSwitcher* Ws_ChatMessage;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional, BlueprintProtected = true, AllowPrivateAccess = true))
UEditableText* Edt_ChatMessage;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional, BlueprintProtected = true, AllowPrivateAccess = true))
UListView* Lv_ChatMessage;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional, BlueprintProtected = true, AllowPrivateAccess = true))
UCommonButtonBase* Btn_Send;
This widget has several functions and a delegate so that other widgets can control what it shows and take player's input from it. Those functions and the delegate are declared in the Header file:
public:
// ...
void AppendChatMessage(UChatData* ChatData) const;
// ...
void ClearChatMessages() const;
// ...
void ClearInput() const;
// ...
void SetWidgetState(const EAccelByteWarsWidgetSwitcherState State) const;
// ...
int32 GetMaxChatHistory() const { return MaxChatHistory; }
// ...
FChatOnSubmit OnSubmitDelegates;
AppendChatMessage
: Add a chat entry.ClearChatMessages
: Clear the current displayed chat entries.ClearInput
: Clear the input editable text.SetWidgetState
: Set the widget state to loading, error, empty, or not empty, which will show the chat entries and input text.GetMaxChatHistory
: Retrieve the predefinedMaxChatHistory
history.OnSubmitDelegates
: A delegate that will be called when the player presses enter when typing a message or clicking the Send button.
Also take note of these variables:
protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
int32 MaxChatHistory = 10000;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
float SendChatCooldown = 1.0f;
// Based on the default value of chat character limit on Admin Portal
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
int32 MaxMessageLength = 500;
MaxChatHistory
: You will use this later to limit how many chats should be retrieved from the backend.SendChatCooldown
: Controls how long the editable text and send button should be disabled right after the player sends a chat. This prevents message spamming from the game client.MaxMessageLength
: Controls the maximum length of the message that can be sent. This can also be configured on the backend side, which will be covered later.
ChatEntry widget
This widget consists of two text blocks: one for displaying a sender name and one for displaying the message. You will not interact with this widget directly; instead, you will interact with this widget through ChatWidget.
The components above are declared in the Header file:
protected:
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional, BlueprintProtected = true, AllowPrivateAccess = true))
UTextBlock* Tb_Sender;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional, BlueprintProtected = true, AllowPrivateAccess = true))
UTextBlock* Tb_Message;
Ready the UI
In this section, you will learn how to prepare the widgets so you can follow along with the tutorial.
Open the
PrivateChatWidget_Starter
class header file. Then, declare the following functions.protected:
// ...
void AppendChatMessage(const FChatMessage& Message) const;
UFUNCTION()
void SendPrivateChatMessage(const FText& MessageText);
void OnSendPrivateChatComplete(FString UserId, FString MsgBody, FString RoomId, bool bWasSuccessful);
void OnPrivateChatMessageReceived(const FUniqueNetId& UserId, const TSharedRef<FChatMessage>& Message);
void GetLastPrivateChatMessages() const;Open the
PrivateChatWidget_Starter
class CPP file and define theAppendChatMessage
function. This function is used to display the chat message from the data provided by the Online Subsystem (OSS), which contains the message and sender information.void UPrivateChatWidget_Starter::AppendChatMessage(const FChatMessage& Message) const
{
if (!PrivateChatSubsystem)
{
UE_LOG_PRIVATECHAT(Warning, TEXT("Cannot append a private chat message to display. Private Chat subsystem is not valid."));
return;
}
if (!ensure(GetOwningPlayer()))
{
UE_LOG_PRIVATECHAT(Warning, TEXT("Cannot append a private chat message to display. PlayerController is not valid."));
return;
}
const ULocalPlayer* LocalPlayer = GetOwningPlayer()->GetLocalPlayer();
if (!ensure(LocalPlayer))
{
UE_LOG_PRIVATECHAT(Warning, TEXT("Cannot append a private chat message to display. LocalPlayer is not valid."));
return;
}
const FUniqueNetIdAccelByteUserRef SenderABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(Message.GetUserId());
if (!SenderABId.Get().IsValid())
{
UE_LOG_PRIVATECHAT(Warning, TEXT("Cannot append a private chat message to display. Sender User Id is invalid."));
return;
}
// Construct chat message.
UChatData* ChatData = NewObject<UChatData>();
ChatData->Message = Message.GetBody();
ChatData->bIsSenderLocal = PrivateChatSubsystem->IsMessageFromLocalUser(LocalPlayer->GetPreferredUniqueNetId().GetUniqueNetId(), Message);
// If the sender doesn't have display name, then use the default display name.
ChatData->Sender = Message.GetNickname();
if (ChatData->Sender.IsEmpty() || SenderABId.Get().GetAccelByteId().Equals(ChatData->Sender))
{
ChatData->Sender = UTutorialModuleOnlineUtility::GetUserDefaultDisplayName(Message.GetUserId().Get());
}
// Display chat message.
W_Chat->AppendChatMessage(ChatData);
}Still in the CPP file, define the
SendPrivateChatMessage
function. You will use this function to actually send the message. For now, add this code as a safeguard:void UPrivateChatWidget_Starter::SendPrivateChatMessage(const FText& MessageText)
{
if (!PrivateChatSubsystem)
{
UE_LOG_PRIVATECHAT(Warning, TEXT("Cannot send private chat message. Private Chat subsystem is not valid."));
return;
}
if (!ensure(GetOwningPlayer()))
{
UE_LOG_PRIVATECHAT(Warning, TEXT("Cannot send private chat message. PlayerController is not valid."));
return;
}
const ULocalPlayer* LocalPlayer = GetOwningPlayer()->GetLocalPlayer();
if (!ensure(LocalPlayer))
{
UE_LOG_PRIVATECHAT(Warning, TEXT("Cannot send private chat message. LocalPlayer is not valid."));
return;
}
// ...
}Next, define the
OnSendPrivateChatComplete
function. You will use this function as a callback to display the chat message when the game receives a response after sending a chat. For now, add the display chat message function.void UPrivateChatWidget_Starter::OnSendPrivateChatComplete(FString UserId, FString MsgBody, FString RoomId, bool bWasSuccessful)
{
// Abort and push a notification if failed.
if (!bWasSuccessful)
{
if (PromptSubsystem)
{
PromptSubsystem->PushNotification(SEND_CHAT_FAILED_MESSAGE);
}
return;
}
// Display the chat if success.
UChatData* ChatData = NewObject<UChatData>();
ChatData->Message = MsgBody;
ChatData->bIsSenderLocal = true;
W_Chat->AppendChatMessage(ChatData);
}Define the
OnPrivateChatMessageReceived
function, which is similar to the previous function, to display the chat message. You will use this function to display messages received.void UPrivateChatWidget_Starter::OnPrivateChatMessageReceived(const FUniqueNetId& UserId, const TSharedRef<FChatMessage>& Message)
{
// Show the received chat message.
AppendChatMessage(Message.Get());
}Lastly, add the
GetLastPrivateChatMessages
function. You will use this function to retrieve the message that the player received when they are not in the chat menu. For now, add this safeguard code:void UPrivateChatWidget_Starter::GetLastPrivateChatMessages() const
{
if (!PrivateChatSubsystem)
{
UE_LOG_PRIVATECHAT(Warning, TEXT("Cannot get last private chat messages. Private Chat subsystem is not valid."));
return;
}
if (!ensure(GetOwningPlayer()))
{
UE_LOG_PRIVATECHAT(Warning, TEXT("Cannot get last private chat messages. PlayerController is not valid."));
return;
}
const ULocalPlayer* LocalPlayer = GetOwningPlayer()->GetLocalPlayer();
if (!ensure(LocalPlayer))
{
UE_LOG_PRIVATECHAT(Warning, TEXT("Cannot get last private chat messages. LocalPlayer is not valid."));
return;
}
// ...
}Build the project and open it in the Unreal Editor.
In the editor, from the content browser, navigate to
/Content/TutorialModules/Social/PrivateChat/
and open theDA_PrivateChat
data asset. EnableIs Starter Mode Active
and save the data asset. This will activate the widgets that you just prepared.Click Play in the editor. Go to Social > Friends > [Click on one of the friend entry] > Chat. The UI will appear if implemented correctly.
You need to have a friend in the friend list to test this menu. Refer to the Add friends module for how to do so.
Resources
- The files used in this tutorial section are available in the ByteWars GitHub repository.
- AccelByteWars/Content/TutorialModules/Social/PrivateChat/UI/W_PrivateChat_Starter.uasset
- AccelByteWars/Source/AccelByteWars/TutorialModules/Social/PrivateChat/UI/PrivateChatWidget_Starter.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Social/PrivateChat/UI/PrivateChatWidget_Starter.cpp
- AccelByteWars/Content/TutorialModules/Social/ChatEssentials/UI/W_ChatEntry.uasset
- AccelByteWars/Source/AccelByteWars/TutorialModules/Social/ChatEssentials/UI/ChatEntryWidget.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Social/ChatEssentials/UI/ChatEntryWidget.cpp