Raycasting to detect terrain only

I’m trying to shoot a ray into the ground from above and detect if it hits terrain only and not a part.
I’ve looked all over and haven’t seemed to find a solution to this yet.

			
			if raycastResult then
				if raycastResult.Instance:IsA("Terrain") then
					print("terrain")
				end
2 Likes

I think you can set up your params where it only includes terrain as the results

local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Include
params.FilterDescendantsInstances = {workspace.Terrain}
3 Likes

doesn’t seem to print anything now, here is my full script.

local rayOrigin = Vector3.new(math.random(-part.Size.X/2, part.Size.X/2) , part.Position.Y + part.Size.Y , math.random(-part.Size.Z/2, part.Size.Z/2))
			local rayDirection = Vector3.new(0,-part.Size.Y,0)
			
			local raycastParams = RaycastParams.new()
			raycastParams.FilterType = Enum.RaycastFilterType.Include
			raycastParams.FilterDescendantsInstances = {workspace.Terrain}
			--raycastParams.RespectCanCollide = false
			local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
			
			if raycastResult then
				print(raycastResult)
				if not table.find(blacklist, raycastResult.Material) then --not blacklisted material
					print("terrain", raycastResult.Material)
					local p = Instance.new("Part",workspace)
					p.Position = raycastResult.Position
					p.Anchored = true
				end
				
			end

I think it’s because I’m firing the ray inside a large part, so it detects that part and returns no result because it isn’t on the filterlist. How could I make this part be ignored?

The code snippet you provided seems to be on the right track. Here’s an example of how you could use raycasting to detect if a ray hits terrain and not a part:

local origin = Vector3.new(0, 100, 0) -- replace this with the starting position of the ray
local direction = Vector3.new(0, -1, 0) -- replace this with the direction of the ray
local ray = Ray.new(origin, direction * 1000) -- create a ray with a length of 1000

local workspace = game:GetService("Workspace")
local terrain = workspace.Terrain

local result = workspace:FindPartOnRay(ray)

if result then
    if result:IsA("Terrain") then
        print("Hit terrain")
    else
        print("Hit part")
    end
else
    print("Did not hit anything")
end

This code creates a ray starting at origin and pointing in the direction specified. It then uses the FindPartOnRay function to cast the ray and find the first object it hits. After casting the ray, we check if it hit anything by checking if result is not nil. If it did hit something, we check if it hit terrain by calling the IsA function on result and passing in "Terrain" as an argument.

I hope this helps you detect if a ray hits terrain and not a part!

It’s all printing nil as the result now. Not sure why.

maybe try result.Instance:IsA(“Terrain”), yeah?

here is my full code again

local blacklist = {
	Enum.Material.Air;
	Enum.Material.Water;
}

local function RandomSpawnLocation(zone)
	if Zones[zone]:FindFirstChild("AI") then
		local part = Zones[zone]:FindFirstChild("AI")

		local position = nil

		repeat
			local rayOrigin = Vector3.new(math.random(-part.Size.X/2, part.Size.X/2) , part.Position.Y + part.Size.Y , math.random(-part.Size.Z/2, part.Size.Z/2))
			local rayDirection = Vector3.new(0,-part.Size.Y,0)
			
			local raycastParams = RaycastParams.new()
			raycastParams.FilterType = Enum.RaycastFilterType.Include
			raycastParams.FilterDescendantsInstances = {workspace.Terrain}
			--raycastParams.RespectCanCollide = false
			local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
			print(raycastResult)
			if raycastResult then
				print(raycastResult)
				if not table.find(blacklist, raycastResult.Material) then --not blacklisted material
					print("terrain", raycastResult.Material)
					local p = Instance.new("Part",workspace)
					p.Position = raycastResult.Position
					p.Anchored = true
				end
				
			end
			wait(.1)
			
		until position
	end
	
end

try printing your coordinates and check if something is actually there and don’t check on result check on .Instance also.

Using the include option will only have the filtered instance and descendants as possible results, so it’s not the fact you’re raycasting from within a part.

Your origin is a random offset based on a parts X and Z size, and it raycasts down by the Y Size of the part. So where is this part in workspace in relation to the terrain?

