With Roblox now having Chat on Console with TextChatService, I thought of making a quick chat system for Gamepad input. Specifically, I made a system like Rocket League with the DPad.
Video:
File: Gamepad Quick Chat.rbxl (71.4 KB)
Local Script:
click to open
-- Services & RemoteEvents:
local TextChatService = game:GetService("TextChatService")
local generalChannel: TextChannel = TextChatService:WaitForChild("TextChannels").RBXGeneral
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local RemoteEventsFolder_ForQuickChatSystem = game.Workspace:WaitForChild("QuickChat_RE_Folder")
local SendQuickChatToAllOtherPlayers = RemoteEventsFolder_ForQuickChatSystem.SendQuickChatToAllOtherPlayers
local UpdateUI = RemoteEventsFolder_ForQuickChatSystem.UpdateUI
local TellPlayerNoSpamming = RemoteEventsFolder_ForQuickChatSystem.TellPlayerNoSpamming
-- Local Settings:
local useQuickChatUIAnimations = true
local shouldThisScriptForceOpenChatIfClosed = true
local idleTimeBeforeClosingUI = 3 -- in seconds
-- Logic variables:
local menuIsOpen = false
local playerInteractedTwice = false
local whatMenuIsOpen = nil -- nil, "DPadUp", etc.
local mainOptionsFrame = script.Parent.CanvasGroup.Frame.Options
local canPlayEvenChat = true
if not TextChatService:CanUserChatAsync(game.Players.LocalPlayer.UserId) then
--warn("this player should not be allowed to chat!")
canPlayEvenChat = false
end
local function OpenUI()
script.Parent.Enabled = true
if useQuickChatUIAnimations then
script.Parent.CanvasGroup.GroupTransparency = 1
local tweenInfoFadeIn = TweenInfo.new(0.25,Enum.EasingStyle.Exponential,Enum.EasingDirection.In)
local TweenFadeIn = TweenService:Create(script.Parent.CanvasGroup,tweenInfoFadeIn,{ GroupTransparency = 0 })
TweenFadeIn:Play()
TweenFadeIn.Completed:Wait()
end
end
local function closeUI()
if useQuickChatUIAnimations then
if useQuickChatUIAnimations then
local tweenInfoFadeOut = TweenInfo.new(0.25,Enum.EasingStyle.Exponential,Enum.EasingDirection.Out)
local TweenFadeOut = TweenService:Create(script.Parent.CanvasGroup,tweenInfoFadeOut,{ GroupTransparency = 1 })
TweenFadeOut:Play()
TweenFadeOut.Completed:Wait()
end
else
script.Parent.Enabled = false
end
end
UserInputService.InputBegan:Connect(function(input)
if input.KeyCode == Enum.KeyCode.DPadUp or input.KeyCode == Enum.KeyCode.DPadDown or input.KeyCode == Enum.KeyCode.DPadLeft or input.KeyCode == Enum.KeyCode.DPadRight then
local nameOfCurrentInput = input.KeyCode.Name
if not menuIsOpen then
menuIsOpen = true
whatMenuIsOpen = nameOfCurrentInput
script.Parent.CanvasGroup.Frame.underText.Text = nameOfCurrentInput.."Menu"
-- Get string info from Server
UpdateUI:FireServer(whatMenuIsOpen)
playerInteractedTwice = false
startTimer() -- so menu automatically goes away if player doesn't continue input
else
-- menu is open
if not (whatMenuIsOpen == nil) then
playerInteractedTwice = true
SendQuickChatToAllOtherPlayers:FireServer(whatMenuIsOpen, nameOfCurrentInput)
-- force open chat so player sees message:
if shouldThisScriptForceOpenChatIfClosed then
if canPlayEvenChat then
if game:GetService("StarterGui"):GetCore("ChatActive") ~= true then -- if chat is not open
game:GetService("StarterGui"):SetCore("ChatActive", true) -- open it
end
else
print("this player cannot chat")
end
end
-- Clean up:
nameOfCurrentInput = nil -- do I even need to set local thing here to nil or is that auto Roblox?
cleanUp()
else
warn("error when trying to quick chat! - menu open is nil")
end
end
end
end)
UpdateUI.OnClientEvent:Connect(function(listOfQuickChatStringsForUI)
mainOptionsFrame.Up.TextLabel.Text = listOfQuickChatStringsForUI.DPadUp
mainOptionsFrame.Left.TextLabel.Text = listOfQuickChatStringsForUI.DPadLeft
mainOptionsFrame.Right.TextLabel.Text = listOfQuickChatStringsForUI.DPadRight
mainOptionsFrame.Down.TextLabel.Text = listOfQuickChatStringsForUI.DPadDown
OpenUI()
end)
function cleanUp()
whatMenuIsOpen = nil
menuIsOpen = false
closeUI()
end
function startTimer()
local startTime = os.time()
local thread_kinda
thread_kinda = RunService.Heartbeat:Connect(function(deltaTime)
if not playerInteractedTwice then
local currentTime = os.time()
local timePast = currentTime - startTime
if timePast >= idleTimeBeforeClosingUI then
cleanUp()
thread_kinda:Disconnect()
end
else
startTime = nil
thread_kinda:Disconnect()
end
end)
playerInteractedTwice = false
end
SendQuickChatToAllOtherPlayers.OnClientEvent:Connect(function(fullMessage_withPrefix)
generalChannel:DisplaySystemMessage(fullMessage_withPrefix)
end)
TellPlayerNoSpamming.OnClientEvent:Connect(function(messageToDisplay,metaData)
generalChannel:DisplaySystemMessage(messageToDisplay,metaData)
end)
-- From @be_nj here: https://devforum.roblox.com/t/properties-of-displaysystemmessage-textchatservices/2051223/6
TextChatService.OnIncomingMessage = function(textChatMessage)
if textChatMessage.Metadata == "QuickChatSystem_TellPlayerNoSpamming" then
local overrideProperties = Instance.new("TextChatMessageProperties")
overrideProperties.Text = string.format("<font color='#FF0000'>%s</font>", textChatMessage.Text)
return overrideProperties
else
return nil
end
end
Server Script:
click to open
local RemoteEventsFolder_ForQuickChatSystem = game.Workspace:WaitForChild("QuickChat_RE_Folder")
local SendQuickChatToAllOtherPlayers = RemoteEventsFolder_ForQuickChatSystem.SendQuickChatToAllOtherPlayers
local UpdateUI = RemoteEventsFolder_ForQuickChatSystem.UpdateUI
local TellPlayerNoSpamming = RemoteEventsFolder_ForQuickChatSystem.TellPlayerNoSpamming
local listOfSetQuickChatStrings = {
DPadUp = {
DPadUp = "up 1 (up)", -- up
DPadLeft = "up 2 (left)", -- left
DPadRight = "up 3 (right)", -- right
DPadDown = "up 4 (down)" -- down
},
DPadDown = {
DPadUp = "down 1 (up)", -- up
DPadLeft = "down 2 (left)", -- left
DPadRight = "down 3 (right)", -- right
DPadDown = "down 4 (down)" -- down
},
DPadLeft = {
DPadUp = "left 1 (up)", -- up
DPadLeft = "left 2 (left)", -- left
DPadRight = "left 3 (right)", -- right
DPadDown = "left 4 (down)" -- down
},
DPadRight = {
DPadUp = "right 1 (up)", -- up
DPadLeft = "right 2 (left)", -- left
DPadRight = "right 3 (right)", -- right
DPadDown = "right 4 (down)" -- down
},
}
-- Spam Settings: ---
local timeForCheckingSpam = 0.5
local timeoutTimeForSpamming = 3
local messageToPlayerIfTheyTryToSpam = "No Spamming! Quick Chat disabled for "..timeoutTimeForSpamming.." seconds."
----
SendQuickChatToAllOtherPlayers.OnServerEvent:Connect(function(player,whatMenuIsOpen, nameOfCurrentInput)
local function isValidInput()
local valToReturn = false
if listOfSetQuickChatStrings[whatMenuIsOpen] and listOfSetQuickChatStrings[whatMenuIsOpen][nameOfCurrentInput] then
valToReturn = true
end
--print("isValidInput() ReturnVal: "..tostring(valToReturn))
return valToReturn
end
local function isATimeoutInPlace()
local valToReturn = false
local quickChatSystem_IsPlayerInTimeout_BoolVal
local success_Timeout, response_Timeout = pcall(function()
quickChatSystem_IsPlayerInTimeout_BoolVal = player["quickChatSystem_IsPlayerInTimeout_BoolVal"]
end)
if success_Timeout then
valToReturn = quickChatSystem_IsPlayerInTimeout_BoolVal.Value
else
-- add it
local BoolVal = Instance.new("BoolValue")
BoolVal.Name = "quickChatSystem_IsPlayerInTimeout_BoolVal"
BoolVal.Value = false
BoolVal.Parent = player
end
--print("isATimeoutInPlace() ReturnVal: "..tostring(valToReturn))
return valToReturn
end
local function isThisSpam()
local valToReturn = false
local quickChatSystem_TimeOfLastQuickChat_NumVal
local success, response = pcall(function()
quickChatSystem_TimeOfLastQuickChat_NumVal = player["quickChatSystem_TimeOfLastQuickChat_NumVal"]
end)
--print("isThisSpam() -- pcall:")
--print(success,response)
if success then
--print("quickChatSystem_TimeOfLastQuickChat_NumVal.Value // "..tostring(quickChatSystem_TimeOfLastQuickChat_NumVal.Value))
local timeDiff = os.time() - quickChatSystem_TimeOfLastQuickChat_NumVal.Value
--print("timeDiff // "..tostring(timeDiff))
--print("timeForCheckingSpam // "..tostring(timeForCheckingSpam))
if timeDiff <= timeForCheckingSpam then
valToReturn = true
if not player["quickChatSystem_IsPlayerInTimeout_BoolVal"].Value then
player["quickChatSystem_IsPlayerInTimeout_BoolVal"].Value = true
end
else
-- it's not spam, make it the new lack quick chat time!
player["quickChatSystem_TimeOfLastQuickChat_NumVal"].Value = os.time()
end
else
-- add it
local NumVal = Instance.new("NumberValue")
NumVal.Name = "quickChatSystem_TimeOfLastQuickChat_NumVal"
NumVal.Value = os.time()
NumVal.Parent = player
end
--print("isThisSpam() ReturnVal: "..tostring(valToReturn))
return valToReturn
end
local function sendMessageToAll_FinalStep()
local messageToMakePlayerSayInChat = listOfSetQuickChatStrings[whatMenuIsOpen][nameOfCurrentInput]
local prefix = player.DisplayName
local fullMessage_withPrefix = prefix..": "..messageToMakePlayerSayInChat
SendQuickChatToAllOtherPlayers:FireAllClients(fullMessage_withPrefix)
end
---
local function main()
--print("-----------")
-- allow input
if not isValidInput() then
warn("Invalid quick chat message tried to be sent.")
else
if isATimeoutInPlace() then
local timeDiffSinceSpam = os.time() - player["quickChatSystem_TimeOfLastQuickChat_NumVal"].Value
if timeDiffSinceSpam <= timeoutTimeForSpamming then
-- tell player time left
local metaData = "QuickChatSystem_TellPlayerNoSpamming"
TellPlayerNoSpamming:FireClient(player,messageToPlayerIfTheyTryToSpam,metaData)
else
-- out of timeout now!
--print("timeout over!")
player["quickChatSystem_IsPlayerInTimeout_BoolVal"].Value = false
player["quickChatSystem_TimeOfLastQuickChat_NumVal"].Value = os.time()
sendMessageToAll_FinalStep()
end
else
if isThisSpam() then
--print("-- THIS IS SPAM!")
local metaData = "QuickChatSystem_TellPlayerNoSpamming"
TellPlayerNoSpamming:FireClient(player,messageToPlayerIfTheyTryToSpam,metaData)
else
--print("THIS SHOULD BE WORKING!!!")
sendMessageToAll_FinalStep()
end
end
end
end
---
main()
end)
UpdateUI.OnServerEvent:Connect(function(player,whatMenuIsOpen)
if listOfSetQuickChatStrings[whatMenuIsOpen] then
local menuToSentBack = listOfSetQuickChatStrings[whatMenuIsOpen]
UpdateUI:FireClient(player,menuToSentBack)
else
warn("Invalid quick chat menu type tried to be sent.")
end
end)
Note that if you just use the code, and don’t use the file, you’ll also need RemoteEvents to connect the scripts. The list for quick chat can be found in the Server script.
Update Log for Resource
3/12/2025:
- Added Fade in/out Animations
- Added anti-spamming measures
- Fixed issue where the UI would close prematurely/too early
3/11/2025:
- Initial release
- Security issue as a player can change the message locally that is shown to all
*Note: This uses a CanvasGroup for ease of transparency animation. Feel free to check out the documentation for its drawbacks.
- Reasoning: (image, text, frame, etc. would otherwise have to be catered to for Tweens in a for loop. This isn’t ideal if devs want to add some UI.).
Feel free to suggest features or report issues you find!