Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

Changing the way character sound effects work?

Asked by 3 years ago
Edited 3 years ago

hi! I posted previously about this, but I was really vague and I think I'm certain on what I want to achieve now.

I'm working on a game that has different playable characters, all of which have their own unique sounds for walking, jumping and dying.

I've been at it for a few hours now and I noticed that RbxCharacterSounds (located in StarterPlayerScripts) is what handles all of the character's sound effects. I noticed that it adds a bunch of sounds to the player's HumanoidRootPart upon spawning.

Basically, here's what I'm wondering: How can I make this script use pre-existing sounds in the HumanoidRootPart (that i would put in there myself) rather than adding new ones?

I tried removing the part where it adds new Instances, but that didn't help. I'm still not perfect at scripting and trying to figure this out has been a headache lol

This is the RbxCharacterSounds script:

-- Roblox character sound script

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local SOUND_DATA = {
    Climbing = {
        SoundId = "rbxasset://sounds/action_footsteps_plastic.mp3",
        Looped = true,
    },
    Died = {
        SoundId = "rbxasset://sounds/uuhhh.mp3",
    },
    FreeFalling = {
        SoundId = "rbxasset://sounds/action_falling.mp3",
        Looped = true,
    },
    GettingUp = {
        SoundId = "rbxasset://sounds/action_get_up.mp3",
    },
    Jumping = {
        SoundId = "rbxasset://sounds/action_jump.mp3",
    },
    Landing = {
        SoundId = "rbxasset://sounds/action_jump_land.mp3",
    },
    Running = {
        SoundId = "rbxasset://sounds/action_footsteps_plastic.mp3",
        Looped = true,
        Pitch = 1.85,
    },
    Splash = {
        SoundId = "rbxasset://sounds/impact_water.mp3",
    },
    Swimming = {
        SoundId = "rbxasset://sounds/action_swim.mp3",
        Looped = true,
        Pitch = 1.6,
    },
}

 -- wait for the first of the passed signals to fire
local function waitForFirst(...)
    local shunt = Instance.new("BindableEvent")
    local slots = {...}

    local function fire(...)
        for i = 1, #slots do
            slots[i]:Disconnect()
        end

        return shunt:Fire(...)
    end

    for i = 1, #slots do
        slots[i] = slots[i]:Connect(fire)
    end

    return shunt.Event:Wait()
end

-- map a value from one range to another
local function map(x, inMin, inMax, outMin, outMax)
    return (x - inMin)*(outMax - outMin)/(inMax - inMin) + outMin
end

local function playSound(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(player, humanoid, rootPart)
    local sounds = {}

    -- initialize sounds
    for name, props in pairs(SOUND_DATA) do
        local sound = Instance.new("Sound")
        sound.Name = name

        -- set default values
        sound.Archivable = false
        sound.EmitterSize = 5
        sound.MaxDistance = 150
        sound.Volume = 0.65

        for propName, propValue in pairs(props) do
            sound[propName] = propValue
        end

        sound.Parent = rootPart
        sounds[name] = sound
    end

    local playingLoopedSounds = {}

    local function stopPlayingLoopedSounds(except)
        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.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.Velocity.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.Velocity.Y)
            if verticalSpeed > 75 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.Velocity.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 = {
        [sounds.Climbing] = function(dt, sound, vel)
            sound.Playing = vel.Magnitude > 0.1
        end,

        [sounds.FreeFalling] = function(dt, sound, vel)
            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, sound, vel)
            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.RunningNoPhysics] = Enum.HumanoidStateType.Running,
    }

    local activeState = 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)
        -- update looped sounds on stepped
        for sound in pairs(playingLoopedSounds) do
            local updater = loopedSoundUpdaters[sound]

            if updater then
                updater(worldDt, sound, rootPart.Velocity)
            end
        end
    end)

    local humanoidAncestryChangedConn
    local rootPartAncestryChangedConn
    local characterAddedConn

    local function terminate()
        stateChangedConn:Disconnect()
        steppedConn:Disconnect()
        humanoidAncestryChangedConn:Disconnect()
        rootPartAncestryChangedConn:Disconnect()
        characterAddedConn:Disconnect()
    end

    humanoidAncestryChangedConn = humanoid.AncestryChanged:Connect(function(_, parent)
        if not parent then
            terminate()
        end
    end)

    rootPartAncestryChangedConn = rootPart.AncestryChanged:Connect(function(_, parent)
        if not parent then
            terminate()
        end
    end)

    characterAddedConn = player.CharacterAdded:Connect(terminate)
end

local function playerAdded(player)
    local function characterAdded(character)
        -- Avoiding memory leaks in the face of Character/Humanoid/RootPart lifetime has a few complications:
        -- * character deparenting is a Remove instead of a Destroy, so signals are not cleaned up automatically.
        -- ** must use a waitForFirst on everything and listen for hierarchy changes.
        -- * the character might not be in the dm by the time CharacterAdded fires
        -- ** constantly check consistency with player.Character and abort if CharacterAdded is fired again
        -- * Humanoid may not exist immediately, and by the time it's inserted the character might be deparented.
        -- * RootPart probably won't exist immediately.
        -- ** by the time RootPart is inserted and Humanoid.RootPart is set, the character or the humanoid might be deparented.

        if not character.Parent then
            waitForFirst(character.AncestryChanged, player.CharacterAdded)
        end

        if player.Character ~= character or not character.Parent then
            return
        end

        local humanoid = character:FindFirstChildOfClass("Humanoid")
        while character:IsDescendantOf(game) and not humanoid do
            waitForFirst(character.ChildAdded, character.AncestryChanged, player.CharacterAdded)
            humanoid = character:FindFirstChildOfClass("Humanoid")
        end

        if player.Character ~= character or not character:IsDescendantOf(game) then
            return
        end

        -- must rely on HumanoidRootPart naming because Humanoid.RootPart does not fire changed signals
        local rootPart = character:FindFirstChild("HumanoidRootPart")
        while character:IsDescendantOf(game) and not rootPart do
            waitForFirst(character.ChildAdded, character.AncestryChanged, humanoid.AncestryChanged, player.CharacterAdded)
            rootPart = character:FindFirstChild("HumanoidRootPart")
        end

        if rootPart and humanoid:IsDescendantOf(game) and character:IsDescendantOf(game) and player.Character == character then
            initializeSoundSystem(player, humanoid, rootPart)
        end
    end

    if player.Character then
        characterAdded(player.Character)
    end
    player.CharacterAdded:Connect(characterAdded)
end

Players.PlayerAdded:Connect(playerAdded)
for _, player in ipairs(Players:GetPlayers()) do
    playerAdded(player)
end


I'm not requesting a script or anything, just wondering how I would make it so that this uses already-existing sounds in the HumanoidRootPart rather than adding new ones (since I want each character to have their own sound effects.)

sorry if this is kinda dumb lol, i'm still learning

Answer this question