Help with ledge mechanics

I’ve been trying to make some ledge mechanics currently and i’m facing some problems

What I've Done So Far
game.Players.PlayerAdded:Connect(function(player)
	
--//Roblox Services 
local runService = game:GetService("RunService")
local collectionService = game:GetService("CollectionService")


--//Variables
local character = player.Character or player.CharacterAdded:Wait()
local humanoidRootPart = character.HumanoidRootPart


runService.Heartbeat:Connect(function()
	
	local ray = Ray.new(humanoidRootPart.Position ,(humanoidRootPart.CFrame.LookVector)*10)
	local hit , position , face , material = workspace:FindPartOnRay(ray,character,false,true)
	
				
	if hit ~= nil then 
		if hit.CanCollide == true and hit.Anchored == true then
			local parts = hit:GetTouchingParts()
			
			for _,v in pairs(parts) do
				if collectionService:HasTag(v,"Ledge") then
							
					if(player.Character.HumanoidRootPart.Position - v.Position).magnitude <= 5 then
						print("Ledge Found")
					end		
						
				end	
			end		
		end	
	end		
		
end)										
		
																	
end)

I’ve tried using CollectionService and getting the position of the ledge but it’s really ineffective as it only recognizes the player if its near the center of the ledge and wouldn’t work in a situation like this where the ledge is so long

I also want to refrain from using CollectionService as it would need to make an insane number of ledges for every model in the game

I’ve even tried to directly get the ledge positions of the block

Like This
local topSurfaceRightEdge = part.Position + Vector3.new((part.Size.X / 2), (part.Size.Y / 2), 0)

But that would also be very ineffective and useless as you would need to calculate the position of every edge of the block

Is there an easier way I could do this using ray casting , to directly get the ledge of the face which the player is looking at ?

5 Likes

Wow! Coincidentally I was just coding something like this:

https://gyazo.com/62b1d49e4cea6f84d30138a80394c49f

I use raycasting to detect the ledge. I cast a couple of rays from the character’s head to their torso each facing forward. When a ledge is found, I transition to a custom “Ledging” state I made. It sets the humanoid state to PlatformStanding, zeroes the velocity of the HumanoidRootPart, and then uses a BodyGyro and BodyVelocity to help my character move along the wall. I use the third result returned from the FindPartOnRay method to get the face of the ledge and align my character with it. For movement, I calculate the left edge and right edge of the ledge the player is hanging on and use it to construct a direction for the BodyVelocity to move along.

8 Likes

You’ll need to use some more advanced maths than just the distance between the centers of the ledge and the center of the character. You can pretty much think of your ledge as a line segment, assuming it will never have any height or depth in a meaningful way.

Now it’s just a matter of googling it :mag:. Here’s a stackoverflow thread on the topic, with an implementation in Java and links to a bunch of other implementations as well as really nice explanations of the underlying maths if you’re into that.

But it’s in Java :roll_eyes: we’ll need it in Lua, so it’s time to port it. Try it yourself, or use this:

Lua version of StackOverflow code
function distanceToSegment( v, a, b )
            --Returns the distance from the point v to the line segment between the points a and b
	local ab = b - a
	local av = v - a

	if (av:Dot(ab) <= 0) then -- Point is lagging behind start of the segment, so
		return av.Magnitude -- Use distance to start of segment instead.
	end

	local bv = v - b

	if (bv:Dot(ab) >= 0 ) then -- Point is advanced past the end of the segment, so
		return bv.Magnitude -- Use distance to end of the segment instead
	end

	return (ab:Cross(av)).Magnitude / ab.Magnitude -- Perpendicular distance of point to segment
end

By the way, does anyone know how to properly give attribution to open-source code?

1 Like