Ray Intersection + Surface Normal

so I am making a wall climb system based off of this guys advice:

and what I am wondering is how i can get the “Surface Normal” of a part, and how to get the Intersection point of a ray? this is my attempt and it moved me backwards diagonally to the right

		local Root = Character:WaitForChild("HumanoidRootPart")
		local WallRay = Ray.new(Root.Position, Vector3.new(0,0,5))
		
		local hit,pos = workspace:FindPartOnRayWithIgnoreList(WallRay,ignorelist, false, true)
		
		if hit then
			if hit == Character:GetChildren() then return end
			print(hit.Name)
			
			local hitpart = Instance.new("Part")
			hitpart.Position = hit.Position
			print(hitpart.Position)
			
			local teleportpart= Instance.new("Part")
			teleportpart.Position = hit.Position - Root.Position
			Humanoid:MoveTo(teleportpart.Position)
			
		end

TDLR; whats the formula to getting a rays intersection point and “Surface normal”

when i tried that i got the same result? am i being stupid?

		local Root = Character:WaitForChild("HumanoidRootPart")
		local WallRay = Ray.new(Root.Position, Vector3.new(0,0,5))
		local ignorelist = {workspace.Baseplate,Root}
		
		local hit,pos,normal = workspace:FindPartOnRayWithIgnoreList(WallRay,ignorelist, false, true)
		
		if hit then
			if hit == Character:GetChildren() then return end
			print(hit.Name)
			
			local hitpart = Instance.new("Part")
			hitpart.Position = hit.Position
			print(hitpart.Position)
			
			local teleportpart= Instance.new("Part")
			teleportpart.Position = normal
			Humanoid:MoveTo(teleportpart.Position)
			
			
		end

edit: i had my ray going backwards let me test now

edit2: nope… now nothing is happening; full code

local UIS = game:GetService("UserInputService")
local Players = game:GetService("Players")

local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild("Humanoid")


local module = {}

UIS.InputBegan:Connect(function(Input,GPE)
	if Input.KeyCode == Enum.KeyCode.Space then
		if GPE then return end
		local Root = Character:WaitForChild("HumanoidRootPart")
		local WallRay = Ray.new(Root.Position, Vector3.new(0,0,-5))
		local ignorelist = {workspace.Baseplate,Root}
		
		local hit,pos,normal = workspace:FindPartOnRayWithIgnoreList(WallRay,ignorelist, false, true)
		
		if hit then
			if hit == Character:GetChildren() then return end
			print(hit.Name)
			
			local hitpart = Instance.new("Part")
			hitpart.Position = hit.Position
			print(hitpart.Position)
			
			local teleportpart= Instance.new("Part")
			teleportpart.Position = normal
			Humanoid:MoveTo(teleportpart.Position)
			
			
		end
	end
end)

return module

Something I’d noticed looking at your code, you’re comparing hit (an object) to Character:GetChildren() (an array). Perhaps you meant to use the IsDescendantOf method? The method returns a bool whether it’s a descendant within a parent.

print(game.Workspace.Folder.Part:IsDescendantOf(game.Workspace)) -- true

Please lemme know if you have any questions. I hope this helped. :slight_smile:

EDIT
Misread the code, oops. I thought it was because of the if statement that it wasn’t executing.

didnt change anything but thanks for the help

1 Like

I created an entirely new script that uses the newer, more recommended version of raycasting: workspace:Raycast().

You never know when they’ll depreciate it, so it’s better to be safe.

Some context:

Part = what the ray will hit
Mover = the representation of where the ray hits
RayPart = new part to visualize the ray

local part = script.Parent
local rayP = RaycastParams.new() --used to set the interaction of the ray, sort of like the ignore list
rayP.FilterDescendantsInstances = {workspace} --ray will collide with any part in workspace
rayP.FilterType = Enum.RaycastFilterType.Whitelist --whitelist the above, only hit the ones we specificed, not all expect itself

local origin = Vector3.new(0, 15, 30) --create a ray somewhere
local direction = (part.Position - origin) * 50 --I just pointed it towards the part just for the current purposes

local rayResult = workspace:Raycast(origin, direction, rayP) --create a ray and get the result

local function cframe(pos, lookVector) --a function to set the cframe of a part
	
	local modelUpVec = Vector3.new(0, 1, 0)
	local rightVec = lookVector:Cross(modelUpVec)
	local upVec = rightVec:Cross(lookVector)
	
	return CFrame.fromMatrix(pos, rightVec, upVec) --this is replacing CFrame.new(pos, lookAt)
	
end

--visualize the ray
local dist = (rayResult.Position - origin).Magnitude
local rayPart = Instance.new("Part")
rayPart.Anchored = true
rayPart.CFrame = cframe((direction - origin) / 2, direction)
rayPart.Material = "Neon"
rayPart.Size = Vector3.new(0.1, 0.1, dist)
rayPart.CFrame = cframe(origin, rayResult.Position - origin) * CFrame.new(0, 0, -dist / 2) --make it go along the ray
rayPart.Parent = part

part.Mover.CFrame = cframe(rayResult.Position, direction) --put this part at the position of intersection
print(rayResult.Normal) --print the vector of normal it hit, aka, in whcih direction is the normal facing

Now, put this in action:

The blue part is Mover. Now you can see that the ray just about hits the top normal, which I kept it facing straight up. Look at the output:

0, 1, 0 --a vector going up, which is right!

Here is the file with the parts and script if you want to find out more:

RayCasting.rbxm (5.1 KB)

Hope that helps! If you have any questions, feel free to ask!

2 Likes

well what can i do with this relating to my questions

edit: so i want to set the primary part cframe to the blue part?

edit2: what should i do with direction to make it cast forward? (like im not gonna have this in a part, its inside a module of controls, so what do i need to do with the direction math to make the ray cast forwards from the root part

edit3: error;

edit4: thats me rushing it cause script.parent is a modulescript so disregard edit3