Raycasting: how do i do it

i’ve read upon raycasts and rays and whatnot for the past hour, and, no matter what i do, i feel so awfully confused.

i want it so if you’re on top of a part, that part’ll become invisible until you step off of it. it’s not what i /need/ for what i’m doing, but, it’s basic enough and i can always go to edit it later.

i’ve tried to have help with this before like with @konlon15 and others in another thread, but, i still can’t understand this at all. how do i call the function for when something intersects? how do i make it efficient so it doesn’t slow down the game?

this all is really confusing. any help would be appreciated.

20 Likes

You’d cast a ray a few studs below the player’s humanoid root part, then find parts on that ray. It should return the part, then you use the appropriate code to change the transparency.

https://www.robloxdev.com/api-reference/function/Workspace/FindPartOnRay

A code example might be:

local Ray = Ray.new(game.Workspace.Mariofly5.HumanoidRootPart.Position, game.Workspace.Mariofly5.HumanoidRootPart.Position - Vector3.new(0,3,0))

local part = game.Workspace:FindPartOnRay(Ray,game.Workspace.Mariofly5)

Things to remember - you will need to ignore the character with FindPartOnRay

hope this helps

3 Likes

Rays are a very interesting part of Roblox because they can be used for many different purposes. They can be used for anti-exploit purposes for checking whether a player is fly hacking or to create ranged weapons. Also many more things…

The way that you create a ray is by using the inbuilt function:
Ray.new(Vector3 origin, Vector3 direction)

This would create an invisible ‘beam’ that can be used to hit parts and return true.
If you wanted to find a part that the ray hits, you would use another inbuilt function:
local part = workspace:FindPartOnRay(ray,[optional] parts to ignore)

What this does is it finds the part that the ray has hit. If it doesn’t hit anything, part will be nil.

You can also use this method to get the Vector3 value in the place that the ray hits an object; This can be done by adding the variable position into the above function:
local part,position = workspace:FindPartOnRay(ray,[optional] parts to ignore)

There is also a more advanced ‘surface normal’ variable that can be added and is probably way to complicated for what you want from raycasting. Having said that, this link is a good place to get started.

As for your idea of invisible parts, you could do something like this:

game.Players.PlayerAdded:Connect(function(player)
    local invisiblePart = workspace.InvisiblePart
    local Ignore = player.Character
    while wait(2) do --You can change the wait value to anything
    local Ray = Ray.new(player.Character.HumanoidRootPart.CFrame.p,Vector3.new(0,-5,0)) --Creating the ray down from the character
    local part = workspace:FindPartOnRay(Ray,Ignore) -- Finding the part that the ray hits
    
    if part then -- If there is a part 
        if part == invisiblePart then -- If the part is the one you want to be invisible
            invisiblePart.Transpareny = 1
        else
            invisiblePart.Transparency = 0
        end
    end
end)

These links may also be useful:
Raycasting article
How to make a raycasting laser gun

Hope this helps! :smile:

42 Likes

thanks @RedDuck765 and @Mariofly5! i tried looking a bit into it, and, i’ve seen the function FindPartOnRayWithWhitelist(). while i have a slightly better understanding now, i still am confused.

i tried using this, since i don’t want to use a bunch of ifs just to find out if the part is a part of a certain model. i just get an error, saying, “cannot cast objects.” any other way i can go about this?

2 Likes

If you are using workspace:FindPartOnRayWithWhitelist(), make sure that the whitelist parameter is a table, not a singular part.
If so, do something like this:

Local whitelist = {part}
Local part = workspace:FindPartOnRayWithWhitelist(Ray,whitelist)
1 Like

Just a quick note: the second argument in Ray.new should be a direction, but it looks like you’re inputting an endpoint there instead. For raycasting down in world space you should just use Vector3.new(0, -Length, 0)

3 Likes

alright, sooo… i’m trying to do this with a group of parts, right?
well, here’s sort of what i have;

(note: safespikes has multiple models in them, to which those models have parts inside.)

local ray = Ray.new(player.Character.HumanoidRootPart.CFrame.p, Vector3.new(0,-5,0)) --Creating the ray down from the character
while character.Humanoid do
	wait()
	local wl = {}
	for i, v in pairs(safespikes:GetChildren()) do
		table.insert(wl, v)
		local p = workspace:FindPartOnRayWithWhitelist(ray, wl)
		print(p)
	end
