would you mind if you explain how this works? I don’t understand how most things work on this system.
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
is there a way to check where the edge is, and where the part’s edge ends?
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.
would this be a valid way?
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?
I attempted to do a part to part
Uh no I think you misunderstood, lemme explain…
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
so like this…?
I mean you tell me did it work… when I gave you the example I didn’t have this in mind but what ever floats your boat man
I fixed a few bugs, it worked, kind of… how would i use the bodygyro to face the current part?
video of my current version vvv
robloxapp-20210703-0832469.wmv (4.6 MB)
function onClimb()
local origin = rootpart.Position
local direction = rootpart.CFrame.LookVector
local result = workspace:Raycast(origin, direction, params)
if result then
climbing=true
script.RemoteEvent:FireServer(char.ForceShiftLock, 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 then
LedgeClimb()
climbing=false
offClimb()
local ve = Instance.new("BodyVelocity")
ve.MaxForce = Vector3.new(1, 1, 1) * 40000
ve.Velocity = rootpart.CFrame.lookVector * 10 + Vector3.new(0, 7, 0)
ve.Parent = rootpart
game.Debris:AddItem(ve, 0.5)
game.TweenService:Create(ve, TweenInfo.new(0.5, Enum.EasingStyle.Sine), {
Velocity = rootpart.CFrame.lookVector * 10 + Vector3.new(0, 12, 0)
}):Play()
end
local sideOrigin = rootpart.CFrame*CFrame.new(0, -0.4, -1).Position
local sideDirection = rootpart.CFrame.RightVector*(down("D") and -2 or 2)
local currentDown = nil
local ifThatHit = nil
if currentDown == down("D") then
local hit = workspace:Raycast(leftArm.CFrame*CFrame.new(-3,0,0).Position,leftArm.CFrame.RightVector*-2,params)
if hit then
ifThatHit = true
end
elseif currentDown == down("A") then
local hit = workspace:Raycast(rightArm.CFrame*CFrame.new(3,0,0).Position,rightArm.CFrame.RightVector*2,params)
if hit then
ifThatHit = true
end
else
ifThatHit = false
end
local hit = workspace:Raycast(sideOrigin, sideDirection, params)
if hit and (down("D") or down("A")) then -- right or left button is down
if down("D") then
currentDown = down("D")
elseif down("A") then
currentDown = down("A")
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)
elseif ifThatHit == true 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
elseif hit and not (down("D") and down("A")) then
ifThatHit = false
end
until (not climbing)
script.RemoteEvent:FireServer(char.ForceShiftLock, false)
hum.AutoRotate=true
hum.PlatformStand=false
end
end
``` I just rewrote the logic, to try to fix the orientation issue, Im pretty confused how i got the logic wrong, could you maybe explain how the logic would work?
i finally got it to work, but it’s a bit glitchy, how would i fix this? the code for the part to part is this:
local hit1 = workspace:Raycast(sideOrigin, sideDirection, params)
local hit2 = nil
local debounceThing = false
if down("D") or down("A") then
if down("D") then
local hit2 = workspace:Raycast(rootpart.CFrame.Position,rootpart.CFrame.RightVector*2,params)
if hit2 and debounceThing == false then
debounceThing = true
print("D hit")
face=CFrame.new(hit2.Position+hit2.Normal, hit2.Position)
wait(0.2)
debounceThing = false
end
elseif down("A") then
local hit2 = workspace:Raycast(rootpart.CFrame.Position,rootpart.CFrame.RightVector*-2,params)
if hit2 and debounceThing == false then
debounceThing = true
print("A hit")
face=CFrame.new(hit2.Position+hit2.Normal, hit2.Position)
wait(0.4)
debounceThing = false
end
end
end
I apologize for the necrobump but recently I’ve been going through my old code as I didn’t work on anything or contributed for a while and well I came across this and I noticed how it uses deprecated bodymovers but what makes it even worse is the fact that it fits nowhere in the task scheduler so yeah that’s updated but please lemme clarify one more time:
PLEASE DONT USE THE SYSTEM RAW PUT IT UNDER SPECIFIED CONTROLS
Oh yeah the silly lil rapid side switch bug has been fixed too lol
as for questions or help just reply I’ll make sure to return next year not two years later!
Could you release a version where you can climb from side to side/part to part? I have made wall climbing but I’m not understanding how ill incorporate side to side/part to part climbing too.
when you find a climbable wall simply keep raycasting to the humanoidrootparts rightvector and -rightvector if if finds something then you just set it to the new found wall
This isn’t useful if you need to check around corners, there’s a few ways to check around corners I believe but if I were to do it I’d just raycast out the rightvector then forward then left and do the inverse for the other side but there’s probably a easier way.
I was looking around the code and noticed that at one part you said that it could be converted to work to climb all surfaces of a part. Could you explain how it can be modified or maybe simply give the code along with a minor explanation?
You’d have to remove the Y limitations that I put on, then shoot rays that hit the bottom and top by shooting a ray slightly in-front and down? maybe up too, I don’t think you have to do super complex ray casting because of Vector3.FromNormalId
Can’t believe I had to edit this so many times, when brain with no food.