Blokav, your way of doing Wall Climbing is frankly unusable for a game where the map changes numerous times and unrelated to the Rogue Lineage Climbing System
Since Blokav’s System was unusable for my situation I followed Jimmy’s Steps as seen above to the best of my abilities this is the result:
I have a few bugs to fix after that I’ll be releasing the system to the public.
I apologize for almost taking a week on this I kind of forgot about it please note that this isn’t a “complete” climbing system since it doesn’t let you climb from part to part, I hope the code is understandable enough though oh and have fun!
I don’t mind the flaws, but thanks for sharing the source to allow more understanding for everyone about wall climbing. I believe others will also improve from the roots of this system.
Thank you for the kind words.
My reasoning behind not releasing a “complete” climbing system is because I’m afraid that people will just get lazy with it and not change anything outside of that I don’t know if my code is ready yet for open sourcing I wanna see the criticism on that.
He said there were flaws and by no means perfect, If you read the opening replies you can see this sort of issue was addressed and a fix was given.
You can incorporate this to your own version…
If I were you I would just take implementation ideas instead of “changing” the code since I personally think it leads to a better code structure everytime.
Well in honesty I don’t use this, I’ve done a different approach for my wall crawling where player wont be able to move from one face of a part to another.
But from how I know this works is that initially the player is mounted onto the wall using BodyVelocity and BodyGyro, this allows the player to face the part and the velocity allows the movement as you see in the script. Now how to move from one face of a part to another is actually simple… we cast a ray from inside the part by offseting the raycast origin by (0,0,-1) from rootpart, which is also shown in the script, using rightVector we can get the direction and cast a ray from within the part. What’s cool is raycasts don’t detect by part, but the surface they hit. So we can get the hit.Normal and hit.Position to turn the body using BodyGyro towards that position. The bug you are having is because of physics in general, where your script doesn’t take into consideration your velocity to turn you at the right time around the corner. I would tamper with the raycast origin, adujusting it in relation to your velocity.
Hope that gave some insight. (I’m making a rogue-like too )
Uhm, I have 0 idea how raycasting works so all i have right now is this (yes, i used the open source):
--------------------------------------------------------------------------------
-------------------------------------------------------------- Service Shortcuts
--------------------------------------------------------------------------------
local cas = game:GetService("ContextActionService")
local ppl = game:GetService("Players")
local run = game:GetService("RunService")
local uis = game:GetService("UserInputService")
--------------------------------------------------------------------------------
--------------------------------------------------------------- Player Shortcuts
--------------------------------------------------------------------------------
local plr = ppl.LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local hum = char:WaitForChild("Humanoid")
local rootpart = char:WaitForChild("HumanoidRootPart")
--------------------------------------------------------------------------------
---------------------------------------------------------- Global Raycast Params
--------------------------------------------------------------------------------
local params = RaycastParams.new()
params.CollisionGroup = ("Climbable")
params.FilterDescendantsInstances = {char}
params.FilterType = Enum.RaycastFilterType.Blacklist
params.IgnoreWater = true
--------------------------------------------------------------------------------
----------------------------------------------------------- Climbing Body Movers
--------------------------------------------------------------------------------
local climbMove = Instance.new("BodyVelocity")
climbMove.MaxForce = Vector3.new(1,1,1)*math.huge
climbMove.P = math.huge
climbMove.Velocity = Vector3.new()
climbMove.Name = ("Climb Speed")
--------------------------------------------------------------------------------
local climbGyro = Instance.new("BodyGyro")
climbGyro.CFrame = CFrame.new()
climbGyro.D = 100
climbGyro.MaxTorque = Vector3.new(1,1,1)*math.huge
climbGyro.P = 3000
--------------------------------------------------------------------------------
------------------------------------------------------------- Climbing Variables
--------------------------------------------------------------------------------
local climbing = false
local face = nil
--------------------------------------------------------------------------------
------------------------------------------------------------- Climbing Functions
--------------------------------------------------------------------------------
function down(KEY) return uis:IsKeyDown(Enum.KeyCode[KEY]) end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
function offClimb()
climbing=false
climbGyro.Parent=nil
climbMove.Parent=nil
face=nil
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
function onClimb()
local origin = rootpart.Position
local direction = rootpart.CFrame.LookVector
local result = workspace:Raycast(origin, direction, params)
if result then
climbing=true
rootpart.CFrame = CFrame.new(rootpart.CFrame.p, Vector3.new(rootpart.Position.X - result.Normal.X, rootpart.Position.Y, rootpart.Position.Z - result.Normal.Z))
face = CFrame.new(result.Position+result.Normal, result.Position)
hum.AutoRotate=false
hum.PlatformStand=true
climbMove.Parent=rootpart
climbGyro.Parent=rootpart
repeat
run.RenderStepped:Wait()
climbGyro.CFrame=face or CFrame.new()
--I'm bad with math so I just used this quick solution, sorry to dissapoint.
if rootpart.Position.Y > result.Instance.Size.Y+3 then
climbing=false
offClimb()
end
--if statements make code look spaghetti, so I used the logical operator: OR
local sideOrigin = rootpart.CFrame*CFrame.new(0, 0, -1).Position
local sideDirection = rootpart.CFrame.RightVector*(down("D") and -2 or 2)
local hit = workspace:Raycast(sideOrigin, sideDirection, params)
if hit and (down("D") or down("A")) then
face=CFrame.new(hit.Position+hit.Normal, hit.Position)
end
until (not climbing)
hum.AutoRotate=true
hum.PlatformStand=false
end
end
--------------------------------------------------------------------------------
----------------------------------------------------------------- Movement Logic
--------------------------------------------------------------------------------
local actions = {0;0; 0;0;}
local buttons = {"A";"D";"S";"W"}
local lastTime
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
uis.InputBegan:Connect(function(obj)
local input = tostring(obj.KeyCode):split('.')[3]
local key = table.find(buttons, input)
if key then
actions[key]=1
end
--I was lazy to script double tap space
if input == "Space" then
local now = tick()
local difference = (now - lastTime)
if difference <= 0.2 then
if (not climbing) then
onClimb()
else
offClimb()
end
wait(2)
end
lastTime = tick()
end
end)
--------------------------------------------------------------------------------
uis.InputEnded:Connect(function(obj)
local input = tostring(obj.KeyCode):split('.')[3]
local key = table.find(buttons, input)
if key then
actions[key]=0
end
end)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
run.RenderStepped:Connect(function()
local strafe = actions[2] - actions[1]
local surge = actions[3] - actions[4]
climbMove.Velocity=rootpart.CFrame.RightVector*(strafe*8)+Vector3.new(0, surge*-8, 0)
end)
--------------------------------------------------------------------------------
---------------------------------------- Hopefully that was a fun and clean read
--------------------- I know that this is glitchy but that's somewhat on purpose
--------------- This is meant to be a introduction to a climbing system, bt dubs
--------------------------------------------------------------------------------
the only thing i did so far was change L to climb to double tap spacebar
Well, matter of fact I don’t think you need to find the edge what should be used in contrast with the current system is maybe AlignPosition | Roblox Creator Documentation, this should prevent the gap between the player and would be a much efficient fix as opposed to finding the sides of a part because you have to include the rotation of a part which is a bunch of math I don’t think worth diving into.
Compared to what @Blend_it has created to find the edge and have the player adjust to the next side I think this much more viable to create a revamp of the original.
All you would have to do is create a attachment for the rootpart and attachment for the raycast hit.Normal, Give these into a AlignPosition instance and I think it should work… I haven’t tested but the logic makes sense.
(Suggest you have a look into raycasting before trying to do this, every roblox dev must know how raycasting works)
Here I felt a little kind today, I quote on quote “assisted” the open source code so that the bug doesn’t occur…
--------------------------------------------------------------------------------
-------------------------------------------------------------- Service Shortcuts
--------------------------------------------------------------------------------
local cas = game:GetService("ContextActionService")
local ppl = game:GetService("Players")
local run = game:GetService("RunService")
local uis = game:GetService("UserInputService")
--------------------------------------------------------------------------------
--------------------------------------------------------------- Player Shortcuts
--------------------------------------------------------------------------------
local plr = ppl.LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local hum = char:WaitForChild("Humanoid")
local rootpart = char:WaitForChild("HumanoidRootPart")
--------------------------------------------------------------------------------
---------------------------------------------------------- Global Raycast Params
--------------------------------------------------------------------------------
local params = RaycastParams.new()
params.CollisionGroup = ("Climbable")
params.FilterDescendantsInstances = {char}
params.FilterType = Enum.RaycastFilterType.Blacklist
params.IgnoreWater = true
--------------------------------------------------------------------------------
----------------------------------------------------------- Climbing Body Movers
--------------------------------------------------------------------------------
local climbMove = Instance.new("BodyVelocity")
climbMove.MaxForce = Vector3.new(1,1,1)*math.huge
climbMove.P = math.huge
climbMove.Velocity = Vector3.new()
climbMove.Name = ("Climb Speed")
--------------------------------------------------------------------------------
local climbGyro = Instance.new("BodyGyro")
climbGyro.CFrame = CFrame.new()
climbGyro.D = 100
climbGyro.MaxTorque = Vector3.new(1,1,1)*math.huge
climbGyro.P = 3000
--------------------------------------------------------------------------------
local alignPos = Instance.new("AlignPosition",rootpart)
local attachment0 = Instance.new("Attachment",rootpart)
attachment0.CFrame = CFrame.new(0,0,-2)
alignPos.Attachment0 = attachment0
alignPos.MaxForce = Vector3.new(1,1,1)*math.huge
alignPos.MaxVelocity = Vector3.new(1,1,1)*math.huge
alignPos.Responsiveness = 20
alignPos.ApplyAtCenterOfMass = true
alignPos.ReactionForceEnabled = true
alignPos.RigidityEnabled = true
--------------------------------------------------------------------------------
------------------------------------------------------------- Climbing Variables
--------------------------------------------------------------------------------
local climbing = false
local face = nil
--------------------------------------------------------------------------------
------------------------------------------------------------- Climbing Functions
--------------------------------------------------------------------------------
function down(KEY) return uis:IsKeyDown(Enum.KeyCode[KEY]) end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
function offClimb()
climbing=false
climbGyro.Parent=nil
climbMove.Parent=nil
face=nil
end
function onClimb()
local origin = rootpart.Position
local direction = rootpart.CFrame.LookVector
local result = workspace:Raycast(origin, direction, params)
if result then
climbing=true
rootpart.CFrame = CFrame.new(rootpart.CFrame.p, Vector3.new(rootpart.Position.X - result.Normal.X, rootpart.Position.Y, rootpart.Position.Z - result.Normal.Z))
face = CFrame.new(result.Position+result.Normal, result.Position)
hum.AutoRotate=false
hum.PlatformStand=true
climbMove.Parent=rootpart
climbGyro.Parent=rootpart
repeat
run.RenderStepped:Wait()
climbGyro.CFrame=face or CFrame.new()
if rootpart.Position.Y > result.Instance.Size.Y+3 then
climbing=false
offClimb()
end
local sideOrigin = rootpart.CFrame*CFrame.new(0, -0.4, -1).Position
local sideDirection = rootpart.CFrame.RightVector*(down("D") and -2 or 2)
local hit = workspace:Raycast(sideOrigin, sideDirection, params)
if hit and (down("D") or down("A")) then
local attachment1 = Instance.new("Attachment",hit.Instance)
attachment1.WorldPosition = hit.Position
alignPos.Attachment1 = attachment1
face=CFrame.new(hit.Position+hit.Normal, hit.Position)
game.Debris:AddItem(attachment1,0.005)
end
until (not climbing)
hum.AutoRotate=true
hum.PlatformStand=false
end
end
local actions = {0;0; 0;0;}
local buttons = {"A";"D";"S";"W"}
local lastTime = tick()
uis.InputBegan:Connect(function(obj)
local input = tostring(obj.KeyCode):split('.')[3]
local key = table.find(buttons, input)
if key then
actions[key]=1
end
if input == "Space" then
local now = tick()
local difference = (now - lastTime)
if difference <= 0.2 then
if (not climbing) then
onClimb()
else
offClimb()
end
wait(2)
end
lastTime = tick()
end
end)
uis.InputEnded:Connect(function(obj)
local input = tostring(obj.KeyCode):split('.')[3]
local key = table.find(buttons, input)
if key then
actions[key]=0
end
end)
run.RenderStepped:Connect(function()
local strafe = actions[2] - actions[1]
local surge = actions[3] - actions[4]
climbMove.Velocity=rootpart.CFrame.RightVector*(strafe*8)+Vector3.new(0, surge*-8, 0)
end)
Note I altered the one you gave so there shouldn’t be any changes to keybinds…
I didn’t include part to part, you would have to do that yourself, the approach I would take to do something like this would be to constantly cast a ray from the rootpart and cast rays depending on which key is being pressed ( A or D ) to cast a ray from either the RightArm/LeftArm in their respective rightVector directions. You would then move from there, understanding how this works and altering it to your desire.
local origin = rootpart.Position
local direction = RightArm.CFrame.RightVector+RightArm.CFrame.LookVector
local result = workspace:Raycast(origin, direction, params)
local hit = workspace:Raycast(sideOrigin, sideDirection, params)
if hit and (down("D") or down("A")) then -- right or left button is down
local anotherDirection
local leftOrRight = nil
local anotherOrigin = rootpart.Position
if down("D") then
leftOrRight = 10
anotherDirection = leftArm.CFrame.RightVector * -1 +leftArm.CFrame.LookVector
elseif down("A") then
leftOrRight = -10
anotherDirection = rightArm.CFrame.RightVector+rightArm.CFrame.LookVector
end
local anotherClimbable = workspace:Raycast(anotherOrigin, anotherDirection, params)
local attachment1 = Instance.new("Attachment",hit.Instance)
attachment1.WorldPosition = hit.Position
alignPos.Attachment1 = attachment1
face=CFrame.new(hit.Position+hit.Normal, hit.Position)
game.Debris:AddItem(attachment1,0.005)
if anotherClimbable then
climbing=false
offClimb()
local ve = Instance.new("BodyVelocity")
ve.MaxForce = Vector3.new(1, 1, 1) * 40000
ve.Velocity = rootpart.CFrame.RightVector * leftOrRight
ve.Parent = rootpart
game.Debris:AddItem(ve, 0.5)
onClimb()
end
end
uhh, i have no idea what i did, and it’s not working… could you explain a bit more of what i should do to fix it?
What you would actually do is maybe casting a ray from each arm depending which direction the player is moving (left/right) this should be done using Right\Vector, reminder that you would need to offset the raycast origin to so that the ray wouldn’t detect the arm.
Example:
local rightarm = char["Right Arm"]
local hit = workspace:Raycast((rightarm.CFrame*CFrame.new(3,0,0).Position,rightarm,CFrame.RightVector*2,param)
if hit then
--// use face variable and the attachment code I made to adjust to the new surface
Here is how you would do a left arm
local leftarm = char["Left Arm"]
local hit = workspace:Raycast((leftarm.CFrame*CFrame.new(-3,0,0).Position,leftarm,CFrame.RightVector*-2,param)
if hit then
--// like rightarm
Now something you should include is casting a ray from the rootpart if the last touched part and the new part is not the same then use the alignposition code along the the climbGyro to adjust.
local hit = workspace:Raycast(sideOrigin, sideDirection, params)
if hit and (down("D") or down("A")) then -- right or left button is down
local ifThatHit = false
if down("D") then
local hit = workspace:Raycast((leftarm.CFrame*CFrame.new(-3,0,0).Position,leftarm,CFrame.RightVector*-2,param)
if hit then
ifThatHit = true
local attachment1 = Instance.new("Attachment",hit.Instance)
attachment1.WorldPosition = hit.Position
alignPos.Attachment1 = attachment1
face=CFrame.new(hit.Position+hit.Normal, hit.Position)
game.Debris:AddItem(attachment1,0.005)
end
elseif down("A") then
leftOrRight = -10
local hit = workspace:Raycast((rightarm.CFrame*CFrame.new(3,0,0).Position,rightarm,CFrame.RightVector*2,param)
if hit then
ifThatHit = true
local attachment1 = Instance.new("Attachment",hit.Instance)
attachment1.WorldPosition = hit.Position
alignPos.Attachment1 = attachment1
face=CFrame.new(hit.Position+hit.Normal, hit.Position)
game.Debris:AddItem(attachment1,0.005)
end
end
if ifThatHit == false then
local attachment1 = Instance.new("Attachment",hit.Instance)
attachment1.WorldPosition = hit.Position
alignPos.Attachment1 = attachment1
face=CFrame.new(hit.Position+hit.Normal, hit.Position)
game.Debris:AddItem(attachment1,0.005)
end
end