end

not sure why it always returns nil. i know i did something wrong… but what??

1 Like

Just try this:

while character.Humanoid do
    wait()
    local wl = {}

    for i,v in pairs(safespikes:GetChilren()) do
        table.insert(wl,v)
    end

    local Ray = Ray.new(player.Character.HumanoidRootPart.CFrame.p, Ve    3.new(0,-10,0)
    local part = workspace:FindPartOnRayWithWhitelist(Ray,wl)
    print(part.Name)
end

I think your problem was that you casted the ray once and so if the player moved, it would return the part from where the player was when they started

2 Likes

character:FindFirstChild("Humanoid") or humanoid.Health>0
you can also use
wl = safespikes:GetChildren()
And if this code is referring to the undertale thing (or hiding spikes as stepped on), you do not need raycasting as I have already mentioned before, you can just use distance checking.

1 Like

yeah it is
i guess that’s true. i’ve just been told it would be easier to use raycasting but i’ll just consider that too

You can do simple AABB testing or just usual distance checking. The recommended method would be to have spike regions where if you are within X studs of the spikes, you handle the spike regions. If you are within Y studs of a spike region, that spike region is handled (to reduce CPU usage) (unneeded spike distance calculations and it still is more efficient than a raycast). You would also move the spikes locally (and on server too, just so it neither lags behind or doesnt replicate). You would most likely check if they are within the X and Z bounds and if they are not too high up on their Y. If you want to use a check that works when spikes are at any angle, you can use:

local localized = spikeCFrame:toObjectSpace(characterCFrame) --coordinates of character relative to the spike (spikeCFrame*offset = characterCFrame)
local withinx = abs(localized.X)<spikeSize.X/2
local withinz = abs(localized.Z)<spikeSize.Z/2
local withiny = localized.Y>0 and localized.Y<4
local pushedup = withinx and withinz and withiny --check if server activated also

This method also makes sure that if you are on multiple platforms, they are all handled, otherwise you would be halfway in a spike

1 Like

yep, that fixed it. thanks!!!

ah yes, i forgot about that hehe

thx :smiley:

Here’s a short helper function I include in all of my projects to help me visualize how my rays are being used in my projects. This can be a helpful learning tool when constructing rays if you prefer visual feedback.

local Debris = game:GetService("Debris")

return function(ray, life, color3)
	life = life or 2
	color3 = color3 or BrickColor.new("Bright red").Color

	local visualRay = Instance.new("CylinderHandleAdornment", workspace.Terrain)
	visualRay.Height = ray.Direction.magnitude

	local center = ray.Origin + ray.Direction / 2

	visualRay.AlwaysOnTop = false
	visualRay.CFrame = CFrame.new(center, center + ray.Direction)
	visualRay.Color3 = color3
	visualRay.Radius = 0.1
	visualRay.Transparency = 0
	visualRay.Adornee = workspace.Terrain

	Debris:AddItem(visualRay, life)
	return visualRay
end
Other visual helper methods I include

DrawPoint.lua

local Debris = game:GetService("Debris")

return function(position, life, color3, size)
	life = life or 2

	local visualPoint = Instance.new("SphereHandleAdornment", workspace.Terrain)
	visualPoint.Adornee = workspace.Terrain
	visualPoint.Radius = size or 1
	visualPoint.CFrame = CFrame.new(position)
	visualPoint.Transparency = 0.5
	visualPoint.Color3 = (color3 or BrickColor.new("Bright blue")).Color
	visualPoint.Name = "DrawnPoint"

	Debris:AddItem(visualPoint, life)

	return visualPoint
end

DrawRegion3.lua

local Debris = game:GetService("Debris")

return function(region, life, color3)
	life = life or 2

	local boxHandle = Instance.new("BoxHandleAdornment", workspace.Terrain)
	boxHandle.Color = color3 or BrickColor.new("Bright green").Color
	boxHandle.Adornee = workspace.Terrain
	boxHandle.Size = region.Size
	boxHandle.CFrame = region.CFrame
	boxHandle.Transparency = 0.5

	Debris:AddItem(boxHandle, life)

	return boxHandle
end
7 Likes