Raycast whitelist but for a specific piece of terrain / material?

I was wondering if it was possible to make GetPartsBoundsInBox or a raycast have a whitelist with a specific terrain or material. Thanks.

1 Like

I’m pretty sure you can achieve a similar result if you raycast recursively. You raycast once, check what is hit, and if the .Instance doesn’t qualify you keep repeating the raycast at the new hit location until you got what you wanted or until you’ve exhausted the length budget.

Here’s an example below, I whitelisted the Mud and Basalt material. The raycast will pass through anything that aren’t of those materials

local function RaycastForMaterial(origin: Vector3, direction: Vector3, whitelist: {[Enum.Material]: true}): RaycastResult?
	local inset: Vector3 = direction.Unit*.01 --offset the next raycast origin inwards a bit
	
	while true do
		local rcr: RaycastResult? = workspace:Raycast(origin, direction)
		if rcr then
			if whitelist[rcr.Material] then
				return rcr
			end
			local travelled: Vector3 = rcr.Position - origin
			origin = rcr.Position+inset
			direction -= travelled
			continue			
		end
		return
	end
end

local uis = game:GetService('UserInputService')
uis.InputBegan:Connect(function(k, gp)
	if gp then return end
	if k.UserInputType == Enum.UserInputType.MouseButton1 then
		local mPos: Vector2 = uis:GetMouseLocation()
		local mRay: Ray = workspace.CurrentCamera:ViewportPointToRay(mPos.X, mPos.Y)
		
		print(RaycastForMaterial(mRay.Origin, mRay.Direction*1e3, {
			[Enum.Material.Basalt] = true,
			[Enum.Material.Mud] = true,
		}))
	end
end)
1 Like

I actually have a similar function for making raycasts ignore players that I can recycle for this purpose.

Is it possible to have a whitelist and a blacklist in a raycast? I was thinking I can reduce the amount of raycasting that has to be done by whitelisting Workspace.Terrain but then I cant blacklist parts that the raycast comes across that aren’t water.

It should be fairly easy to modify the function I provided to use a proper RaycastParams, and just have that be an additional filter that whitelist/blacklist based on the hierarchy.

If you’re talking about blacklisting materials instead, you can add a boolean parameter to specify that and reuse the whitelist parameter like this:

if (isBlacklist and not whitelist[rcr.Material]) or whitelist[rcr.Material] then
    return rcr
end
1 Like

Not blacklisting materials but blacklisting the actual terrain it goes through (I use the word terrain since parts will be excluded if I have a strictly terrain whitelist. I am basically trying to detect only water with raycasts.) I want to add the terrain it goes through that isnt water to a blacklist before raycasting again.

alternatively is there a way I can reference terrain in a script? E.g getting all the terrain in the game and if the material is water then i add it to the whitelist? If I could do that I’d only ever need to raycast once.

Then all you need to do is whitelist water and nothing else. The function I gave will pass through anything that isn’t whitelisted, meaning that it will continue to raycast through terrain until it hits water.

Here’s the edited function that also takes in a RaycastParams to whitelist terrain, and then only cast for water

local function RaycastForMaterial(origin: Vector3, direction: Vector3, params: RaycastParams?, whitelist: {[Enum.Material]: true}): RaycastResult?
	local inset: Vector3 = direction.Unit*.01 --offset the next raycast origin inwards a bit
	
	while true do
		local rcr: RaycastResult? = workspace:Raycast(origin, direction, params)
		if rcr then
			if whitelist[rcr.Material] then
				return rcr
			end
			local travelled: Vector3 = rcr.Position - origin
			origin = rcr.Position+inset
			direction -= travelled
			continue			
		end
		return
	end
end

local RCP: RaycastParams = RaycastParams.new()
RCP.FilterDescendantsInstances = {workspace.Terrain}
RCP.FilterType = Enum.RaycastFilterType.Include

local uis = game:GetService('UserInputService')
uis.InputBegan:Connect(function(k, gp)
	if gp then return end
	if k.UserInputType == Enum.UserInputType.MouseButton1 then
		local mPos: Vector2 = uis:GetMouseLocation()
		local mRay: Ray = workspace.CurrentCamera:ViewportPointToRay(mPos.X, mPos.Y)
		
		print(RaycastForMaterial(mRay.Origin, mRay.Direction*1e3, RCP, {
			[Enum.Material.Water] = true,
		}))
	end
end)
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.