Contents
- Preface
- Setting Up
- Editing ExtraDataInitializer
-
Editing BubbleChat
4.1. Looking at where our chat is received
4.2. Editing OnPlayerChatMessage
4.3. Editing createPlayerChatLine and createChatLine
4.4. Going through CreateChatLineRender - Postface
Preface
Hey there, it’s been a while. I’m coming back to tutorial writing! With my return to tutorial writing, I’m continuing my talks on the Lua Chat System modification and extensibility. My hope is that I can write many tutorials about using the system so you can do cool things with chat!
If you perform a quick search on the forum, you’ll find that there are actually a lot of developers around here interested in knowing how to change the colours of a bubble chat. There are many different approaches to this problem as well as available resources that you can use. Here, I’d like to teach you a best-case scenario method of doing this using native APIs.
With this tutorial, I hope that I can show you how exactly you can modify the BubbleChat LocalScript so that you can modify bubble chat colours. You will need to know some things prior to reading this tutorial, such as how to enable bubble chat and how to fork. These are very important and without them, this tutorial won’t mean much for you. A bit of API knowledge comes handy as well: we focus in on ChatSpeaker’s ExtraData capacity and ChatMessage which contains chat metadata. The system will pass on the ChatSpeaker’s ExtraData to a ChatMessage or it can be directly modified!
PS: By bubble chat colours, I mean both the text and fill colouring!
This tutorial is designed to work with LCS version 0.8.2018.05.16, which is the latest version since the posting of this thread. Any part of this can quickly go out of date.
Setting Up
The two things you will need to fork are the BubbleChat LocalScript and the ExtraDataInitializer. I will briefly explain why we need each of these pieces before getting into how to set them up.
LocalScript BubbleChat
The BubbleChat LocalScript is responsible for rendering all chats as bubbles above a sender’s head if bubble chat is enabled. This tutorial does not cover how to enable bubble chat but links a resource that can do this while avoiding forking as we don’t need other settings majorly changed.
ModuleScript ChatModules/ExtraDataInitializer
The ExtraDataInitializer script is, as the name suggests, what sets up ChatSpeaker ExtraData. We use this module to control defaults: in other cases, developers might use this to attach their own custom initial data (e.g. staff or VIP tags) as well. We’ll need this to set up some default colours for our script to fall back on, otherwise we’d have to do extra work to account for missing defaults.
Now that you know what we need, we can get right into forking. The best way to do this is to start a new place file (Ctrl + N) and start a play session (F5). All the components of the Lua Chat System will be added to the Chat service, which we can take out. All these items should be present:
What we want is the BubbleChat LocalScript and the ExtraDataInitializer ModuleScript which resides in ChatModules. Copy these two items and then exit your play session (Shift + F5). When you come back to the Studio view, paste these two items in the Chat service. Your view should look like this if done correctly:
We’re not done yet though. We only want ExtraDataInitializer forked, but copying ChatModules will copy any other ChatModules as well. Open up the folder so you can see all the children. You will want to delete everything except InsertDefaultModules and ExtraDataInitializer. The InsertDefaultModules object will tell Roblox to fill for missing items that we have not forked, completing the installation.
When done, you should be left with just three objects.
We can now get into the code work that will help us be able to change bubble chat colours using the ExtraData system. Please note: you can perform this in any way you want that makes you comfortable, but will need to make changes based on what you do. For best practice, I will be using a table which enforces the need to change both text and colour and makes the grouping clear.
Editing ExtraDataInitializer
This is the easy part, so we can handle this first. If you’re working with a fresh installation of the Lua Chat System, no colour settings for bubbles should be present. All you’re going to need to do is add a line of ExtraData that defaults our text and fill colour values.
We will be adding our code to the onNewSpeaker function (line 129) as that is where the ExtraDataInitializer begins attaching initial data to our ChatSpeakers. I prefer to do this right above the Tags portion since it looks consistent with the name and window chat colours set beforehand.
if not speaker:GetExtraData("BubbleChatColors") then
speaker:SetExtraData("BubbleChatColors", {
Text = Color3.new(0, 0, 0),
Fill = Color3.new(1, 1, 1)
})
end
If you wanted to do your own setups for ExtraData as well, that’s fine to roll with as well! The important part that needs to get across here is that in the end, no matter what method you pick, you will need to add keys to ExtraData via SetExtraData so that we can pull them later.
For the sake of confirming that what you’ve done works, I recommend using different colour values so that your chat colours are actually different when you start typing. When you’ve confirmed it works, you can change them back to the black text on white fill schema or apply your own, up to you! I will be using Color3.new(1, 1, 1)
for text and Color3.new(0.3, 0, 0)
for fill.
Editing BubbleChat
Here is where the tricky part comes into play. I got lost myself when I initially tried fiddling around with the script before I finally got around to making coloured bubble chats. It is easier if you work in terms of the logical flow rather than editing from top down so you don’t get lost as quickly.
Note: BubbleChat renders two types of chats - player chats and calls from Chat.Chat. We will only be working with text and fill colours from player chats. Calls from Chat.Chat apply the ChatColor Enum to change the ribbon colour. As these calls do not adhere to the ChatSpeaker API, it is much more difficult to work with them and you need to do the heavy lifting on your own. I may write a separate tutorial on changing bubble chat colours for NPCs so you can safely use Chat.Chat with different colours.
Looking at where our chat is received
Start by scrolling to the bottom. You will find two OnClientEvent connections: OnNewMessage and OnMessageDoneFiltering. These functions are identical but function differently. I haven’t looked into these too deeply, but I believe OnMessageDoneFiltering is for the client while OnNewMessage is for everyone else. To remain on the same page, we will edit both the same way.
We’re lucky that the Lua Chat System is very extensible and mod-friendly. A ChatMessage object is sent to the client, but the BubbleChat script only uses the Message key of it. So that we can access our ExtraData later, we’re going to instead pass the whole ChatMessage around.
At the bottom of each function of the remotes, you will see this line:
this:OnPlayerChatMessage(sender, messageData.Message, nil)
Erase the message part. We will be sending the entire object rather than just the message string to our relevant functions. We can later pull out the message string and colours for our own use.
this:OnPlayerChatMessage(sender, messageData, nil)
We’ve now handled the receiver. When a player chats, the ChatMessage is sent through a RemoteEvent. The BubbleChat LocalScript listens for this so it can catch the message and then process it for rendering above player’s heads. Let’s move down the logic path and jump to the OnPlayerChatMessage function at line 557.
Editing OnPlayerChatMessage
Next we have OnPlayerChatMessage, which is where our ChatMessage object was passed to. We’re going to do a little bit of modifying here. The function is currently expecting that we’ll be giving it a string, but we’ve changed it so it receives a table. We now need to account for this.
Start by making it clear that OnPlayerChatMessage should expect a messageData table by changing the parameter message
to messageData
. This isn’t a requirement and you can continue working with the message
parameter, but I reserve that for the actual message I take out below and want to be very clear about what my function is doing.
The next few lines require a message to perform input sanitsation on, so we’re going to go ahead and extract our variables right away so they’re ready to go. The other keys of ChatMessage after this are fairly pointless and we end up dropping the object here after pulling our needed keys.
On an unrelated note, if you're interested in sanitisation. It's not related to the thread, but you may have had that question after seeing me mention the word.
Here’s a quick rundown on sanitisation and a related term, validation, in case there are any questions on that. They aren’t important to know in the context of this tutorial, so completely optional if you want to read on here. It’s good knowledge to have on hand but again, irrelevant.
Input sanitisation
The act of input sanitisation is accepting user input but only after you’ve performed scrubbing on the information. For example, say a user sends a 400 character message but our server limits messages to 200 characters. Instead of rejecting this message, we truncate (drop) all characters after the 200 character mark, then accept that through.
Input validation
The act of input validation is accepting only correctly formed data according to your formats and rejecting anything that is outside of these parameters (malformed data). For example again, our chat scenario. If a user sends a 400 character message when the limit is 200, we instead reject the message from being sent.
So the difference?
Sanitisation fits sent data to your format before sending it. Validation will reject it if it is incorrect. If you have used a guard clause for remote security or otherwise rejected the script from continuing functions if input fails a check, you’ve done validation. If you have an input area that imposes a maximum amount of characters allowed, you’ve done sanitisation.
Awesome! Let’s get back to the tutorial. Set up the following code just above the message sanitisation:
local message = messageData.Message
local bubbleColors = messageData.ExtraData.BubbleChatColors
A note on the second variable: ChatSpeaker also passes its ExtraData to the ChatMessage object. This is useful if developers want to intercept chat metadata without using the speaker’s defaults (e.g. certain prefixes trigger the chat to be a different colour while a player’s chat is normally white), so we’ll want to access ExtraData in order to get our bubble chat colours.
And now for a picture of what your code should look like. As always, I’ve highlighted the new chunks we’ve added in green and our extra guiding information in purple - in this case, indicating where the input sanitisation happens. It’s important that the above two variables come before that call.
Now that we’ve properly tailored the OnPlayerChatMessage function to handle a ChatMessage table instead of a message, we’re good to go. We no longer care about the message from this point on and want to focus on changing the colours. Let’s move on.
The next part we’re going to want to do is look at createPlayerChatLine. This function is responsible for handling metadata for the chat message. Naturally, we are also going to add our colours here so that when the chat line is sent around, the colours can be pulled from the metadata.
We’re going to send an extra argument here, the bubbleColors variable we defined earlier. I prefer to have this sent after the sanitised message is sent because it makes sense this way when you’re reading your code: first you deliver the message and then the colours. Parameter order matters here, so if you want to to make it the last parameter then make sure you’re keeping track of that.
This line:
local line = createPlayerChatLine(sourcePlayer, safeMessage, not fromOthers)
Will become this:
local line = createPlayerChatLine(sourcePlayer, safeMessage, bubbleColors, not fromOthers)
After this, we are done modifying OnPlayerChatMessage. To refresh: createPlayerChatLine will create a chat line which is actually just metadata associated with our message for later rendering. This object is passed around, so we’ll have our colours readily available for use later in regards to where it’s sent, CreateChatLineRender. We will revisit this later: for now, we need to go to createPlayerChatLine to account for the new parameter and to inject our colours to the line data. To line 181!
Editing createPlayerChatLine and createChatLine
Welcome to createPlayerChatLine, where we will now inject our received colours from the parameters.
Fun fact: createPlayerChatLine is actually a wrapper function for createChatLine. createChatLine is also used by other functions, so createPlayerChatLine wraps it to attach player information to the chat line for future reference such as the player’s name. createChatLine is called and then the table is returned to createPlayerChatLine if we want to add extra player info. In our case, as player input will trigger the need to use a different chat colour.
With that in mind: so we can keep createChatLine usable by other functions (specifically Chat.Chat which is the only other natively supported function that can cause bubble chats), we’re going to add our colours from here instead of from the root function, but so that we don’t have to do heavy lifting later, we’re also going to quickly modify createChatLine to create some defaults.
First of all, add a bubbleColors parameter beside the message one so we can shift our parameters down and accept the colours. Straight forward, pictured example if you need it.
Recall what bubbleColors is. It is the “BubbleChatColors” table that we extracted from the ChatMessage object a while back. This is a table containing two keys: Text and Fill. this
is a table. With that in mind, we will be placing our two keys from bubbleColors into this. ChatMessage after this becomes wholly irrelevant to us.
if bubbleColors then
this.TextColor = bubbleColors.Text
this.FillColor = bubbleColors.Fill
end
* Note: While we know for a fact that bubbleColors will be non-nil, just to be on the safe side you’ll want to account for the incident that we do not receive bubbleColors.
Now move up to the function before this, which is the root createChatLine. We will be setting our default bubble chat colours, black text and white fill, here. Going forward, we will have the BubbleChat LocalScript refer to these fields in the chat line table to determine the colour of a chat and make it easier for us to integrate different colours. Possibly, in the future, NPC colours too!
Just like in the code above, add a TextColor and FillColor to the this
table.
this.TextColor = Color3.new(0, 0, 0)
this.FillColor = Color3.new(1, 1, 1)
And then we’re done. We have successfully specified default colours that createChatLine will use when creating a chat line and our createPlayerChatLine function will, if it receives a bubbleColors parameter, modify these to the proper colours. We can move on to our final destinations.
createPlayerChatLine was called by OnPlayerChatMessage, so following back through the logic we find ourselves back at line 577 with the variable line. This is sent to the function CreateChatLineRender, so we will now be going there. Follow me to line 520, or wherever it is - the line count has definitely changed since we’ve added code.
Going through CreateChatLineRender
CreateChatLineRender is the primary function responsible for doing the displaying of chats. This is where we can see the results of our work pay off, with coloured chats. This function receives the chat line made earlier from createPlayerChatLine, so we just need to use that data to start modifying the way chats are set up.
Are you still with me? I hope!
We’ll start with the easy parts first and then move into more difficult grounds. The easy part is changing the colour of the text. Look for the function CreateBubbleText in CreateChatLineRender. Like at the beginning of the tutorial, this only deals in the message but we want to use more than just the message here. That being said, as this function is reused for a different purpose, we’ll need to send the message first and then the rest of the data later. This will also help us modify the distant chat bubble.
Find this:
local bubbleText = this:CreateBubbleText(line.Message, shouldAutoLocalize)
And make it this:
local bubbleText = this:CreateBubbleText(line.Message, line, shouldAutoLocalize)
Now find the actual CreateBubbleText function. Again, super easy to modify because this function only creates a TextLabel. We’ll be adding a second parameter which is the full table of message data that we can use later.
Now that this is done, we’re going to go and change the properties of the TextLabel using the information we have in chat line information. This is just standard property writing. As we already have access to the original message as a parameter, we’re only adding a new line - new text colour.
Done. Text colour is now changed. In the off chance that another function uses CreateBubbleText without passing the second and/or third argument, the if statement will catch that and prevent the text color from being changed. In our case, the only other time this is called is to make the distant chat ellipses, so we’ll revisit changing those colours later. That part is fairly difficult.
You can quickly test what you have so far. In my case, as I set my text to be pure white for testing purposes, my bubble should also be pure white if this works.
Let’s move on to colour the fill of the bubbles now. This part is also super easy, but dangerous. We’re assuming that the hierarchy of the chat bubbles will always remain the same. Any change to the chat bubble’s structure will break this code and it’s impossible to account for it arbitrarily, so we’ll need to move forward with the assumption that the bubble parent-child structure stays the same.
The actual rendered bubble will be an ImageLabel and the tail will be a child of it. We’re lucky that these images are pure white, because changing the image colour will exactly reflect the colours that we want. Add two lines to change the bubble’s colours. They must be done before setting it visible so that the changes register first, then it’s enabled.
chatBubbleRender.ImageColor3 = line.FillColor
chatBubbleRender.ChatBubbleTail.ImageColor3 = line.FillColor
And now let’s test again. If this works, I should have white text and a dark red chat bubble. This is nearly our final destination, as we will still have one more roadblock to overcome: the distance chat bubble. That part is much more difficult to work with!
Our finished result:
And our final problem:
The distance chat bubble is a much more difficult barricade to overcome. There is also only one chat line, so we can never keep the colours fully consistent. It’d be fine to leave it like this but you may be slightly annoyed by the inconsistency.
For this, I would create a new function entirely. It will make sure that the chat bubble assumes the last colour used for a chat. The BubbleChat LocalScript first calls a function to create a BillboardGui for bubble chat renders, so after that’s done we will run our update function.
Like how we changed the colour of the bubble and the tail earlier, we need to make various assumptions about the structure of the chat and that they will remain true. We will also need to follow some of the conventions seen throughout the script to get this done right.
Our main assumption is that the distance chat bubble will always remain “SmallTalkBubble”. Our second assumption is that SmallTalkBubble always exists, but is made visible or invisible depending on the camera’s distance from the character. Using these assumptions, we get to work.
I will hand over the code fully written with comments to guide you through what it is doing. I recommend putting this function under CreateBillboardGuiHelper so that you can find it right in around the same area as the actual billboard creation.
This is the function you will need:
--- Ran to modify the colour of the small talk bubble after-the-fact
-- Accepts a character and the message data
function this:UpdateBillboardGui(instance, line)
-- This is how the BubbleChat script gets the BillboardGui for rendering chats
local billboardGui = this.CharacterSortedMsg:Get(instance)["BillboardGui"]
-- If the BillboardGui exists, then we'll proceed. It seems the above returns a table,
-- where the key BillboardGui is set to the return value of createBillboardInstance
-- (which literally returns a BillboardGui instance).
if billboardGui then
-- Find SmallTalkBubble, assuming the structure of the BillboardGui if non-nil
-- is always the same (has a BillboardFrame container in it).
local smallTalkBubble = billboardGui.BillboardFrame:FindFirstChild("SmallTalkBubble")
if smallTalkBubble then
-- Make wild assumptions about the structure remaining consistent.
smallTalkBubble.ImageColor3 = line.FillColor
smallTalkBubble.ChatBubbleTailFrame.ChatBubbleTail.ImageColor3 = line.FillColor
smallTalkBubble.BubbleText.TextColor3 = line.TextColor
end
end
end
And you will place it right under the creation of the BillboardGui.
this:UpdateBillboardGui(instance, line)
Our long carnival ride of wild assumptions has brought us this glorious resolution:
* Note: Wouldn’t be surprised if this breaks. Let me know and I’ll try and look into a solution that better accounts for this possibility while still taking a novice-friendly approach. I don’t want to start adding third party libraries or anything complex to get this done.
And with that taken care of, you now have yourself a fully customisable bubble chat colouring system to work from. All you need to do is add bubble chat colours to players at your own whims by using SetExtraData and passing a table with the Text and Fill keys. You can also directly modify a ChatMessage’s ExtraData if you know how to do that.
ChatSpeaker:SetExtraData("BubbleChatColors", {
Text = Color3.new(0, 0, 0),
Fill = Color3.new(0, 0, 0),
})
Postface
This has been my first tutorial in a long while, and I’ve really forgotten how fun it is to share my knowledge with the developer community to help better their works. I may have made a few mistakes or oversights. I’ve gone into as much depth as I could about this topic. I always tinker with the Lua Chat System every now and again, learning the API and how to customise it to fit a game’s needs.
On another note, I have created the #lua-chat-system tag. Although tags can be publicly used and so threads that also use this may blur out my own, I will be using this tag on all previous and new tutorials on the Lua Chat System. I plan to have a lot more tutorials going out about working with the Lua Chat System for those who want to continue to use it but also reap the benefits of it, rather than resorting to custom chat systems to have that same level of extensibility.
Let me know if you have any questions, comments, concerns or any other type of feedback. Let’s keep things constructive and on topic. Happy developing!