Recently I’ve been scripting movement system for my game. The script is as follows:
---- Player related variables
local player = game.Players.LocalPlayer
local character = player.CharacterAdded:Wait()
local humanoid: Humanoid = character.Humanoid
local humanoidRootPart: BasePart = character.HumanoidRootPart
local animator = humanoid.Animator
---- Movement system related variables
local isClimbing = false
local climbSpeed = 9
local climbAttachment
local climbAlignPosition
local climbAnimation = Instance.new("Animation")
climbAnimation.AnimationId = "rbxassetid://88696739277660"
local climbAnimationTrack = animator:LoadAnimation(climbAnimation)
local isRunning = false
local doubleTapCheck
local walkSpeed = game.StarterPlayer.CharacterWalkSpeed
local runSpeed = 16
---- Functions
local function stopClimbing()
humanoid.AutoRotate = true
climbAlignPosition:Destroy()
climbAttachment:Destroy()
climbAnimationTrack:Stop()
isClimbing = false
end
local function getWallInFront()
local raycastOrigin = humanoidRootPart.Position
local raycastDirection = humanoidRootPart.CFrame.LookVector * 1.5
return workspace:Raycast(raycastOrigin, raycastDirection)
end
local function tryClimb(wall)
if not wall then return end
-- Setup character position and orientation
humanoidRootPart.CFrame = CFrame.new(wall.Position + (wall.Normal // 1), wall.Position)
humanoid.AutoRotate = false
-- Setup forces for climbing
climbAttachment = Instance.new("Attachment")
climbAttachment.Parent = humanoidRootPart
climbAlignPosition = Instance.new("AlignPosition")
climbAlignPosition.Parent = humanoidRootPart
climbAlignPosition.Mode = Enum.PositionAlignmentMode.OneAttachment
climbAlignPosition.RigidityEnabled = true
climbAlignPosition.Attachment0 = climbAttachment
climbAlignPosition.Position = humanoidRootPart.CFrame.Position
climbAnimationTrack:Play()
isClimbing = true
end
local function onInputBegan(input, gameProcessedEvent)
if gameProcessedEvent then return end
if input.KeyCode == Enum.KeyCode.LeftControl then
if not isClimbing then
local wall = getWallInFront()
tryClimb(wall)
elseif isClimbing then stopClimbing() end
end
end
local function onHeartbeat(deltaTime)
if not isClimbing then return end -- Check if currently climbing
local wall = getWallInFront()
if not wall then stopClimbing() end -- Check if there's a wall to climb
-- Calculate climbing movement for character
local xMovement = humanoidRootPart.CFrame.RightVector.X * deltaTime * (
(game.UserInputService:IsKeyDown(Enum.KeyCode.A) and -climbSpeed or 0) +
(game.UserInputService:IsKeyDown(Enum.KeyCode.D) and climbSpeed or 0)
)
local yMovement = deltaTime * (
(game.UserInputService:IsKeyDown(Enum.KeyCode.W) and climbSpeed or 0) +
(game.UserInputService:IsKeyDown(Enum.KeyCode.S) and -climbSpeed or 0)
)
local zMovement = humanoidRootPart.CFrame.RightVector.Z * deltaTime * (
(game.UserInputService:IsKeyDown(Enum.KeyCode.A) and -climbSpeed or 0) +
(game.UserInputService:IsKeyDown(Enum.KeyCode.D) and climbSpeed or 0)
)
climbAlignPosition.Position += Vector3.new(xMovement, yMovement, zMovement)
end
---- Init
game.UserInputService.InputBegan:Connect(onInputBegan)
game["Run Service"].Heartbeat:Connect(onHeartbeat)
The current implementation behaves as expected except for one oddity (which I believe has something to do with not disabling default character controls when climbing) - as you attempt to move to right or left while climbing, the character proceeds to sink into the wall.
Video demonstration:
I’ve tried enabling PlatformStanding/Physics, which ended up partially fixing the issue, but alas introducing another one - the character would wiggle uncontrollably in accordance to physics and everything would eventually break.
Now, my questions for this topic - what are the possible solutions to fixing this issue using my current approach? Are there actually any other objectively better approaches that I’m not aware of?
Please enlighten me and thank you for your time spent reading this post.
I believe what’s happening is the humanoids ground-movement code is still running every physics step:
Pressing A / D sets Humanoid.MoveDirection sideways in local space
Since you’ve rotated the RootPart to face the wall, that sideways vector points into the wall’s normal
The humanoid applies an internal impulse to reach MoveDirection * WalkSpeed each frame; the wall pushes back, so the solver compromises and your root slowly tunnels inward
Try setting the humanoid state to humanoid:ChangeState(Enum.HumanoidStateType.Physics), this should remove the walk forces
Unfortunately, doesn’t help, as I mentioned that I have anticipated some inner engine workings and tried to resolve them by doing exactly what you suggested:
On your original recording (before doing anything with PlatformStanding/Physics), is the character phasing into the wall? Like as in, if you kept holding D to go to the right – would you phase through the wall or would it just bug you out?
After some testing with your original code, I found that the .CFrame code sometimes orients the character the wrong way (and sometimes triggers an automatic wall-climbing cancel). Instead of
I assume by “phasing through the wall” you mean completely go into it, in which case - no, it does not happen. The character only has it’s arms and legs submerged and generally positions much closer to the wall, but doesn’t straight up go through it.
I also tried your suggestion, and it seems to have no impact on the problematic behaviour described previously in my post.
Since the time of OP, I have also discovered another abnormality - for whatever reason, when holding W while climbing, the character rotates specifically in the right direction, it’s especially easy to observe when disabling climb animation.
Hope this helps thinking of a solution. If there’s anything you don’t understand about my code, please let me know and thanks for sticking with me on this one!
I think your problem is that you are using humanoidRootPart.CFrame.RightVector (and other hrp direction vectors) which will give you slightly inaccurate results if the root part moves in some unexpected way, that then add up overtime giving you the weird sinking behavior.
Try saving your initial wall CFrame that you set the character to when it first gets on the wall, and use that CFrame’s direction vectors instead.
And the slow rotating as you are only moving up could be simply because align position does not align orientation. You probably need an align orientation as well.
Edit: Now that I think about it, the crux of the issue is probably what I just said, the character is not being aligned to any orientation so it can drift slightly which creates these weird effects.
I lowkey don’t know what’s happening here – would you be willing to send your place file? Because I can’t seem to replicate your issues, or I probably missed something.
Quick notice - I tested your solution, and it indeed worked.
I will mark your post as solution for now until @Downrest comes up with solution of their own and if it turns out to be more elegant I’ll transfer the solution award to them
Edit: …upon further testing, I found out this also introduced yet another bug, which I assume is fixable by introducing more raycasts, but I’d rather not add layers upon layers of “crutches” if possible, so definitely will wait for Downrest to respond.