How can I make my material-dependent footstep sounds not stack?

heyhey.

  1. What do you want to achieve?
    I’m trying to achieve a functioning footstep system that plays certain sounds depending on the material of the part the character walks on. It plays a sound when an animation event is reached in my run animation and plays a sound depending on the material of what’s underneath the character, which is found by raycasting.

  2. What is the issue? Everything works fine-- the sound plays at the right time, the sounds change with the material-- except when I move to a new material, the sounds stack instead of changing. I’m very new to this so I have no idea how to fix this issue.
    Here’s a video:

  3. What solutions have you tried so far? I’ve tried searching this, but I’m not exactly sure how to word it-- feels too specific. I need sounds to not stack when a new one is picked from a table. I’d mess with my own script to try and get a solution but I have no clue where to even start.

Here’s the table that references the different placeholder step sounds. “Grass”, “Plastic”, and “Sand” are the names of sounds in workspace

local materialSounds = 
	{
		[Enum.Material.Grass] = (game.Workspace:FindFirstChild("Grass")),
		[Enum.Material.Plastic] = (game.Workspace:FindFirstChild("Plastic")),
		[Enum.Material.Sand] = (game.Workspace:FindFirstChild("Sand")),
	}

here’s what loads the sounds and raycast. this snippet includes making the character sprint and it’s kinda messy, so I added some notes

UIS.InputBegan:Connect(function(Input: InputObject, Processed: boolean)
	if not Processed then 
		if Input.UserInputType == Enum.UserInputType.Keyboard and Input.KeyCode == Key and Faster == true then 
			Humanoid.WalkSpeed = 35 -- make character faster 
			SprintingTween:Play() -- screen effect
			print("runningfaster") -- old debug
			sprintAnimTrack:Play(0) -- play animation
			runService.RenderStepped:Connect(function() -- the raycasting
				local ray = Ray.new(character:WaitForChild("HumanoidRootPart").Position, Vector3.new(0, -5, 0))
				local part, pos, _, material = workspace:FindPartOnRayWithIgnoreList(ray, {character, workspace.Baseplate})
				if part then
					print(part.Name) -- prints what im standing on
					if material then
						print(material) -- prints material of what im standing on
					end
					local soundOfMat = materialSounds[material] -- goes into my sounds table and finds one with the same name as the material
					if soundOfMat then -- if there is a sound with the same name
					sprintAnimTrack:GetMarkerReachedSignal("FootStep"):Connect(function() -- when the animation reaches the marker
					soundOfMat:Play() -- play the sound
						end)
						end
					end
				end)

again, im very new to scripting entirely, so please be patient with me if I don’t understand something/the solution is ridiculously obvious :smiling_face_with_tear: i feel like it is
let me know if you need anything else to help

2 Likes

maybe when you switch terrain, you can destroy the previous sound before adding a new one?

4 Likes

Might be a dumb question, but how could I do this in the script? I can’t think of how without being unable to get it back for the next time I’m on that material
I tried creating the sounds depending on soundOfMat.Name but they are not created

runService.RenderStepped:Connect(function()
				local ray = Ray.new(character:WaitForChild("HumanoidRootPart").Position, Vector3.new(0, -5, 0))
				local part, pos, _, material = workspace:FindPartOnRayWithIgnoreList(ray, {character, workspace.Baseplate})-- Casting a ray
				if part then
					print(part.Name)
					if material then
						print(material)
					end
					local soundOfMat = materialSounds[material]
					if soundOfMat then
						if soundOfMat.Name == ("Enum.Material.Grass") then
						local newsoundgrass = Instance.new("Sound")
						newsoundgrass.Name = ("Grass")
						newsoundgrass.Parent = game.Workspace
							newsoundgrass.SoundId = "rbxassetid://507863105"
							end
						if soundOfMat.Name == ("Enum.Material.Plastic") then
						local newsoundplastic = Instance.new("Sound")
						newsoundplastic.Name = ("Plastic")
						newsoundplastic.SoundId = "rbxassetid://4453297814"
								newsoundplastic.Parent = game.Workspace
								end
						if soundOfMat.Name == ("Enum.Material.Sand") then
						local newsoundsand = Instance.new("Sound")
						newsoundsand.SoundId = "rbxassetid://265653329"
						newsoundsand.Name = ("Sand")
							newsoundsand.Parent = game.Workspace
							end
					sprintAnimTrack:GetMarkerReachedSignal("FootStep"):Connect(function()
							soundOfMat:Play()
							soundOfMat:Destroy()
						end)
						end
					end
				end)
1 Like

so sorry for the late response!

you could do something like

if groundMaterial == "Plastic" then
    if HRP:FindFirstChild("GrassSound") then
         HRP:FindFirstChild("GrassSound"):Destroy
    end
         --create the plastic stepping sound
end

when your script detects a material, it should check for any sounds that might be existing previously and destroy them. After the sound is destroyed it should create the new sound

2 Likes

I understand how this would work, but when I try and use this for the script it still just does not create a new sound instance in workspace
here’s a snippet of the code (what i edited)

if material then
						print(material)
					end
					local soundOfMat = materialSounds[material]
					if soundOfMat then
						print(soundOfMat)
						if material.Name == ("Enum.Material.Grass") then
							local grasssound = Instance.New("Sound")
							grasssound.Name = ("Grass")
							grasssound.Parent = game.Workspace
							if game.Workspace:FindFirstChild("Plastic") then
								game.Workspace:FindFirstCihld("Plastic"):Destroy()
								end
							if game.Workspace:FindFirstChild("Sand") then
								game.Workspace:FindFirstCihld("Sand"):Destroy()
								end
						sprintAnimTrack:GetMarkerReachedSignal("FootStep"):Connect(function() -- when the animation reaches the marker
							soundOfMat:Play() -- play the sound
						end)
					end
					end
					end
			end)

