I’m trying to make a PARKOUR inspired game and I’m really stuck on trying to figure out how to make the character latch onto the pipe and climb on it.
For instance, this is what I’m trying to recreate:
As you can see, when the player presses W or S, they go up/down the pipe. When they press space while looking at the pipe, they boost up for a second, and jump off if they’re not facing it.
I’ve seen some other games recreate this system similarly, so it shouldn’t be that hard to come up with a way to make the pipes behave like that, right? I have some code that detects when the player gets near a pipe, but I haven’t thought up of a way to make the player latch onto the pipe and climb it. I know I can use UserInputService to increase and decrease a value that could change the player’s position on the pipe.
(local script)
local plr = game.Players.LocalPlayer
local char = script.Parent
local hrp = char.HumanoidRootPart
local hum = char.Humanoid
local uis = game:GetService("UserInputService")
local run = game:GetService("RunService")
run.Heartbeat:Connect(function(dt)
local ray = workspace:Raycast(hrp.Position,hrp.CFrame.LookVector * 3)
local part = ray and ray.Instance or nil
if part and part:HasTag("pipe") then
-- pipe climbing code
end
end)
And yes, I have already looked around on the forums, but I mainly created this post since either some posts didn’t help me a lot, or they talked about actual pipes, and there was no posts at all for a system like this.
If anyone could help figure it out and/or tell me what I should or shouldn’t be doing for pipe detection that would be very great!
I created something like this over a year ago. Some in-dev footage of what it looked like at the time (before any polish):
Key ingredients:
AlignPosition to make the player move up/down the pole
AlignOrientation to make the player rotate around the pole
These physics constraints are used to move and rotate the player. Both have their Mode set to OneAttachment and RigidtyEnabled is set to true.
To stick the player onto the pole at the right position, I check for a Touched event between the player’s RootPart and the pole and then find the closest position on the pole to the player’s RootPart. That’s where I attach them to the pole:
local function getClosestPointOnLine(startPoint: Vector3, endPoint: Vector3, point: Vector3)
local lineVector = endPoint - startPoint
local pointVector = point - startPoint
local projectionLength = pointVector:Dot(lineVector.Unit)
if projectionLength < 0 then
return startPoint
elseif projectionLength > lineVector.Magnitude then
return endPoint
else
return startPoint + projectionLength * lineVector.Unit
end
end
When I attach the player to the pole, I set a variable distanceOnLine to track where on the pole the player is located. I also clamp this variable so they can’t climb infinitely up/down.
local posOnLine = closestPointOnLine(poleBottom, poleTop, HRP.Position)
distanceOnLine = (posOnLine - poleBottom).Magnitude
poleLength = (poleBottom - poleTop).Magnitude
if distanceOnLine < 0 then distanceOnLine = 0 end
if distanceOnLine > poleLength then distanceOnLine = poleLength end
Once the player is attached I run a while loop to update the AlignPosition and AlignOrientation every frame.
while climbingThePole do
local leftRight = 0
if UserInputService:IsKeyDown(Enum.KeyCode.A) then -- rotating left
leftRight = leftRight - 1
end
if UserInputService:IsKeyDown(Enum.KeyCode.D) then -- rotating right
leftRight = leftRight + 1
end
AlignOrientation.CFrame = AlignOrientation.CFrame * CFrame.Angles(0, leftRight * ANGULAR_SPEED * dt, 0)
local forward = 0
if UserInputService:IsKeyDown(Enum.KeyCode.W) then -- climbing up
forward = forward + 1
end
if UserInputService:IsKeyDown(Enum.KeyCode.S) then -- climbing down
forward = forward - 1
end
distanceOnLine = distanceOnLine + forward
if distanceOnLine < 0 then -- prevent player from climbing too far up/down
distanceOnLine = 0
elseif distanceOnLine > poleLength then
distanceOnLine = poleLength
end
if climbingThePole then -- update the position on the pole
AlignPosition.Position = poleBottom + (poleTop - poleBottom).Unit * distanceOnLine
end
end
This is not the full code of course. For demonstration purposes I cut out a lot of stuff like mobile and gamepad controls, animation updates and so on. I believe you would be able to add those yourself. Obviously you will also want to make sure this code is all ran on the client, not on the server.
Thanks for the code, however how would I assemble all this to work for my game? Not sure if it’s because you cut out a lot of stuff but many variables are missing that I can’t make out and it’s pretty hard trying to piece it together considering most of it is just functions. Could you show me some more parts that could help me piece this together? Or is that too much to ask for?
Instead of explaining everything step for step I think it’s easier for us both if I give you an example place file. I tied together the things I said above into a place file with some extra code to make it all work. There’s a LocalScript inside the StarterCharacterScripts that makes the player climb any pipes they touch.
I tried to keep it very basic, but despite that there’s still a lot of code in there. I made sure to add comments to help you understand the ideas behind the code. A small caveat: This only works for pipes that are upright. If they are angled your character won’t rotate correctly. This also only supports keyboard controls as adding gamepad & mobile would almost double the amount of code.
Thanks a lot! I was able to make it behave how I want it to, but I still need help making it so the player always gets positioned in front of the pipe and not where ever they touched the pipe. By the way, I’m using normal (block) parts for the pipes, sorry I didn’t clarify that earlier.