This tutorial will demonstrate how to create a sound region with parts. Upon entering a part (zone), the sound will be played, and upon leaving the part (zone), the sound will cease.
Now, let's code!
Let's start by getting the services to use them later.
local Players = game:GetService("Players") -- Get player service
local SoundService = game:GetService("SoundService") -- Get sound service
local RunService = game:GetService("RunService") -- get run service for heartbeat, ...
local player = Players.LocalPlayer -- get local player (client)
Now, let's make a function which will handle the zone system, for every zone parts, this function will bee called
local function zone_added(part:BasePart)
end
First, we will check if the part is a basepart and check if there is a sound in there. If no sound is found, then the zone wont work.
local function zone_added(part:BasePart)
if part:IsA("BasePart") then -- Check if the instance is a basepart, to avoid errors
local sound = part:FindFirstChildOfClass("Sound")
if sound ~= nil then -- check if there is a sound instance which will be played when walking in the part, if there is no sound instance then it won't work
end
end
end
Let's configure the sound, by looping it and cloning it into the SoundService to hear it. We will also set the volume to 0 and create an old volume variable which will store the max volume.
local function zone_added(part:BasePart)
if part:IsA("BasePart") then -- Check if the instance is a basepart, to avoid errors
local sound = part:FindFirstChildOfClass("Sound")
if sound ~= nil then -- check if there is a sound instance which will be played when walking in the part, if there is no sound instance then it won't work
sound = sound:Clone()
sound.Parent = SoundService
local oldVolume = tonumber(sound.Volume) -- Volume that will be set when entering the zone
sound.Volume = 0 -- Set the volume to 0
sound.Looped = true -- loop the sound
end
end
end
After doing that, we can now start by making a variable to check if the player is in the part, let's name it isInPart and it'll be a boolean. We'll also create a variable which will store a coroutine to make coroutines for our system. They will be useful for cancelling loops when the player will exit a zone or something else.
local function zone_added(part:BasePart)
if part:IsA("BasePart") then -- Check if the instance is a basepart, to avoid errors
local sound = part:FindFirstChildOfClass("Sound")
if sound ~= nil then -- check if there is a sound instance which will be played when walking in the part, if there is no sound instance then it won't work
sound = sound:Clone()
sound.Parent = SoundService
local oldVolume = tonumber(sound.Volume) -- Volume that will be set when entering the zone
sound.Volume = 0 -- Set the volume to 0
sound.Looped = true -- loop the sound
local isInPart = false -- check if the player is in the zone or not
local actualCoroutine = coroutine.create(function() -- Create a coroutine
end)
end
end
end
Now, everything is ready to be connected. Let's connect the touch event and the touch ended event. We will detect when the player enters the zone with the Touched event and when the player leaves the zone with the TouchEnded event.
local function zone_added(part:BasePart)
if part:IsA("BasePart") then -- Check if the instance is a basepart, to avoid errors
local sound = part:FindFirstChildOfClass("Sound")
if sound ~= nil then -- check if there is a sound instance which will be played when walking in the part, if there is no sound instance then it won't work
sound = sound:Clone()
sound.Parent = SoundService
local oldVolume = tonumber(sound.Volume) -- Volume that will be set when entering the zone
sound.Volume = 0 -- Set the volume to 0
sound.Looped = true -- loop the sound
local isInPart = false -- check if the player is in the zone or not
local actualCoroutine = coroutine.create(function() -- Create a coroutine
end)
part.Touched:Connect(function(hit:BasePart)
end)
part.TouchEnded:Connect(function(hit:BasePart)
end)
end
end
end
Now, we connected the events. Let's add some script in, the hardest part for the script. Let's start by detecting if the hit part is the player and if the player is in or is not in the zone (depends for each event)
local function zone_added(part:BasePart)
if part:IsA("BasePart") then -- Check if the instance is a basepart, to avoid errors
local sound = part:FindFirstChildOfClass("Sound")
if sound ~= nil then -- check if there is a sound instance which will be played when walking in the part, if there is no sound instance then it won't work
sound = sound:Clone()
sound.Parent = SoundService
local oldVolume = tonumber(sound.Volume) -- Volume that will be set when entering the zone
sound.Volume = 0 -- Set the volume to 0
sound.Looped = true -- loop the sound
local isInPart = false -- check if the player is in the zone or not
local actualCoroutine = coroutine.create(function() -- Create a coroutine
end)
part.Touched:Connect(function(hit:BasePart)
if hit.Parent == player.Character and isInPart == false then
end
end)
part.TouchEnded:Connect(function(hit:BasePart)
if hit.Parent == player.Character and isInPart == true then
end
end)
end
end
end
We can start the hardest part. First, we will set the variable isInPart to true for the touched event and to false for the touchended event when the player will get in.
local function zone_added(part:BasePart)
if part:IsA("BasePart") then -- Check if the instance is a basepart, to avoid errors
local sound = part:FindFirstChildOfClass("Sound")
if sound ~= nil then -- check if there is a sound instance which will be played when walking in the part, if there is no sound instance then it won't work
sound = sound:Clone()
sound.Parent = SoundService
local oldVolume = tonumber(sound.Volume) -- Volume that will be set when entering the zone
sound.Volume = 0 -- Set the volume to 0
sound.Looped = true -- loop the sound
local isInPart = false -- check if the player is in the zone or not
local actualCoroutine = coroutine.create(function() -- Create a coroutine
end)
part.Touched:Connect(function(hit:BasePart)
if hit.Parent == player.Character and isInPart == false then
isInPart = true
end
end)
part.TouchEnded:Connect(function(hit:BasePart)
if hit.Parent == player.Character and isInPart == true then
isInPart = false
end
end)
end
end
end
Let's check the status of the coroutine that we created, named actualCoroutine. If this coroutine is running, we will cancel it to break the loop.
local function zone_added(part:BasePart)
if part:IsA("BasePart") then -- Check if the instance is a basepart, to avoid errors
local sound = part:FindFirstChildOfClass("Sound")
if sound ~= nil then -- check if there is a sound instance which will be played when walking in the part, if there is no sound instance then it won't work
sound = sound:Clone()
sound.Parent = SoundService
local oldVolume = tonumber(sound.Volume) -- Volume that will be set when entering the zone
sound.Volume = 0 -- Set the volume to 0
sound.Looped = true -- loop the sound
local isInPart = false -- check if the player is in the zone or not
local actualCoroutine = coroutine.create(function() -- Create a coroutine
end)
part.Touched:Connect(function(hit:BasePart)
if hit.Parent == player.Character and isInPart == false then
isInPart = true
if coroutine.status(actualCoroutine) == "running" then -- check if actualCoroutine is running, if it is, then it will stop it and stop the loops, ... which are inside the coroutine
coroutine.close(actualCoroutine)
end
end
end)
part.TouchEnded:Connect(function(hit:BasePart)
if hit.Parent == player.Character and isInPart == true then
isInPart = false
if coroutine.status(actualCoroutine) == "running" then -- check if actualCoroutine is running, if it is, then it will stop it and stop the loops, ... which are inside the coroutine
coroutine.close(actualCoroutine)
end
end
end)
end
end
end
Now, we will define the coroutine and what will be inside. Inside of the coroutine, we will set:
local function zone_added(part:BasePart)
if part:IsA("BasePart") then -- Check if the instance is a basepart, to avoid errors
local sound = part:FindFirstChildOfClass("Sound")
if sound ~= nil then -- check if there is a sound instance which will be played when walking in the part, if there is no sound instance then it won't work
sound = sound:Clone()
sound.Parent = SoundService
local oldVolume = tonumber(sound.Volume) -- Volume that will be set when entering the zone
sound.Volume = 0 -- Set the volume to 0
sound.Looped = true -- loop the sound
local isInPart = false -- check if the player is in the zone or not
local actualCoroutine = coroutine.create(function() -- Create a coroutine
end)
part.Touched:Connect(function(hit:BasePart)
if hit.Parent == player.Character and isInPart == false then
isInPart = true
if coroutine.status(actualCoroutine) == "running" then -- check if actualCoroutine is running, if it is, then it will stop it and stop the loops, ... which are inside the coroutine
coroutine.close(actualCoroutine)
end
actualCoroutine = coroutine.create(function() -- create a new coroutine
sound:Play() -- play the target sound
for i = 0, oldVolume, 0.01 do -- turn up the sound
if isInPart == false then
break
end
sound.Volume = i
RunService.Heartbeat:Wait()
end
end)
coroutine.resume(actualCoroutine) -- start the new coroutine
end
end)
part.TouchEnded:Connect(function(hit:BasePart)
if hit.Parent == player.Character and isInPart == true then
isInPart = false
if coroutine.status(actualCoroutine) == "running" then -- check if actualCoroutine is running, if it is, then it will stop it and stop the loops, ... which are inside the coroutine
coroutine.close(actualCoroutine)
end
end
end)
end
end
end
local function zone_added(part:BasePart)
if part:IsA("BasePart") then -- Check if the instance is a basepart, to avoid errors
local sound = part:FindFirstChildOfClass("Sound")
if sound ~= nil then -- check if there is a sound instance which will be played when walking in the part, if there is no sound instance then it won't work
sound = sound:Clone()
sound.Parent = SoundService
local oldVolume = tonumber(sound.Volume) -- Volume that will be set when entering the zone
sound.Volume = 0 -- Set the volume to 0
sound.Looped = true -- loop the sound
local isInPart = false -- check if the player is in the zone or not
local actualCoroutine = coroutine.create(function() -- Create a coroutine
end)
part.Touched:Connect(function(hit:BasePart)
if hit.Parent == player.Character and isInPart == false then
isInPart = true
if coroutine.status(actualCoroutine) == "running" then -- check if actualCoroutine is running, if it is, then it will stop it and stop the loops, ... which are inside the coroutine
coroutine.close(actualCoroutine)
end
actualCoroutine = coroutine.create(function() -- create a new coroutine
sound:Play() -- play the target sound
for i = 0, oldVolume, 0.01 do -- turn up the sound
if isInPart == false then
break
end
sound.Volume = i
RunService.Heartbeat:Wait()
end
end)
coroutine.resume(actualCoroutine) -- start the new coroutine
end
end)
part.TouchEnded:Connect(function(hit:BasePart)
if hit.Parent == player.Character and isInPart == true then
isInPart = false
if coroutine.status(actualCoroutine) == "running" then -- check if actualCoroutine is running, if it is, then it will stop it and stop the loops, ... which are inside the coroutine
coroutine.close(actualCoroutine)
end
actualCoroutine = coroutine.create(function() -- create a new coroutine
for i = sound.Volume, 0, -.01 do -- turn off the sound
if isInPart == true then
break
end
sound.Volume = i -- set the volume
RunService.Heartbeat:Wait() -- wait for next heartbeat event fires
end
if coroutine.status(actualCoroutine) == "running" then -- check if the coroutine hasn't be closed, and if it isn't, the sound will stop
sound:Stop()
end
end)
coroutine.resume(actualCoroutine) -- start the new coroutine
end
end)
end
end
end
We finished to make the zone function. Now, let's connect it!
for _, v in pairs(workspace:WaitForChild("Zones"):GetChildren()) do
zone_added(v)
end
Roblox Signal Connection
to connect every new children to the main function:workspace:WaitForChild("Zones").ChildAdded:Connect(zone_added) -- if a zone is added while in game it will be fired to the function and the system will work with this part
It could be hard for you to take every code parts from here and to insert them into your LocalScript but I have no choice. I can't send the full script because there is a text limit but you can contact me if you have any problem (message roblox).