How can I make other players chat something client sided? and look real

this is what i have now


this is using chat service - local TextChat = game:GetService(“Chat”)

TextChat:Chat(CurrentChatter.HumanoidRootPart, textBox.Text)

this doesnt look good because the 2 chats are separated and stack separately ( the chat service is the bottom chat)
is there a way to make the chat look like its coming from the player?

i dont want it to be server sided or etc, i just want it to look like theyre chatting from my perspective

you can’t really fake a real player message on the default chat GUI without server authority
(and that’d be a huge exploit) so most people inject a system message that looks like it came from them;

local StarterGui = game:GetService("StarterGui")

local function fakeChat(name, msg)
    StarterGui:SetCore("ChatMakeSystemMessage", {
        Text = string.format("[%s]: %s", name, msg);
        Color = Color3.fromRGB(255,255,255);
        Font = Enum.Font.SourceSans;
        TextSize = 18;
    })
end

-- example
fakeChat("Player1", "Hello")

EDIT;

2 Likes

thanks for responding
i dont undersatand how it works, i tried running from client and it errors this

SetCore: ChatMakeSystemMessage has not been registered by the CoreScripts  -  Client 

Wait, I forgot they use TextChatService now and LegacyChatService is deprecated even tho it still works, I’ll look up how to do it with the new TextChatService API and get back to you.

1 Like

if you have the time to do so , that’d be really helpful
bee n stuck on it for a while

yeah, I’ll do that, I believe it’s something like

TextChatService.TextChannels.<ChannelName>:DisplaySystemMessage(...)

in a localscript to inject client only chat

1 Like

As far as I know, there is no offset property for the chat. The only solution I can think of is to have a part or other kind of instance above the player’s head. When making someone chat, use that instance instead of the character.

1 Like

sorry I dont know how to use this too,
can you show an example of chatting a player?

I didn’t know myself until I read through the docs on how we could achieve this, also watched this video,
I’ve simply just adapted the script to work with TextChatService.

local TextChatService = game:GetService("TextChatService")
local head = script.Parent.Head -- or whatever part you want the bubble above

task.wait(5)
TextChatService:DisplayBubble(head, "Hello")

Let me know if this doesn’t work for some reason.

YouTube Guide I watched

1 Like

thanks this is closer to what i want
thanks for effort so far


when a real message and a fake message is sent they overlap, is there a way to make the fake messages stack with the real messages?
edit: if there isnt, this should work good, Thank u for helping :happy2:

I’ll read on it, seems a bit complicated, I’ll get back to this post if I can find a solution.

1 Like

OK maybeeee, I believe I found something that could work, normal docs Bubble Chat TextChatService
Adornee is the part the bubble is attached to,
weird name I know lol, we should be able to do it by grabbing the generated BillboardGui and tweak its StudsOffset and, in theory, would serve as a fix.

We have to override the default bubble behavior,
we should be able to do that by hooking OnBubbleAdded

local TextChatService = game:GetService("TextChatService")
local head = script.Parent:WaitForChild("Head")
local stackIndex = 0

TextChatService.OnBubbleAdded = function(msg, adornee)
    if adornee == head then
        stackIndex += 1
        local gui = adornee:FindFirstChildWhichIsA("BillboardGui", true)
        if gui then
            gui.StudsOffset = Vector3.new(0, 3 + (stackIndex-1) * 0.6, 0)
            task.delay(gui:GetAttribute("Lifetime") or 5, function()
                stackIndex = math.max(0, stackIndex - 1)
            end)
        end
    end
end

TextChatService:DisplayBubble(head, "Hello")
1 Like

i mightve scripted it wrong but it seems to be doing this

local function KeybindPressed(Input, Gpe)
	
	if not ChangingKeybind and Input.UserInputType == Enum.UserInputType.Keyboard and not Gpe and CurrentChatter then
		
		for _, list in ScrollingFrameChildren do
			
			if Input.KeyCode.Name == list.Text then
				local textBox = list:FindFirstChild("TextBox")
				--TextChat:Chat(CurrentChatter.HumanoidRootPart, textBox.Text)
				TextChatService:DisplayBubble(CurrentChatter.Head, textBox.Text)

(i put onbubbleadded function and stackidnex variable above it)
Lmk if you notice if i did something wrong
idk if it doesnt work but, it should be fine without overlaying
thank u alot for the help

Simplified it, works as intended.

local TextChatService = game:GetService("TextChatService")
local npc = workspace:WaitForChild("NPC")
local head = npc:WaitForChild("Head")

local function sayStacked(lines, pause)
	pause = pause or 1 -- wait in seconds
	for _, text in ipairs(lines) do
		TextChatService:DisplayBubble(head, text)
		task.wait(pause) -- before next line
	end
end

task.wait(2)
sayStacked({
	"Hello, I am Tix",
	"this line pops after 1 second",
	"and another 1 second later"
}, 1)

2 Likes

forgot to mention, just use a script with the RunContext set to client so you can simply leave it inside the NPC.

really sorry just Realised if you put the character model in displaybubble() instead of a part, the chats stack

thank you alot for helping , ive been stuck on this for a while

1 Like

Glad to have been of help, my friend!

1 Like