Walkspeed does not change with terrain changes

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? I want the player walkspeed to change when the terrain is changed

  2. What is the issue? The message indicates that the correct terrain is detected, but the walkspeed just stays the same at 20.

  3. What solutions have you tried so far? I found solutions on develop hub like using Floormaterial, but when I use this it doesnt even register the change in terrain. My script is below. I have changed it many times to incorporate various solutions, so I apologise if it is a bit messy.


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

local TERRAIN_SPEEDS = {
	["Leafy Grass"] = 10,
	["Grass"] = 40,
	["Mud"] = 25
}
local DEFAULT_SPEED = 20
local WATER_DEATH_MATERIAL = Enum.Material.Water

local lastMaterialEncountered = {}

local function setPlayerSpeed(player, speed)
	local character = player.Character
	if character then
		local humanoid = character:FindFirstChildOfClass("Humanoid")
		if humanoid then
			print("Setting speed for", player.Name, "to", speed) -- Debug message
			humanoid.WalkSpeed = speed
		end
	end
end

local function safeSetPlayerSpeed(player, speed)
	local success, errorMessage = pcall(setPlayerSpeed, player, speed)
	if not success then
		warn("Failed to set player speed for", player.Name, ":", errorMessage)
	end
end

local function onTerrainCheck(player)
	local character = player.Character
	if character then
		local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
		if humanoidRootPart then
			local rayOrigin = humanoidRootPart.Position + Vector3.new(0, 2, 0)
			local rayDirection = Vector3.new(0, -5, 0)
			local raycastParams = RaycastParams.new()
			raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
			raycastParams.FilterDescendantsInstances = {character}

			local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
			if raycastResult and raycastResult.Instance:IsA("Terrain") then
				local material = raycastResult.Material
				local materialName = tostring(material) -- Ensure conversion for table lookup

				-- Check if the material has changed since the last check
				if lastMaterialEncountered[player.Name] ~= materialName then
					local speed = TERRAIN_SPEEDS[materialName] or DEFAULT_SPEED
					print(player.Name, "Walking on", materialName, "- Speed is", speed) 
					lastMaterialEncountered[player.Name] = materialName -- Update the last encountered material

					if material == WATER_DEATH_MATERIAL then
						
						pcall(function()
							local humanoid = character:FindFirstChildOfClass("Humanoid")
							if humanoid then
								humanoid.Health = 0
							end
						end)
					else
						
						safeSetPlayerSpeed(player, speed)
					end
				end
			else
			
				print("Raycast did not hit the terrain or the expected material for", player.Name)
			end
		end
	end
end

RunService.Heartbeat:Connect(function()
	for _, player in ipairs(Players:GetPlayers()) do
		onTerrainCheck(player)
	end
end)

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function()
		safeSetPlayerSpeed(player, DEFAULT_SPEED)
		lastMaterialEncountered[player.Name] = nil 
	end)
end)

1 Like

What does it show the material as in this print statement for the various types? I thought the material would be returned as an Enum, ie Enum.Material.Water

Instead of using a raycast there’s an attribute on the humanoid called FloorMaterial or something like that that returns the material Enum of the floor the humanoid is standing on

It does show it like that, or Enum.Material.Grass, Enum.Material.Mud etc