Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
COMMUNITY QUESTION

Is there anyway to check what terrain the player is walking on?

I'm making a survival game, and I want the player to both walk slower and regain less heat on snow, but I don't know how to check what material they're walking on. I've tried using Humanoid.FloorMaterial, but that value is always nil for me. If anyone knows a better method, that'd be amazing.

3 comments

0

i don't think that there is a way to do that yet

0

use raycasting

0

The issue with terrain is that it is all one big part, and you can't script it, delete it, move it, edit it, check it etc... it would be more efficient to find a realistic grass part/mesh that you can script with. That's also what .FloorMaterial is for the material of the part not the terrian. Let me know if this helps

2 answers

That's actually a good question and it's quite simple too, all you need for that is Raycasting. It will fire ray from your Character and return the result. Here is how you set it up (LocalScript):

So first you need RunService, i will be using it's event called RenderStepped in this case:

local RunService = game:GetService("RunService")

-- Function which will run when render is stepped (Every frame)
-- If you have 60 FPS, it will run 60 times per second
local function OnRenderStepped()

end)

RunService.RenderStepped:Connect(OnRenderStepped) -- Connects the event to the function

Now you need to create Ray, rays are created with 3 arguments, Origin, Direction and RaycastParams. The parameters should be created right at the start of script, they have 4 arguments, FilterDescendantsInstances (Which descendants of given instances will only be accepted OR be ignored based off of given FilterType), IgnoreWater (Whether the ray will ignore water), CollisionGroup (Ray's collisiong group) and FilterType (Blacklist or Whitelist). So let's create the parameters:

local RaycastParameters = RaycastParams.new()
RaycastParameters.IgnoreWater = true or false -- Choose what you need
RaycastParameters.FilterDescendantsInstances = {Character} -- Put your character in here so rays won't be detecting the character
RaycastParameters.FilterType = Enum.RaycastFilterType.Blacklist -- If it would be Whitelist then ONLY Character would be detected, but with blaclist the character WON'T be detectable

Now let's create the ray!

local RunService = game:GetService("RunService")

local HumanoidRootPart = "Your Humanoid Root Part"

local RaycastDistance = 5 -- How far will the ray cast, 5 studs is perfect (It won't detect the terrain when you jump which is good)

local function OnRenderStepped()
	local Origin = HumanoidRootPart.Position -- From where will the ray cast (Vector3)

	-- Now for direction, Since you can't do CFrame.DownVector, you will need to use CFrame.UpVector which returns upper look vector of given CFrame and turn it into negative number which will turn it into DownVector.
	local Direction = - (HumanoidRootPart.CFrame.UpVector * RaycastDistance) - Multiply by raycast distance here to make the ray longer

	-- Now this creates the ray itself which as i said earlier, accepts 3 arguments and is called using workspace
	local Hit = workspace:Raycast(Origin, Direction, RaycastParameters) -- This returns hit which means what did the ray hit
	
	-- Now check if the hit exists
	if Hit then
		-- Hit returns: Instance, Position, Material and 1 more thing sorry i don't remember but it's said somewhere on the devforum, in this case we need Material
		local TerrainName = Hit.Material.Name -- Gets the name of material

		print(TerrainName) -- if i would be standing on grass material, it would say "Grass"
	end
end)

RunService.RenderStepped:Connect(OnRenderStepped)

Now this should be working but let's turn this into a lot more comfortable thing using BindableEvents and it's event called BindableEvent.Event!

local TerrainChangedSignal = Instance.new("BindableEvent") -- Creates the event

local function OnTerrainChanged(NewTerrain) -- Whenever the event is fired,  this function will run
	print(NewTerrain)
end

TerrainChangedSignal.Event:Connec(OnTerrainChanged) -- Connects the event

And now it's done, then you need to connect the event and so the whole script looks like this:

local RunService = game:GetService("RunService")

local TerrainChangedSignal = Instance.new("BindableEvent")

local Character = "Your Character"
local HumanoidRootPart = "Your Humanoid Root Part"

local RaycastParameters = RaycastParams.new()
RaycastParameters.IgnoreWater = false
RaycastParameters.FilterDescendantsInstances = {Character}
RaycastParameters.FilterType = Enum.RaycastFilterType.Blacklist

local RaycastDistance = 5

local function OnTerrainChanged(NewTerrain)
	print(NewTerrain)
end

local function OnRenderStepped()
	local Origin = HumanoidRootPart.Position
	local Direction = - (HumanoidRootPart.CFrame.UpVector * RaycastDistance)

	local Hit = workspace:Raycast(Origin, Direction, RaycastParameters)
	
	if Hit then
		local TerrainName = Hit.Material.Name

		TerrainChangedSignal:Fire(TerrainName) -- Fires the event that floor was changed!
	end
end)

TerrainChangedSignal.Event:Connec(OnTerrainChanged)
RunService.RenderStepped:Connect(OnRenderStepped)

The OnTerrainChanged function will fire every render because i did not include checking if last floor terrain was not the new one but i guess you can make it work, you have 80 reputation after all.

2 comments

0

Help appreciated. I'll be sure to check it out soon.

0

Great answer, every part is nicely described. OP must accept this answer.

I would make the terrain a part. Then the grassy terrain would be normal terrain.

So:

local humanoid = game.Players.LocalPlayer:WaitForChild("Humanoid")
local terrain = game.Workspace.Terrain --the default terrain things in Roblox Studio
local snowMat = game.Workspace.SnowGround --the part here
local lavaMat = game.Workspace.LavaGround --the part here

if humanoid.FloorMaterial = snowMat then
print("On snow.")
end

if humanoid.FloorMaterial = terrain then
print("On terrain base.")
end

if humanoid.FloorMaterial = lavaMat then
print("Burning cuz of lava.")
end

require(script)
require(lavaMat)
require(snowMat)
require(Terrain)
require(humanoid)

Ensure that you know how to use: local humanoid humanoid.FloorMaterial

This discussion is preserved as it was. New answers, comments, and votes are closed.