So i am working on a custom chat script and i am making custom bubbles and i am currently struggling on animations. My script works but after the first 2 bubbles it doesn’t get removed and it removes a little decoration on all bubbles instead it supposed to remove the last 3. It is similar to Roblox Chat System like UUID’s to make a report system. Here is my FULL script:
-- Config
local Debug = false
local SDS = game.ServerScriptService.ExperienceChat.Main.ServerDebug
local SDebug = SDS.Value
-- Services
local G = game
local Players = G:GetService("Players")
local ReplicatedStorage = G:GetService("ReplicatedStorage")
local TextService = G:GetService("TextService")
local TweenService = G:GetService("TweenService")
-- Folders
local Main_Remote = ReplicatedStorage:WaitForChild("ExperienceChat"):WaitForChild("remote")
local Main_Module = ReplicatedStorage:WaitForChild("ExperienceChat"):WaitForChild("module")
local FrameChat_Remote = ReplicatedStorage:WaitForChild("ExperienceChat"):WaitForChild("FrameChat"):WaitForChild("remote")
local BubbleChat_Remote = ReplicatedStorage:WaitForChild("ExperienceChat"):WaitForChild("BubbleChat"):WaitForChild("remote")
-- Remotes
local ReceiveText = FrameChat_Remote:WaitForChild("ReceiveText")
local SendText = FrameChat_Remote:WaitForChild("SendText")
local MessageBlocker = FrameChat_Remote:WaitForChild("MessageBlocker")
local SendBubble = BubbleChat_Remote:WaitForChild("SendBubble")
local LocalDebug = Main_Remote:WaitForChild("LocalDebug")
local ServerDebug = Main_Remote:WaitForChild("ServerDebug")
local RunCommand = Main_Remote:WaitForChild("RunCommand")
local Notifier = Main_Remote:WaitForChild("Notifier")
LocalDebug.OnServerEvent:Connect(function(Player)
if Debug == false then
Debug = true
else
Debug = false
end
end)
SDS.Changed:Connect(function()
SDebug = SDS.Value
end)
local function generateRandomString()
local function getRandomSection(length)
local section = ""
for i = 1, length do
local charType = math.random(1, 2)
if charType == 1 then
section = section .. string.char(math.random(65, 90))
else
section = section .. math.random(0, 9)
end
end
return section
end
return getRandomSection(8) .. "-" .. getRandomSection(4) .. "-" .. getRandomSection(4) .. "-" .. getRandomSection(4) .. "-" .. getRandomSection(12)
end
SendBubble.OnServerEvent:Connect(function(player, Username, Text, ID)
if Debug then
local message = "[Debug] Received bubble request from: ".. Username.. '", Text: "'.. Text.. '" | Server'
local Type = print
Notifier:FireClient(player, message, Type)
end
if SDebug then
print("[ServerDebug] @".. Username, 'fired a bubble request: "'.. Text.. '" | SendBubble')
end
local random = generateRandomString()
local BubbleTemplate = player:WaitForChild("ExperienceChat"):WaitForChild("Template"):WaitForChild("BubbleFrame")
if not BubbleTemplate then
if SDebug then
warn("[Debug] Could not find Bubble Template for @".. Username.. " | SendBubble")
end
return
end
local Bubble = BubbleTemplate:Clone()
Bubble.Name = "Bubble" .. ID .. "-{" .. random .. "}"
local Character = game.Workspace:FindFirstChild(Username)
local Head = Character and Character:FindFirstChild("Head")
local BubbleGui = Head and Head:FindFirstChildOfClass("BillboardGui")
local BubbleFrame = BubbleGui and BubbleGui:FindFirstChild("BubbleChatList")
if not BubbleFrame then
if SDebug then
warn("[ServerDebug] Could not find BubbleChatList for @".. Username.. " | SendBubble")
end
return
end
-- Clean up old bubbles
for _, child in ipairs(BubbleFrame:GetChildren()) do
local deadlineValue = child:FindFirstChild("DeadlineLayout")
if deadlineValue then
local deadlineNumber = tonumber(deadlineValue.Value) or 0
if deadlineNumber >= 4 then
if Debug then
print("[Debug] Removing old bubble:", child.Name)
end
child:Destroy()
else
deadlineValue.Value = tostring(deadlineNumber + 1)
end
end
end
-- Update the positions of the bubbles
local function updateBubblesYPosition()
local bubbles = {}
-- Collect all bubbles that are frames and have a DeadlineLayout
for _, bubble in ipairs(BubbleFrame:GetChildren()) do
if bubble:IsA("Frame") and bubble:FindFirstChild("DeadlineLayout") then
table.insert(bubbles, bubble)
end
end
-- Sort bubbles based on DeadlineLayout value
table.sort(bubbles, function(a, b)
local aDeadline = tonumber(a:FindFirstChild("DeadlineLayout") and a.DeadlineLayout.Value) or 0
local bDeadline = tonumber(b:FindFirstChild("DeadlineLayout") and b.DeadlineLayout.Value) or 0
return aDeadline < bDeadline
end)
local startY = 1
local spacing = 0.25
local tweens = {} -- Store tweens here
-- Destroy the old Layout to reset the positions
local Layout = BubbleFrame:FindFirstChild("Layout")
if Layout then
Layout:Destroy()
if SDebug then
print("[ServerDebug] Disabled UIListLayout for @".. Username, "| SendBubble")
end
end
-- Pre-calculate the target positions for all bubbles
for i, bubble in ipairs(bubbles) do
local newY = startY - ((i - 1) * spacing)
local deadlineValue = tonumber(bubble:FindFirstChild("DeadlineLayout") and bubble.DeadlineLayout.Value) or 0
-- Handle bubbles with deadlineValue == 1 (skip caret destruction for these)
if deadlineValue == 1 then
bubble.Position = UDim2.new(0.5, 0, 1, 0)
print("[Fixed] Position for", bubble.Name, "set to", bubble.Position)
else
-- Destroy caret for non-deadlineValue 1 bubbles
local caret = bubble:FindFirstChild("Caret")
if caret then
print("[Debug] Destroying Caret for bubble:", bubble.Name)
caret:Destroy()
end
-- Create and store tween for the position
local tweenInfo = TweenInfo.new(0.4, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local tweenGoal = { Position = UDim2.new(0.5, 0, newY, 0) }
local tween = TweenService:Create(bubble, tweenInfo, tweenGoal)
table.insert(tweens, tween) -- Store the tween
print("[Fixed] Tweened position for", bubble.Name, "to", bubble.Position)
end
end
-- Play all the tweens simultaneously
for _, tween in ipairs(tweens) do
tween:Play()
end
-- Function to check if all tweens are completed
local function checkTweensCompletion()
for _, tween in ipairs(tweens) do
tween.Completed:Wait()
if tween.PlaybackState ~= Enum.PlaybackState.Completed then
return false
end
end
return true
end
-- Retry logic until tweens complete
while not checkTweensCompletion() do
warn("[ServerDebug] Tweens didn't complete properly. Retrying...")
-- Re-run the function and reset tweens
tweens = {}
updateBubblesYPosition() -- Keep retrying by calling the function until successful
end
-- After successful completion, create the new Layout
local LayoutCopy = player:WaitForChild("ExperienceChat"):WaitForChild("Template"):WaitForChild("BubbleChatGui"):WaitForChild("BubbleChatList"):WaitForChild("Layout"):Clone()
LayoutCopy.Parent = BubbleFrame
if SDebug then
print("[ServerDebug] Enabled UIListLayout for @".. Username, "| SendBubble")
end
end
-- Parent the new bubble and set the text
Bubble.Parent = BubbleFrame
local BubbleText = Bubble.ChatBubbleFrame.Text
BubbleText.Text = Text
-- Calculate and set bubble size based on the text size
local textSize = TextService:GetTextSize(Text, BubbleText.TextSize, BubbleText.Font, Vector2.new(math.huge, math.huge))
local BaseXoffset = 16
local Max_Width = 21 * 15 -- Maximum width (21 characters max)
local NewXoffset = math.min(textSize.X + BaseXoffset, Max_Width)
local BaseYoffset = 16
local Yoffset_PerLine = 20
local Max_Lines = 8
local NumLines = math.min(math.ceil(textSize.X / Max_Width), Max_Lines)
local NewYoffset = BaseYoffset + NumLines * Yoffset_PerLine
-- Apply new bubble size
Bubble.Size = UDim2.new(0, NewXoffset, 0, NewYoffset)
print("[Debug] Set bubble size to", Bubble.Size)
-- Call function to update bubble positions (WITH TWEENS)
updateBubblesYPosition()
-- Destroy the bubble after 10 seconds, with fade-out effect
task.delay(10, function()
if Bubble then
print("[Debug] Destroying bubble:", Bubble.Name)
local chatBubbleFrame = Bubble:FindFirstChild("ChatBubbleFrame")
local text = chatBubbleFrame and chatBubbleFrame:FindFirstChild("Text")
if chatBubbleFrame and text then
local fadeOutBackgroundTween = TweenService:Create(
chatBubbleFrame,
TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out),
{ BackgroundTransparency = 1 }
)
fadeOutBackgroundTween:Play()
local fadeOutTextTween = TweenService:Create(
text,
TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out),
{ TextTransparency = 1 }
)
fadeOutTextTween:Play()
chatBubbleFrame.BackgroundTransparency = 0.1
text.TextTransparency = 0.1
fadeOutBackgroundTween.Completed:Wait()
fadeOutTextTween.Completed:Wait()
Bubble:Destroy()
end
end
end)
end)
The error is after the “updateBubblesYPosition” function
First error; it removes a little decoration under all the bubbles instead it should remove the last 3 one’s, it does work for the first 2 but then it breaks apart:
for i, bubble in ipairs(bubbles) do
local newY = startY - ((i - 1) * spacing)
local deadlineValue = tonumber(bubble:FindFirstChild("DeadlineLayout") and bubble.DeadlineLayout.Value) or 0
-- Handle bubbles with deadlineValue == 1 (skip caret destruction for these)
if deadlineValue == 1 then
bubble.Position = UDim2.new(0.5, 0, 1, 0)
print("[Fixed] Position for", bubble.Name, "set to", bubble.Position)
else
-- Destroy caret for non-deadlineValue 1 bubbles
local caret = bubble:FindFirstChild("Caret")
if caret then
print("[Debug] Destroying Caret for bubble:", bubble.Name)
caret:Destroy()
end
-- Create and store tween for the position
local tweenInfo = TweenInfo.new(0.4, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local tweenGoal = { Position = UDim2.new(0.5, 0, newY, 0) }
local tween = TweenService:Create(bubble, tweenInfo, tweenGoal)
table.insert(tweens, tween) -- Store the tween
print("[Fixed] Tweened position for", bubble.Name, "to", bubble.Position)
end
end
Second error; removing the bubbles doesn’t work after the first 2 bubbles while testing:
task.delay(10, function()
if Bubble then
print("[Debug] Destroying bubble:", Bubble.Name)
local chatBubbleFrame = Bubble:FindFirstChild("ChatBubbleFrame")
local text = chatBubbleFrame and chatBubbleFrame:FindFirstChild("Text")
if chatBubbleFrame and text then
local fadeOutBackgroundTween = TweenService:Create(
chatBubbleFrame,
TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out),
{ BackgroundTransparency = 1 }
)
fadeOutBackgroundTween:Play()
local fadeOutTextTween = TweenService:Create(
text,
TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out),
{ TextTransparency = 1 }
)
fadeOutTextTween:Play()
chatBubbleFrame.BackgroundTransparency = 0.1
text.TextTransparency = 0.1
fadeOutBackgroundTween.Completed:Wait()
fadeOutTextTween.Completed:Wait()
Bubble:Destroy()
end
end
end)