Greetings!
I’m dev_fractility,
Any feedback onto this topic or any improvement on this is always appreciated! Sorry in advance, this is my first ever post and I hope to get a decent understanding on how to make an Advanced Chat System! I haven’t seen many topics about this, and when I started scripting it always seemed confusing so hopefully this helps clear some issues people had making their own!
First
We are going to create a script underneath ServerScriptService name doesn’t matter!
Inside of the script we will set our locals and create our remote that we are going to use!
local chat_remote = Instance.new("RemoteEvent", game.ReplicatedStorage)
chat_remote.Name = "ChatEvent"
local ServerScriptService = game:GetService("ServerScriptService")
local ChatService = require(ServerScriptService:WaitForChild("ChatServiceRunner").ChatService)
local emojis = {
["devforum"] = "http://www.roblox.com/asset/?id=1251795601"
}
Secondly
We are going to be making the chat message splice function. We will return to a Character Set essentially, it’s a table with all of our data for our chat. The reason we do this is so we can have special effects on pretty much anything allowing our creativity to sprout in ways. I am aware this may be sending too much data through the remote but again this is for example reasons. We will also be setting some for our emotes!
local split = function(Player, Message)
local characterSet = {
--[[
{
Text = "[Owner] ",
Color = Color3.fromgRGB(255, 85, 85)
},
]]-- This is for a rank, you can use images as well using the emoji format.
{
Text = Player.Name..": ",
Color = Color3.fromRGB(85, 85, 255) -- Name Color
}
}
local split_msg = Message:split(" ")
for i, str in ipairs(split_msg) do
local emoted = (emojis[str:lower()] ~= nil and true or false)
if emoted then
table.insert(characterSet, {
Image = emojis[str:lower()] --Making a Image Variable to have our decal id
})
else
table.insert(characterSet, {
Text = str..(#split_msg == i and "" or " "),
--[[
Actually making the text whether its a space after or not depending on the index.
]]
Color = Color3.fromRGB(255, 255, 255) --Color of the chat.
})
end
end
return characterSet
end
Next
This whole function is because “.Chatted” doesn’t return a proper instance for whispers. For those who are wondering what I mean.
e.x
Player.Chatted:connect(function(Message, Whisper)
if Whisper == nil then -- Whisper is always nil.
print("All / Message")
else
print("Whispered")
end
end)
So to switch this up a bit, we can use a hook from Roblox’s Lua Chat System.
local function speakerAdded(speakerName)
local speaker = ChatService:GetSpeaker(speakerName)
local player = speaker:GetPlayer()
speaker.SaidMessage:Connect(function (message, channelName)
if message.MessageType == "Whisper" then
return -- Don't want to send out whispers!
end
for _, plr in next, game.Players:GetChildren() do
local msg = tostring(message.FilterResult) --Studio's Version
if typeof(message.FilterResult) == "Instance" then --Live Version
msg = message.FilterResult:GetChatForUserAsync(plr.UserId) --Filtering
end
chat_remote:FireClient(plr, split(player, msg))
end
end)
end
ChatService.SpeakerAdded:Connect(speakerAdded)
for _, speaker in ipairs(ChatService:GetSpeakerList()) do
speakerAdded(speaker)
end
Lastly
We setup our Chat GUI!
These are just my properties, yours can be whatever, this is just for example!
Inside of our LocalScript (ClientHandler)
We are going to setup our locals and a simple check function for text going out of our chat frame holder.
local chat_remote = game.ReplicatedStorage:WaitForChild("ChatEvent")
local Holder = script.Parent:WaitForChild("Holder")
local chat_size, chat_font = 14, Enum.Font.Merriweather
local function checkOutOfBounds(CurrentTextBounds, Label)
if CurrentTextBounds + (Label:IsA'TextLabel' and Label.TextBounds.X or (chat_size + 8)) > Holder.AbsoluteSize.X then
return true
end
return false
end
Here we are connecting to our OnClientEvent signal to instantiate our chat to be created.
chat_remote.OnClientEvent:connect(function(CharacterSet)
local CurrentLine, CurrentTextBounds = 1, 0 --Positioning for our labels.
--[[ Frame for our Chat ]]--
local chat = Instance.new("Frame", Holder)
chat.Size = UDim2.new(1, 0, 0, chat_size + 4)
chat.BackgroundTransparency = 1
for _, entry in ipairs(CharacterSet) do
if entry["Image"] == nil then -- Checking if its text or image.
for i=1, #entry.Text do -- We will create a label for each letter in our text
--Reasons we do this is to prevent chat breaking when people use things such as "AAAAAAAAAAAAAAAAAAAAA"
local label = Instance.new("TextLabel", chat)
label.Size = UDim2.new(0, 1, 0, chat_size + 4)
label.BackgroundTransparency = 1
label.Font = chat_font
label.TextSize = chat_size
label.TextXAlignment = "Left" -- Better positioning.
label.Text = entry.Text:sub(i,i)
label.TextColor3 = entry.Color
label.TextStrokeTransparency = 0.7 -- Stroke.
--This is just a simple shadow effect (vv)
--[[
local shadow = label:Clone()
label.ZIndex = 2
shadow.Parent = label
shadow.Position = UDim2.new(0, 2, 0, 2)
]]--
if checkOutOfBounds(CurrentTextBounds, label) then
CurrentTextBounds = 0
CurrentLine +=1
end
label.Position = UDim2.new(0, CurrentTextBounds, 0, (CurrentLine-1) * chat_size) -- Positioning based on line-1 to not have a skipped line.
CurrentTextBounds += label.TextBounds.X
end
else -- Images get made here.
local label = Instance.new("ImageLabel", chat)
label.BackgroundTransparency = 1
label.Image = entry.Image
label.Size = UDim2.new(0, chat_size+8, 0, chat_size+8)
if checkOutOfBounds(CurrentTextBounds, label) then
CurrentTextBounds = 0
CurrentLine += 1
end
label.Position = UDim2.new(0, CurrentTextBounds, 0, (CurrentLine-1) * chat_size) -- Positioning based on line-1 to not have a skipped line.
CurrentTextBounds += (chat_size + 8)
end
end
This is where we move old chat frames to make room for our new frame.
for _, old in next, Holder:GetChildren() do
if old:IsA'Frame' and old ~= chat then
old.Position += UDim2.new(0, 0, 0, -(chat_size * CurrentLine))
if math.abs(old.AbsolutePosition.Y) >= Holder.AbsoluteSize.Y then
old:Remove()
end
else
old.Position = UDim2.new(0, 0, 1, -(chat_size * CurrentLine))
end
end
end)
This is our finished result!
Uncopylocked Game Link
Hope you enjoyed this tutorial, and blox on!
Edit 1: Added link for game!
Edit 2: Fixed grammatical issues!