Because if the Y Size is really small, the ray will naturally be small as well in this case. Make sure the part’s Y Position is at least part.Size.Y distance above the terrain (Minus a buffer for consistency), Or set the direction of the raycast to Vector3.new(0, SomeLargerNumber, 0).

Similar to what @DeEchteBelg said.

1 Like

Well it shoots a ray in a random x/z location inside a large part inside of my map, the y coordinate is at the very top of that part, and shoots downward. I’m trying to have it do an event when it hits terrain, I tested it to make parts spawn where it hits, and they seem to spawn on the part itself in random air, and all over the place below, but none on terrain. Not sure how to fix this

local blacklist = {
	Enum.Material.Air;
	Enum.Material.Water;
}

local function RandomSpawnLocation(zone)
	if Zones[zone]:FindFirstChild("AI") then
		local part = Zones[zone]:FindFirstChild("AI")

		local position = nil

		repeat
			local rayOrigin = Vector3.new(math.random(-part.Size.X/2, part.Size.X/2) , part.Position.Y + part.Size.Y , math.random(-part.Size.Z/2, part.Size.Z/2))
			local rayDirection = Vector3.new(0,-part.Size.Y,0)
			
			local raycastParams = RaycastParams.new()
			raycastParams.FilterType = Enum.RaycastFilterType.Include
			raycastParams.FilterDescendantsInstances = {workspace.Terrain}
			--raycastParams.RespectCanCollide = false
			local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
			print(raycastResult)
			if raycastResult then
				print(raycastResult)
				if not table.find(blacklist, raycastResult.Material) then --not blacklisted material
					print("terrain", raycastResult.Material)
					--local p = Instance.new("Part",workspace)
					--p.Position = raycastResult.Position
					--p.Anchored = true
				end
				
			end
			wait(.1)
			
		until position
	end
	
end

this wasmeant to reply to @Kitsomia ^

Have you tried multiplying the rayDirection? Dont forget that raycast are finite on both ends, so I always recommend in cases like your’s, especially if you’re using part’s size as a basically “lenght”, to make the ray longer. Try doing:

local raycastResult = workspace:Raycast(rayOrigin, rayDirection.Unit*10, raycastParams)

It is just printing nil repeatedly from print(raycastresult)

	local rayOrigin = Vector3.new(math.random(-part.Size.X/2, part.Size.X/2) , part.Position.Y + part.Size.Y , math.random(-part.Size.Z/2, part.Size.Z/2))
			local rayDirection = Vector3.new(0,-part.Size.Y,0)
			
			local raycastParams = RaycastParams.new()
			--raycastParams.FilterType = Enum.RaycastFilterType.Include
			raycastParams.FilterDescendantsInstances = {workspace.Terrain}
			--raycastParams.RespectCanCollide = false
			local raycastResult = workspace:Raycast(rayOrigin, rayDirection.Unit * 10, raycastParams)
			print(raycastResult)
			if raycastResult then
				print(raycastResult)
				if not table.find(blacklist, raycastResult.Material) then --not blacklisted material
					print("terrain", raycastResult.Material)
					local p = Instance.new("Part",workspace)
					p.Position = raycastResult.Position
					p.Anchored = true
				end
				
			end
			wait(.1)

why did you comment out this line? The default filterType is blacklist, so if you just ran the script as you provided now, it ignored the terrain cells. Please uncomment this line and try it again

Added that line back, still get the same nil output.

Could you provide us an image on how does the part the raycasts are performed upon look like in the game?


I tried moving it yesterday to serverstorage to make sure the ray wasnt just hitting the part, so the result i’ve been getting has been with this part in serverstorage not in workspace.

image
Noticed this for when you set the ray origin, make it + part.Size.Y * .5. It’s raycasting from above the part in the first place. And not the top of the part.

Try changing your ray origin parameters to this:

local rayOrigin = Vector3.new(part.Position.X + math.random(-part.Size.X/2, part.Size.X/2) , part.Position.Y + part.Size.Y , part.Position.Z + math.random(-part.Size.Z/2, part.Size.Z/2))