Making A Death Message System [Updated!]

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:
image

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.
image

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.

image

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);

image
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!

14 Likes

Just a quick note, I don’t think “killed themselves” is allowed! Haha!

Other than that, great job! Hope this helps a lot of people out.

4 Likes

Good point, I’ll remove it :sweat_smile:

4 Likes

I know I’m late, but I used this script, and it does not send any messages to the chat, and there aren’t any errors in the output. Does anyone know how to fix this?

It seems with the new chat system that StarterGui:SetCore("ChatMakeSystemMessage", ...) no longer works. You’ll have to use TextChatService API.

For my tutorial, simply replace the client code with the following

local ReplicatedStorage = game:GetService("ReplicatedStorage");
local TextChatService = game:GetService("TextChatService")

local deathMessageEvent = ReplicatedStorage:WaitForChild("DeathMessage");

--// Called when a message is sent with TextChatService:DisplaySystemMessage()
function onIncomingMessage(message : TextChatMessage)
	--// 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 onDeathMessage(message)
	--// Adding a prefix to differentiate between death messages and non-death chat messages in onIncomingMessage()
	TextChatService.TextChannels.RBXGeneral:DisplaySystemMessage("@DEATH_MESSAGE:" .. message);
end
deathMessageEvent.OnClientEvent:Connect(onDeathMessage);

I’m getting a warn for “deathMessageEvent”. (i also added game. before TextChatService for the same reason)

What does the warning for deathMessageEvent say?

The warning for it is an unknown global.

deathMessageEvent is a variable that’s supposed to be assigned to a remote event, if you’re getting the unknown global warning, you haven’t assigned it to anything