Death messages are a fine addition to any game, particularly in a PvP game where knowing who’s dead and who’s alive at a given time is pretty crucial information to have. Yap, yap, yap, you’re probably here because funny death messages go brrr.
With the introduction of TextChatService, using the method StarterGui:SetCore("ChatMakeSystemMessage", ...)
to send chat messages for any purpose is now outdated! But even then, it’s pretty simple to send a chat message and customize them to your liking. Anyways, enough preamble, let’s get started.
At runtime, TextChatService creates two TextChannel instances, RBXGeneral and RBXSystem. It also makes team channels and whisper channels when necessary. For this tutorial we’ll be sending messages using RBXGeneral.
The structure for channels under TextChatService looks like this:
For some set up, we’ll need a remote event in ReplicatedStorage named “SendDeathMessage”
So far, we’ll have a script that looks like this:
-- LocalScript in StarterPlayer.StarterCharacterScripts
--[[ Variables ]]--
-- Services --
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local TextChatService = game:GetService("TextChatService");
-- Remotes --
local sendDeathMessageRemote = ReplicatedStorage.SendDeathMessage;
-- Channels --
local textChannels = TextChatService:WaitForChild("TextChannels");
local generalChannel = textChannels:WaitForChild("RBXGeneral");
For some troubleshooting, TextChatService will not create these default TextChannels if the property CreateDefaultTextChannels
is false. Make sure it is set to true.
Now we’ll make a function that will handle when our death message remote is fired.
There are two methods that will display a chat message: SendAsync and DisplaySystemMessage.
SendAsync will send a chat message to the server as the client, DisplaySystemMessage, locally displays a chat message. Since we just want to display a death message to the player, we’ll use DisplaySystemMessage, which takes only a single argument; the message.
function onDeathMessageSent(message)
generalChannel:DisplaySystemMessage(message);
end
sendDeathMessageRemote.OnClientEvent:Connect(onDeathMessageSent);
Now that the client is all set up, let’s switch over to the server.
This script is going to listen for the character to die, then fire the remote we set up earlier with a random formatted death message, which we will use string.format for.
-- Script in StarterPlayer.CharacterCharacterScripts
--[[ Variables ]]
-- Services --
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local random = Random.new();
-- Remotes --
local sendDeathMessageRemote = ReplicatedStorage.SendDeathMessage;
-- Character --
local char = script.Parent;
local hum = char.Humanoid;
-- Death Messages --
local genericMessages = {"%s died.", "%s is dead.", "%s flatlined."};
local pvpMessages = {"%s killed %s.", "%s murdered %s.", "%s destroyed %s.", "%s ruined %s."};
--[[ Functions ]]--
function getKillerName()
--// Doesn't matter how your game handles this kind of thing, you can tweak this as you need.
return hum:GetAttribute("killerName");
end
function getDeathMessage()
local victimName = hum.DisplayName;
local killerName = getKillerName();
--// If the player dies on their own, send a generic message
if (not killerName or killerName == victimName) then
--// Random string from the genericMessages array
--// #genericMessages is the length of genericMessages
local index = random:NextInteger(1, #genericMessages);
local message = genericMessages[index];
--// The %s in the message will be replaced by the player's name
local formattedMessage = string.format(message, victimName);
return formattedMessage;
end
--// If the player died to another player
local index = random:NextInteger(1, #pvpMessages);
local message = pvpMessages[index];
--// string.format will replace each %s the pvpMessage in the order they appear
local formattedMessage = string.format(message, killerName, victimName);
return formattedMessage;
end
function onDeath()
local deathMessage = getDeathMessage();
sendDeathMessageRemote:FireAllClients(deathMessage);
end
hum.Died:Connect(onDeath);
It might look like I’ve kinda jumped the gun here in terms of complexity but it’s pretty simple. On death, check for a killer. Then take a random string from the appropriate array and format it using the names of the killer (if any) and the victim. Then we take that formatted string and send it to every client in game, who then handles it with the above script.
As you can see, it works great! But there’s an extra step we can do to make it a little more impactful.
If we want to edit text color, size, or font, we’ll have to use TextChatService’s callback function OnIncomingMessage
.
TextChatService.OnIncomingMessage is called before a message is displayed locally. If the function you assign it to returns a TextChatMessageProperties instance, it will send a message with the new properties. There’s no TextColor or size or anything property we can edit here, we’ll have to use RichText formatting!
Here we’re simply going to change the color of the message in chat, so we just have to wrap our message around the following: <font color='rgb(255,0,0)'>MESSAGE_HERE</font>
Now there’s a problem here, we don’t have a way to differentiate between messages being sent. This callback processes ALL messages, be it from players or other system messages you may send. For this reason, we’re going to add a prefix to our death message before we call :DisplaySystemMessage().
--[[ Variables ]]--
-- Services --
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local TextChatService = game:GetService("TextChatService");
-- Remotes --
local sendDeathMessageRemote = ReplicatedStorage.SendDeathMessage;
-- Channels --
local textChannels = TextChatService:WaitForChild("TextChannels");
local generalChannel = textChannels:WaitForChild("RBXGeneral");
--[[ Functions ]]--
--// Called when a message is sent with TextChatService:DisplaySystemMessage()
function onIncomingMessage(message)
--// Checks if this is a death message or not
if (not string.find(message.Text, "@DEATH_MESSAGE:")) then
return nil;
end
local text = string.gsub(message.Text, "@DEATH_MESSAGE:", ""); --// Removes the @DEATH_MESSAGE: prefix
--// You can edit text size, color, and font using rich text formatting here.
local overrideProperties = Instance.new("TextChatMessageProperties");
overrideProperties.Text = string.format("<font color='rgb(255,0,0)'>%s</font>", text);
return overrideProperties;
end
TextChatService.OnIncomingMessage = onIncomingMessage;
function onDeathMessageSent(message)
--// Adding a prefix to differentiate between death messages and non-death chat messages in onIncomingMessage()
generalChannel:DisplaySystemMessage("@DEATH_MESSAGE:" .. message);
end
sendDeathMessageRemote.OnClientEvent:Connect(onDeathMessageSent);
And we’re done! This is a very simple way of adding death messages to your games that requires very little overhead from other scripts. Whenever you damage someone, simply set a killerName attribute to their humanoid and this script will handle the rest.
If you have any questions feel free to ask, thanks for reading. <3
Old, outdated tutorial using legacy chat system
I thought this would be a simple tutorial that requires relatively little scripting knowledge.
For one, you’re going to want a list of two types of death messages, suicides and PvP. I’ll provide some here if you need one.
Then you’re going to need a remote event in ReplicatedStorage, which will pass the message over to the client.
--Put this in a script in StarterCharacterScripts.
local suicideMessages = {"%s died.", "%s is dead.", "%s flatlined."};
local pvpMessages = {"%s killed %s.", "%s murdered %s.", "%s destroyed %s.", "%s ruined %s."};
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local random = Random.new();
local deathMessageEvent = ReplicatedStorage.DeathMessage;
local char = script.Parent;
local hum = char:FindFirstChildOfClass("Humanoid");
function onDeath()
--killerName doesn't have to be an attribute, but for this method, it has to be a string.
local killerName = hum:GetAttribute("killer");
local victimName = hum.DisplayName;
local deathMessage;
if killerName and killerName ~= victimName then --If a player killer this person, and it wasn't themselves.
--This will get a random entry from pvpMessages.
deathMessage = pvpMessages[random:NextInteger(1, #pvpMessages)];
--Then we use string.format() to replace those '%s' with the killerName and victimName.
deathMessage = string.format(deathMessage, killerName, victimName);
else --If it was a suicide.
--This will get a random entry from suicideMessages.
deathMessage = suicideMessages[random:NextInteger(1, #suicideMessages)];
--Then formats like we did above, but with just the victimName.
deathMessage = string.format(deathMessage, victimName);
end
deathMessageEvent:FireAllClients(deathMessage);
end
hum.Died:Connect(onDeath);
--Then put this in a LocalScript.
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local StarterGui = game:GetService("StarterGui");
local deathMessageEvent = ReplicatedStorage:WaitForChild("DeathMessage");
function onDeathMessage(message)
StarterGui:SetCore("ChatMakeSystemMessage", {
Text = message; --Sets the message to be, well, the message.
Color = Color3.fromRGB(255, 0, 0); --Makes the text appear in red.
Font = Enum.Font.SourceSansBold; --Change this if you'd like, but this is the default.
TextSize = 18; --You can also change this if you'd like.
}); --This function displays a message in the chat, the text, color, font, and size can be changed.
end
deathMessageEvent.OnClientEvent:Connect(onDeathMessage);
This whole thing mainly revolves around StarterGui:SetCore() function. This can do more than just make chat messages, you can disable character resetting, send a notification, and more. You can see the rest here.
If you’re a more experienced programmer, you could even make different messages depending on how the player dies! Maybe I’ll update this with a guide on how to do that if anyone would like that.
Thanks for reading!