Put this Script anywhere you like but preferably somewhere this Script can only runs once
Source Code
Code
– added comments and minor improvements
--| SERVICES:
local PlayerS = game:GetService("Players")
local plr = PlayerS.LocalPlayer
local Cha;
local Hum;
local RootPart;
local TweenS = game:GetService('TweenService')
local TweenInfo_New = TweenInfo.new
--| VARIABLES:
local Musics = script.Musics
Musics.Parent = game.SoundService
local currentMusic;
--| FUNCTIONS:
-- Character Handling:
local HumDied_Connection, Humanoid_Died;
function Humanoid_Died()
HumDied_Connection:Disconnect(); HumDied_Connection = nil
if currentMusic then
currentMusic()
end
end
local function Character_Added(Character)
--[[
Make sure that this doesn't run twice when Character_Added() is called exclisively @line 64 if CharacterAdded:Connect Fires
however we could create another function and call that instead (then assign nil)
because the if statement becomes useless after the first time
--]]
if (not Cha) then
Cha = Character; Hum = Cha:WaitForChild('Humanoid'); RootPart = Hum.RootPart
HumDied_Connection = Hum.Died:Connect(Humanoid_Died)
end
end
local function Character_Removing(Character)
Cha:Destroy(); Hum:Destroy();
Cha, Hum = nil
end
local function isPointInPart_Fn(Point, Part)
local relPos = Part.CFrame:PointToObjectSpace(Point)
return math.abs(relPos.X) < Part.Size.X*.5 and math.abs(relPos.Y) < Part.Size.Y*.5 and math.abs(relPos.Z) < Part.Size.Z*.5
end
local function isAlive(humanoid)
return humanoid and humanoid.Health > 0
end
--| SCRIPTS:
plr.CharacterAdded:Connect(Character_Added);
--[[
We call Character_Added() exclusively because CharacterAdded:Connect might not Fire
--]]
Character_Added(plr.Character or plr.CharacterAdded:Wait());
plr.CharacterRemoving:Connect(Character_Removing);
-- Area Music:
for _,Sound in ipairs(Musics:GetChildren()) do
local isInPart
local part = Sound.Part
local touched_Fn, touchEnded_Fn
local touched_Ev, touchEnded_Ev = v.Part.Touched, v.Part.TouchEnded
local touched_Connection, touchEnded_Connection
local playTween = TweenS:Create(Sound, TweenInfo_New(1,0,0), {Volume = 5,})
local stopTween = TweenS:Create(Sound, TweenInfo_New(2,0,1), {Playing = false, Volume = 0,})
local diedTween = TweenS:Create(Sound, TweenInfo_New(2,0,1), {PlaybackSpeed = 0, Volume = .1,})
local function died_Fn() -- Handles when the player dies
diedTween:Play()
isInPart = nil
touchEnded_Connection:Disconnect() -- stop detecting touched
touched_Connection = touched_Ev:Connect(touched_Fn) -- start detecting touchEnded
end
-- Play
function touched_Fn(touchedPart)
-- isAlive | make sure that the Humanoid is Alive
-- isInPart | acts as a Debounce and State
if isAlive(Hum) and (not isInPart) and touchedPart == RootPart then
isInPart = true
touched_Connection:Disconnect() -- stop detecting touched
touchEnded_Connection = touchEnded_Ev:Connect(touchEnded_Fn) -- start detecting touchEnded
Sound.PlaybackSpeed = 1
Sound:Play()
stopTween:Cancel()
playTween:Play()
currentMusic = died_Fn
end
end
-- Stop
function touchEnded_Fn(touchedPart)
if isInPart and touchedPart == RootPart then
if (not isPointInPart_Fn(RootPart.Position,part)) then
isInPart = nil
touchEnded_Connection:Disconnect() -- stop detecting touchEnded
touched_Connection = touched_Ev:Connect(touched_Fn) -- start detecting touched
playTween:Cancel()
stopTween:Play()
currentMusic = nil
end
end
end
touched_Connection = touched_Ev:Connect(touched_Fn)
part.Parent = workspace
--[[
You might want to do
part.Transparency = 1
part.CanCollide = false
part.Anchored = true
... etc
--]]
end
return nil
– might actually be better to do Tween:Pause() instead of Tween:Cancel()
– but not doing Tween:Pause() nor Tween:Cancel() could be better
– I haven’t done enough testing on those so let me know if you get to
This is actually very nice, I’ve been looking around for something similar to this and I couldn’t seem to find anyone who did anything like it, props to you man .
as a future idea, allowing multiple audios to play for the same area, random, so that each time you enter it is a different song
also, for some reason I keep getting this error
[Requested module experienced an error while loading](rbxopenscript://www.dummy.com/dummy?scriptGuid=%7B727781D7-3228-4274-8BED-E6F29B5693FB%7D&gst=2#1)
@Bloxrrey, Interesting that you mentioned that, Region3 is very tedious and not Non-Programmer friendly, it’s a lot harder to set up than just using Parts, I can’t see how it’s easier to use.
You can’t Rotate it
You need to use a while loop and then go through all the parts inside the Region
You need to set it up correctly with a lot of restrictions
The API is tedious and hard to understand
Using TouchEvent is more Efficient and Effective in this case
if you meant using Zone+ or some other Module, I rather not share something claimed as my own creation by using someone else’s resource
I don’t know what that error means @Wertyhappy27 but the suggestions are good
If you are requiring by Id you can’t this Module isn’t meant for that.
Just to add on, Region3 is more and more intensive for larger and larger regions. Ideally you would use Region3 to index a small area for multiple instances when a Touched event isn’t reliable; local music playing should really be enforced by the server since it doesn’t affect other players, thus Touched is pretty safe to use.
How does your method account for rotation? All I see is a comparison of rectangular dimensions. Although I don’t really see rotation being useful for something like this anyways, better to keep it simple too. (EDIT: this is wrong, read below for clarification)
Regardless this is a very clean module, and I like the smooth transitions you implemented.
Using CFrame does account for rotation, but when you are checking if the point is in the part, you are only using the position of the point and the size of the part.
i.e., you are only using position data from relPos to check. You could expand it by checking the orientation data from the CFrame but like I said above, it’s not really useful.
Edit:
Nevermind I’m just being dumb as usual, PointToObjectSpace automatically takes into account the orientation and applies it to position (like a transformation matrix). Thanks @RedcommanderV2
@Wertyhappy27
You can’t require a LocalScript, only ModuleScript.
@dispeller
Nope, but you can use this:
local function IsPointInPart_Fn(Point, Part) -- Cylinder
local relPos = Part.CFrame:PointToObjectSpace(Point)
local distFromCenter = Vector2_New(relPos.Y, relPos.Z).Magnitude
local radius = Part.Size.Y*.5
return Math_Abs(relPos.X) < Part.Size.X*.5 and distFromCenter < radius
end
Personally, the first time I made an area music system, I used Region3.
I looped this every 5 seconds. That way, each piece of music played for at least 5 seconds. Kinda prevents breaking immersion
function UpdateRegion()
local inRegion = false
for i,v in pairs(game.ReplicatedStorage.BorderBlocks:GetChildren()) do
local min = v.Position - (0.5 * v.Size)
local max = v.Position + (0.5 * v.Size)
local region = Region3.new(min, max)
local whitelist = {Player.Character}
local parts = workspace:FindPartsInRegion3WithWhiteList(region,
whitelist, 100)
--warn(#parts)
for partIndex, part in pairs(parts) do
--print('PART NAME!'..v.Name)
if part.Parent == Player.Character then
inRegion = true
if regionCurrentlyInside ~= v.Name then
regionCurrentlyInside = v.Name
print'CHANGED REGION!'
end
end
end
end
if not inRegion then
if regionCurrentlyInside ~= 'Outside' then
regionCurrentlyInside = 'Outside'
print'went outside'
end
end
end
There was also this module called Zone+ that might be useful.
Despite the comments about using region 3 and about using TouchedEvent not being reliable, I thank you for this resource that can improve my scripting abilities, continue doing what you do best .
Also is there a way to turn this into a music player where you can just insert the song id and play different types of music such as a party setting etc and how would you go about doing that?