material prints as “Enum.Material.Grass” when I’m standing on grass, so why doesnt the “if material.Name == (“Enum.Material.Grass”) then” create the sound instance?

2 Likes

You’re printing the material itself, not the name of the material. materials dont have names, its printing “Enum.Material.Grass” because the material is grass. You should try something like

if material == enum.Material.Grass then
   --your code here
end
2 Likes

I tried doing it without before posting .Name, but it didn’t work so I tried with just in case and just left it as that when that didn’t work either. Even tried again without .Name to make sure

i’ve tried
if material == Enum.Material.Grass then
if material == ("Enum.Material.Grass") then
if material == (Enum.Material.Grass) then
none work
:pensive:

2 Likes

You’ve made a mistake on one of the lines: local grasssound = Instance.New("Sound")

It should be = local grasssound = Instance.new("Sound")

Instead…

2 Likes

Fixed this error. Still no dice
here’s how the script snippet is right now

UIS.InputBegan:Connect(function(Input: InputObject, Processed: boolean)
	if not Processed then 
		if Input.UserInputType == Enum.UserInputType.Keyboard and Input.KeyCode == Key and Faster == true then 
			Humanoid.WalkSpeed = 35 -- make character faster 
			SprintingTween:Play() -- screen effect
			print("runningfaster") -- old debug
			sprintAnimTrack:Play(0) -- play animation
			runService.RenderStepped:Connect(function() -- the raycasting
				local ray = Ray.new(character:WaitForChild("HumanoidRootPart").Position, Vector3.new(0, -5, 0))
				local part, pos, _, material = workspace:FindPartOnRayWithIgnoreList(ray, {character, workspace.Baseplate})
				if part then
					print(part.Name) -- prints what im standing on
					if material then
						print(material) -- prints material of what im standing on
					end
					local soundOfMat = materialSounds[material] -- goes into my sounds table and finds one with the same name as the material
					if soundOfMat then
						print(soundOfMat)
						if material == Enum.Material.Grass then
							local grasssound = Instance.new("Sound")
							grasssound.Name = ("Grass")
							grasssound.Parent = game.Workspace
							if game.Workspace:FindFirstChild("Plastic") then
								game.Workspace:FindFirstCihld("Plastic"):Destroy()
								end
							if game.Workspace:FindFirstChild("Sand") then
								game.Workspace:FindFirstCihld("Sand"):Destroy()
								end
						sprintAnimTrack:GetMarkerReachedSignal("FootStep"):Connect(function() -- when the animation reaches the marker
							soundOfMat:Play() -- play the sound
						end)
					end
					end
					end
			end)

as of now all im trying to get to happen is the sound to create. i know it wont play i just need it to actually be made

1 Like

Try to print something to the console if you touch grass, if it prints then you know that the if statement is correct and it has something to do with the creation of the sound.

2 Likes

doesnt print. must be something with the if statement, ill try and figure it out

1 Like

FIGURED IT OUT-- this is what i get for messy code. The above if statement looking for soundOfMat was looking for a sound to already be made so it just didn’t run.

…but now the sound is created just about a thousand times. How can I get it to only be made once?

1 Like

I guess you could make it wait like 0.3-0.5 seconds between each sound.

wait(0.3)
2 Likes

The sound is created when the raycast detects im standing on grass. Every single frame. It would still make multiple (correct me if im wrong).
I’m going to try making another if statement inside that detects if a sound named “Grass” already exists and to just return if so

1 Like

Oh okay then you could just delete the sound after you have waited some time. I don’t think another if statement is needed.

1 Like

Could also try adding a debounce and a soundOfmat.ended:wait() to let the sound completely finish before lifting the debounce

1 Like

Oookay so as soon as I fix one thing, there’s another issue. Sound is created fine. Only one is made and the right one is made on the right material. But the others don’t delete? I step on grass, grass sound is made. Then, I step on sand, and the sand sound is made, but grass is not deleted?

if material == Enum.Material.Grass then
						if game.Workspace:FindFirstChild("Grass") then
							return
						else
							local grasssound = Instance.new("Sound")
							grasssound.Name = ("Grass")
							grasssound.SoundId = "rbxassetid://507863105"
							grasssound.Parent = game.Workspace
							if game.Workspace:FindFirstChild("Plastic") then
								game.Workspace:FindFirstCihld("Plastic"):Destroy()
								end
							if game.Workspace:FindFirstChild("Sand") then
								game.Workspace:FindFirstCihld("Sand"):Destroy()
							end
						end
						end
							if material == Enum.Material.Sand then
								if game.Workspace:FindFirstChild("Sand") then
									return
								else
									local sandsound = Instance.new("Sound")
									sandsound.Name = ("Sand")
								sandsound.Parent = game.Workspace
								sandsound.SoundId = "rbxassetid://265653329"
									if game.Workspace:FindFirstChild("Plastic") then
										game.Workspace:FindFirstCihld("Plastic"):Destroy()
									end
									if game.Workspace:FindFirstChild("Grass") then
										game.Workspace:FindFirstCihld("Grass"):Destroy()
									end

im essentially back to square one with the same issue but now longer code

1 Like
local function OnFloorMaterialChanged()
	print(Humanoid.FloorMaterial) --Enum.Material.Air
end

Humanoid:GetPropertyChangedSignal("FloorMaterial"):Connect(OnFloorMaterialChanged)

Just so you’re aware, you can detect floor material changes like this.

1 Like

I actually tried this first, but my custom character’s humanoid hardly detected any materials for some reason. If I remember correctly it was just plastic and grass? Not 100% sure. Switched to raycasting because it worked on all materials

1 Like

You typed FindFirstCihld instead of FindFirstChild in the destroy lines

2 Likes