Let’s create a project that uses a BindableEvent to notify multiple scripts about an event. This will be an illuminated dance floor that uses the event to turn on the music and play the activate the dance floor.
A ClickDetector will be used to listen for the player input. But in order to start the dance, the player must have a ticket.
Meaning there needs to be a check for the existence of the ticket. Normally, the dance and music script could just simply connect to the ClickDetector and toggle their functionality.
But because there now needs to be a check for the ticket, that also has to be handled in the connected scripts. If more scripts are added on, you’re gonna be repeating a lot of code in those scripts that connect to the click event.
And then what happens if we add a different means of starting the dance. Say, the option to pay at the door.
Pretty soon, the scripts designated for doing one thing become bloated with repetitive code that would be better handled elsewhere.
So rather than connecting the dance floor related scripts directly to the ClickDetector, we’ll connect them to a BindableEvent. And then have the ClickDetector script or the ticket booth script fire the BindableEvent after the player passes any required checks.
Start by creating the ticket. Just a Part placed down somewhere named ‘Tix.’ I made mine gold and added a decal with the ID http://www.roblox.com/asset/?id=385652894
.
Then add to that a child Script.
local Players = game:GetService("Players")
local part = script.Parent
local debounceTick = 0
part.Touched:Connect(function(otherPart)
if tick() - debounceTick < 0.5 then return end
debounceTick = tick()
local player = Players:GetPlayerFromCharacter(otherPart.Parent)
if player then
part.Parent = player.Backpack
end
end)
This is just a simple touch script. When the player touches the part, it gets moved into the player’s backpack. The ClickDetector will look for this object.
For the ClickDetector, create a stand for the button and the button itself. Then insert the ClickDetector along with a Script.
In ReplicatedStorage, add a BindableEvent object.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local clickDectector = script.Parent.ClickDetector
local bindableEvent = ReplicatedStorage.Event
local errorSoundId = "http://www.roblox.com/asset/?id=3779045779"
local sound = Instance.new("Sound")
sound.SoundId = errorSoundId
sound.Parent = script.Parent
local on = false
clickDectector.MouseClick:Connect(function(player)
if not player or not player.Backpack:FindFirstChild("Tix") then
sound:Play()
return
end
on = not on
bindableEvent:Fire(on)
end)
The button will play an error sound if the player does not have a ticket. To do that, we create a sound object and assign it the ID above then parent it to the button. This will make the sound come from that position.
The script also keeps track of a flag to determine the status of the dance floor. If you had multiple possible sources to enable/disable the dance floor, you might make this a shared variable between those scripts or maybe use a BoolValue and forgo the BindableEvent completely.
In the main function, after the ticket check is successful, all that remains is to fire the event and pass in the status. Because this script is decoupled from the other scripts, we don’t have to track which scripts need to activate or deactivate.
For the music, place a Script into the workspace somewhere.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local bindableEvent = ReplicatedStorage.Event
local songId = 'rbxassetid://1839686965'
local sound = Instance.new("Sound")
sound.SoundId = songId
sound.Looped = true
sound.Parent = game.Workspace
bindableEvent.Event:Connect(function(status)
if status then
sound:Resume()
else
sound:Pause()
end
end)
Similar to the error sound, we create a sound and give it a sound ID. This time, the sound is parented to the workspace which will make it audible from anywhere in the game.
Then the BindableEvent will just plays or pauses the song based on the ‘status’ parameter.
To build the illuminated dance floor, start by placing the tiles and then pattern them however you want. I’m using 4×4 tiles in a checkerboard pattern.
Then parent a SurfaceLight to each tile and set the ‘Face’ property to ‘Top.’ This will make the light shine in the upward direction.
After placing all of the tiles, group them into a model and add a ModuleScript into the model. Rename it ‘DanceFloor.’
We’ll first define the variables.
local RunService = game:GetService("RunService")
local SPEED = 1
local COLOR_CHANCE_CHANCE = 0.5
local TILE_TRANSPARENCY = 0.5
local connection = nil
local timeLeft = 1 / SPEED
local tilesList = {}
local colorsList = {
Color3.fromRGB(164, 20, 217),
Color3.fromRGB(255, 128, 43),
Color3.fromRGB(52, 199, 165),
Color3.fromRGB(249, 225, 5),
Color3.fromRGB(93, 80, 206),
Color3.fromRGB(255, 0, 0),
Color3.fromRGB(0, 255, 0),
Color3.fromRGB(0, 0, 255),
Color3.fromRGB(255, 255, 255)
}
for _, tile in script.Parent:GetChildren() do
if tile:IsA("Part") then
local data = {
tile = tile,
light = tile:FindFirstChild("SurfaceLight"),
defaultColor = tile.Color,
}
table.insert(tilesList, data)
end
end
The speed variable determines how often the tiles change color, in number of times per second. The higher the number, the shorter the cycle and therefore the time between changes.
However, I only wanted only about 50% of the tiles to change each cycle so I created a ‘COLOR_CHANGE_CHANCE’ variable which will be used to determine if a tile changes color.
As the name implies, the ‘timeLeft’ variable is the countdown until the next cycle.
The color list table defines the available colors. During runtime, a color will be randomly selected and assigned to the tile and light.
Finally, we use a loop to populate the tile list. The default color is stored so that we can revert to that color when the dance floor is turned off.
Next, we define the function to change the tile+light colors and the update function:
local function changeColor(tile, color)
local light = tile:FindFirstChild("SurfaceLight")
if color then
tile.Material = Enum.Material.Plastic
tile.Transparency = 0
light.Enabled = false
else
color = colorsList[math.random(1, #colorsList)]
tile.Material = Enum.Material.Neon
tile.Transparency = TILE_TRANSPARENCY
light.Enabled = true
end
tile.Color = color
light.Color = color
end
local function update(dt)
timeLeft = timeLeft - dt
if timeLeft > 0 then return end
timeLeft = 1 / SPEED
for _, tileData in tilesList do
if math.random() > COLOR_CHANCE_CHANCE then
changeColor(tileData.tile)
end
end
end
In addition to changing a tile’s color, the function also needs to have the ability to turn illumination off. To reduce the number of required arguments, we will use the color parameter to determine if the tile should be turned off.
If a color is specified, then the tile is intended to be turned off. And if there is not a color specified, then select a random color from the list and assign it to the tile and light.
In the update function, we decrement the timer until it reaches 0 after which, the time is reset. Then iterate through the list and randomly change color on some of the tiles.
Since this module is self-contained, the only required functions for the interface are a stop and play function.
local DanceFloor = {}
function DanceFloor:play()
if connection then return end
for _, tileData in tilesList do
changeColor(tileData.tile)
end
timeLeft = 1 / SPEED
connection = RunService.Heartbeat:Connect(update)
end
function DanceFloor:stop()
if connection then
connection:Disconnect()
end
for _, tileData in tilesList do
changeColor(tileData.tile, tileData.defaultColor)
end
timeLeft = 1 / SPEED
connection = nil
end
return DanceFloor
The play function turns the tile on for the first time and then sets the timer and connects the update function.
The the stop function works in the opposite. First disconnecting the update function and then loops through each tile, passing the default color to the color change function to turn the tile off.
Finally, the dance floor also needs to listen for the BindableEvent and invoke the module. While this can be integrated into the module, we’ll keep is separate so that dance floor will require fewer external dependencies.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local danceModule = require(game.Workspace.Model.DanceFloor)
local bindableEvent = ReplicatedStorage.Event
bindableEvent.Event:Connect(function(status)
if status then
danceModule:play()
else
danceModule:stop()
end
end)
I added some additional decorations to complete the look and set it to run.
--Workspace/Tix/Script
local Players = game:GetService("Players")
local part = script.Parent
local debounceTick = 0
part.Touched:Connect(function(otherPart)
if tick() - debounceTick < 0.5 then return end
debounceTick = tick()
local player = Players:GetPlayerFromCharacter(otherPart.Parent)
if player then
part.Parent = player.Backpack
end
end)
--Workspace/Button/Script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local clickDectector = script.Parent.ClickDetector
local bindableEvent = ReplicatedStorage.Event
local errorSoundId = "http://www.roblox.com/asset/?id=3779045779"
local sound = Instance.new("Sound")
sound.SoundId = errorSoundId
sound.Parent = script.Parent
local on = false
clickDectector.MouseClick:Connect(function(player)
if not player or not player.Backpack:FindFirstChild("Tix") then
sound:Play()
return
end
on = not on
bindableEvent:Fire(on)
end)
--Workspace/SoundScript
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local bindableEvent = ReplicatedStorage.Event
local songId = 'rbxassetid://1839686965'
local sound = Instance.new("Sound")
sound.SoundId = songId
sound.Looped = true
sound.Parent = game.Workspace
bindableEvent.Event:Connect(function(status)
if status then
sound:Resume()
else
sound:Pause()
end
end)
--Workspace/Model/DanceFloor (ModuleScript)
local RunService = game:GetService("RunService")
local SPEED = 1
local COLOR_CHANCE_CHANCE = 0.5
local TILE_TRANSPARENCY = 0.5
local connection = nil
local timeLeft = 1 / SPEED
local tilesList = {}
local colorsList = {
Color3.fromRGB(164, 20, 217),
Color3.fromRGB(255, 128, 43),
Color3.fromRGB(52, 199, 165),
Color3.fromRGB(249, 225, 5),
Color3.fromRGB(93, 80, 206),
Color3.fromRGB(255, 0, 0),
Color3.fromRGB(0, 255, 0),
Color3.fromRGB(0, 0, 255),
Color3.fromRGB(255, 255, 255)
}
for _, tile in script.Parent:GetChildren() do
if tile:IsA("Part") then
local data = {
tile = tile,
defaultColor = tile.Color,
}
table.insert(tilesList, data)
end
end
local function changeColor(tile, color)
local light = tile:FindFirstChild("SurfaceLight")
if color then
tile.Material = Enum.Material.Plastic
tile.Transparency = 0
light.Enabled = false
else
color = colorsList[math.random(1, #colorsList)]
tile.Material = Enum.Material.Neon
tile.Transparency = TILE_TRANSPARENCY
light.Enabled = true
end
tile.Color = color
light.Color = color
end
local function update(dt)
timeLeft = timeLeft - dt
if timeLeft > 0 then return end
timeLeft = 1 / SPEED
for _, tileData in tilesList do
if math.random() > COLOR_CHANCE_CHANCE then
changeColor(tileData.tile)
end
end
end
local DanceFloor = {}
function DanceFloor:play()
if connection then return end
for _, tileData in tilesList do
changeColor(tileData.tile)
end
timeLeft = 1 / SPEED
connection = RunService.Heartbeat:Connect(update)
end
function DanceFloor:stop()
if connection then
connection:Disconnect()
end
for _, tileData in tilesList do
changeColor(tileData.tile, tileData.defaultColor)
end
timeLeft = 1 / SPEED
connection = nil
end
return DanceFloor
--Workspace/DanceFloorScript
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local danceModule = require(game.Workspace.Model.DanceFloor)
local bindableEvent = ReplicatedStorage.Event
bindableEvent.Event:Connect(function(status)
if status then
danceModule:play()
else
danceModule:stop()
end
end)
Next 🠞
-
- Essentials
- RunService
- Bindables
- Illuminated Dance Floor
- (Coming Soon)
- Essentials