What’s up gamers, I was working a new game recently and the need for material specific footsteps came up again, so after rewriting my preexisting system I decided to make public it so all Roblox games could be better with little effort.
Features:
You can hear other peoples footsteps (With adjustable range)
Time between steps varies based on speed
Very easy to customize
Pretty lightweight
Force specific parts to sound like a different material
Force specific parts to use a specific footstep sound
Showcase:
Main Script
-- Created by Smile4 / Studio Storm --
if script.Parent:IsA("StarterCharacterScripts") then
return
end
-- Services --
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Debris = game:GetService("Debris")
-- Consts --
local SPEED_GATE = 10
local SPEED_MAXIMUM = 15
local MIN_DISTANCE = 0
local MAX_DISTANCE = 100
local FOOTSTEPS : Folder = ReplicatedStorage:WaitForChild("Footsteps")
local CHARACTER : Model = script.Parent
local HUMANOID : Humanoid = CHARACTER:WaitForChild("Humanoid")
local HUMANOID_ROOT_PART : Part = CHARACTER:WaitForChild("HumanoidRootPart")
local SKIN_VECTOR = Vector3.new(0.1, 0, 0.1)
local DELAY_UPPER = (0.33 * (SPEED_MAXIMUM / 17.5)) * (SPEED_MAXIMUM / SPEED_GATE)
-- Vars --
local LastMaterial : string = nil
local LastNum : number = nil
-- Functions --
local function FootstepLoop()
if HUMANOID.Health <= 0 then
return
end
local walkSpeed = Vector3.new(HUMANOID_ROOT_PART.AssemblyLinearVelocity.X, 0, HUMANOID_ROOT_PART.AssemblyLinearVelocity.Z).Magnitude
local delayTime = math.clamp(0.33 * (SPEED_MAXIMUM / walkSpeed), 0.1, DELAY_UPPER)
if walkSpeed > SPEED_GATE then
local castParams = RaycastParams.new()
castParams.FilterDescendantsInstances = {CHARACTER, workspace.CurrentCamera}
castParams.FilterType = Enum.RaycastFilterType.Exclude
castParams.RespectCanCollide = true
local cast = workspace:Blockcast(
CFrame.new(HUMANOID_ROOT_PART.Position),
HUMANOID_ROOT_PART.Size - SKIN_VECTOR,
Vector3.new(0, -1, 0) * (HUMANOID.HipHeight + 0.1),
castParams
)
if cast then
local soundOveride = cast.Instance:GetAttribute("SoundOveride")
if soundOveride then
local soundTable : table = FOOTSTEPS.Raw[soundOveride]:GetChildren()
local newNum = math.random(#soundTable)
if LastMaterial == soundOveride and #soundOveride ~= 1 then
while newNum == LastNum do
newNum = math.random(#soundTable)
end
end
local sound : Sound = soundTable[newNum]:Clone()
LastMaterial = soundOveride
LastNum = newNum
sound.Name = "step"
sound.Volume = 0.33
sound.RollOffMode = Enum.RollOffMode.Linear
sound.RollOffMinDistance = MIN_DISTANCE
sound.RollOffMaxDistance = MAX_DISTANCE
sound.Parent = HUMANOID_ROOT_PART
sound:Play()
Debris:AddItem(sound, sound.TimeLength + 0.1)
else
local material : string = cast.Material.Name
local materialOveride : string = cast.Instance:GetAttribute("Material")
if materialOveride ~= nil then
material = materialOveride
end
local soundTable : table = FOOTSTEPS.Raw[FOOTSTEPS[material]:GetAttribute("Value")]:GetChildren()
local newNum = math.random(#soundTable)
if LastMaterial == material and #soundTable ~= 1 then
while newNum == LastNum do
newNum = math.random(#soundTable)
end
end
local sound : Sound = soundTable[newNum]:Clone()
LastMaterial = material
LastNum = newNum
sound.Name = "step"
sound.Volume = 0.33
sound.RollOffMode = Enum.RollOffMode.Linear
sound.RollOffMinDistance = MIN_DISTANCE
sound.RollOffMaxDistance = MAX_DISTANCE
sound.Parent = HUMANOID_ROOT_PART
sound:Play()
Debris:AddItem(sound, sound.TimeLength + 0.1)
end
end
end
task.delay(delayTime, function()
FootstepLoop()
end)
end
FootstepLoop()
oh and if you use this in your game let me know!