Hi there. hope you’re well.
I’m currently working on volume sliders for the game, one of which is the Roblox Character Sounds.
I’m using Roblox’s default script for the character sounds, I just forked it with my own sounds. I’ve also added an Attribute to the script called Volume
. I’ve set every single sound’s volume in the script according to the attribute, to no avail. What do I do about it please?
--!nonstrict
-- Roblox character sound script
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local AtomicBinding = require(script:WaitForChild("AtomicBinding"))
local function loadFlag(flag: string)
local success, result = pcall(function()
return UserSettings():IsUserFeatureEnabled(flag)
end)
return success and result
end
local FFlagUserAtomicCharacterSoundsUnparent = loadFlag("UserAtomicCharacterSoundsUnparent")
local GeneralVolume = script:GetAttribute("Volume")
local SOUND_DATA : { [string]: {[string]: any}} = {
Climbing = {
SoundId = "rbxasset://sounds/action_footsteps_plastic.mp3",
Looped = true,
Volume = GeneralVolume
},
Died = {
SoundId = "rbxassetid://9116939458",
Volume = GeneralVolume
},
FreeFalling = {
SoundId = "rbxassetid://8382131596",
Looped = true,
Volume = GeneralVolume
},
GettingUp = {
SoundId = "rbxasset://sounds/action_get_up.mp3",
Volume = GeneralVolume
},
Jumping = {
SoundId = "rbxassetid://12567015150",
Volume = GeneralVolume
},
Landing = {
SoundId = "rbxassetid://9113480917",
Volume = GeneralVolume
},
Running = {
SoundId = "rbxassetid://12574518445",
Looped = true,
Volume = GeneralVolume
},
Splash = {
SoundId = "rbxassetid://9116325419",
},
Swimming = {
SoundId = "rbxassetid://342534701",
Looped = true,
Volume = GeneralVolume
},
}
script:GetAttributeChangedSignal("Volume"):Connect(function()
GeneralVolume = script:GetAttribute("Volume")
end)
-- map a value from one range to another
local function map(x: number, inMin: number, inMax: number, outMin: number, outMax: number): number
return (x - inMin)*(outMax - outMin)/(inMax - inMin) + outMin
end
local function playSound(sound: Sound)
sound.TimePosition = 0
sound.Playing = true
end
local function shallowCopy(t)
local out = {}
for k, v in pairs(t) do
out[k] = v
end
return out
end
local function initializeSoundSystem(instances)
local player = instances.player
local humanoid = instances.humanoid
local rootPart = instances.rootPart
local sounds: {[string]: Sound} = {}
-- initialize sounds
for name: string, props: {[string]: any} in pairs(SOUND_DATA) do
local sound: Sound = Instance.new("Sound")
sound.Name = name
-- set default values
sound.Archivable = false
sound.RollOffMinDistance = 5
sound.RollOffMaxDistance = 150
sound.Volume = 0.65
for propName, propValue: any in pairs(props) do
(sound :: any)[propName] = propValue
end
sound.Parent = rootPart
sounds[name] = sound
end
local playingLoopedSounds: {[Sound]: boolean?} = {}
local function stopPlayingLoopedSounds(except: Sound?)
for sound in pairs(shallowCopy(playingLoopedSounds)) do
if sound ~= except then
sound.Playing = false
playingLoopedSounds[sound] = nil
end
end
end
-- state transition callbacks.
local stateTransitions: {[Enum.HumanoidStateType]: () -> ()} = {
[Enum.HumanoidStateType.FallingDown] = function()
stopPlayingLoopedSounds()
end,
[Enum.HumanoidStateType.GettingUp] = function()
stopPlayingLoopedSounds()
playSound(sounds.GettingUp)
end,
[Enum.HumanoidStateType.Jumping] = function()
stopPlayingLoopedSounds()
playSound(sounds.Jumping)
end,
[Enum.HumanoidStateType.Swimming] = function()
local verticalSpeed = math.abs(rootPart.AssemblyLinearVelocity.Y)
if verticalSpeed > 0.1 then
sounds.Splash.Volume = math.clamp(map(verticalSpeed, 100, 350, 0.28, 1), 0, 1)
playSound(sounds.Splash)
end
stopPlayingLoopedSounds(sounds.Swimming)
sounds.Swimming.Playing = true
playingLoopedSounds[sounds.Swimming] = true
end,
[Enum.HumanoidStateType.Freefall] = function()
sounds.FreeFalling.Volume = 0
stopPlayingLoopedSounds(sounds.FreeFalling)
playingLoopedSounds[sounds.FreeFalling] = true
end,
[Enum.HumanoidStateType.Landed] = function()
stopPlayingLoopedSounds()
local verticalSpeed = math.abs(rootPart.AssemblyLinearVelocity.Y)
if verticalSpeed > 0 then
sounds.Landing.Volume = math.clamp(map(verticalSpeed, 50, 100, 0, 1), 0, 1)
playSound(sounds.Landing)
end
end,
[Enum.HumanoidStateType.Running] = function()
stopPlayingLoopedSounds(sounds.Running)
sounds.Running.Playing = true
playingLoopedSounds[sounds.Running] = true
end,
[Enum.HumanoidStateType.Climbing] = function()
local sound = sounds.Climbing
if math.abs(rootPart.AssemblyLinearVelocity.Y) > 0.1 then
sound.Playing = true
stopPlayingLoopedSounds(sound)
else
stopPlayingLoopedSounds()
end
playingLoopedSounds[sound] = true
end,
[Enum.HumanoidStateType.Seated] = function()
stopPlayingLoopedSounds()
end,
[Enum.HumanoidStateType.Dead] = function()
stopPlayingLoopedSounds()
playSound(sounds.Died)
end,
}
-- updaters for looped sounds
local loopedSoundUpdaters: {[Sound]: (number, Sound, Vector3) -> ()} = {
[sounds.Climbing] = function(dt: number, sound: Sound, vel: Vector3)
sound.Playing = vel.Magnitude > 0.1
end,
[sounds.FreeFalling] = function(dt: number, sound: Sound, vel: Vector3): ()
if vel.Magnitude > 75 then
sound.Volume = math.clamp(sound.Volume + 0.9*dt, 0, 1)
else
sound.Volume = 0
end
end,
[sounds.Running] = function(dt: number, sound: Sound, vel: Vector3)
sound.Playing = vel.Magnitude > 0.5 and humanoid.MoveDirection.Magnitude > 0.5
end,
}
-- state substitutions to avoid duplicating entries in the state table
local stateRemap: {[Enum.HumanoidStateType]: Enum.HumanoidStateType} = {
[Enum.HumanoidStateType.RunningNoPhysics] = Enum.HumanoidStateType.Running,
}
local activeState: Enum.HumanoidStateType = stateRemap[humanoid:GetState()] or humanoid:GetState()
local stateChangedConn = humanoid.StateChanged:Connect(function(_, state)
state = stateRemap[state] or state
if state ~= activeState then
local transitionFunc: () -> () = stateTransitions[state]
if transitionFunc then
transitionFunc()
end
activeState = state
end
end)
local steppedConn = RunService.Stepped:Connect(function(_, worldDt: number)
-- update looped sounds on stepped
for sound in pairs(playingLoopedSounds) do
local updater: (number, Sound, Vector3) -> () = loopedSoundUpdaters[sound]
if updater then
updater(worldDt, sound, rootPart.AssemblyLinearVelocity)
end
end
end)
local function terminate()
stateChangedConn:Disconnect()
steppedConn:Disconnect()
if FFlagUserAtomicCharacterSoundsUnparent then
-- Unparent all sounds and empty sounds table
-- This is needed in order to support the case where initializeSoundSystem might be called more than once for the same player,
-- which might happen in case player character is unparented and parented back on server and reset-children mechanism is active.
for name: string, sound: Sound in pairs(sounds) do
sound:Destroy()
end
table.clear(sounds)
end
end
return terminate
end
local binding = AtomicBinding.new({
humanoid = "Humanoid",
rootPart = "HumanoidRootPart",
}, initializeSoundSystem)
local playerConnections = {}
local function characterAdded(character)
binding:bindRoot(character)
end
local function characterRemoving(character)
binding:unbindRoot(character)
end
local function playerAdded(player: Player)
local connections = playerConnections[player]
if not connections then
connections = {}
playerConnections[player] = connections
end
if player.Character then
characterAdded(player.Character)
end
table.insert(connections, player.CharacterAdded:Connect(characterAdded))
table.insert(connections, player.CharacterRemoving:Connect(characterRemoving))
end
local function playerRemoving(player: Player)
local connections = playerConnections[player]
if connections then
for _, conn in ipairs(connections) do
conn:Disconnect()
end
playerConnections[player] = nil
end
if player.Character then
characterRemoving(player.Character)
end
end
for _, player in ipairs(Players:GetPlayers()) do
task.spawn(playerAdded, player)
end
Players.PlayerAdded:Connect(playerAdded)
Players.PlayerRemoving:Connect(playerRemoving)
Thanks,
Aki