Hello! I want to make a wall jump/wall slide system that is like super blox 64’s one. I’ve already made the detection for when a player is going to start wall sliding, but I ran into a lot of problems when trying to actually make the wall slide.
First, the wall slide is supposed to slow the player’s fall, but when I tried using different kinds of forces (vectorforce, linearvelocity, i want to try avoiding the deprecated forces), they all ended up looking very unnatural and weird. Either the player still accelerates too fast because of gravity or the player slides down way too slow. I wanted to achieve a slow down that looks like super blox 64’s one.
Second, I have no idea whats the best way to lock the player’s position. When wall sliding, the player’s movement should be limited, where they can’t walk towards and away from the wall, instead they can only move left and right parallel to the wall. I thought of setting the player’s walkspeed to 0 and manually updating the player’s position every frame, but it’s way too complicated for my smol brain and I think it will look very buggy. I’ve also tried using alignorientation and alignposition to limit the player’s movement, but it didn’t work out.
How can I make the player slow down falling naturally and limit their movement? Sorry im not that good at handling physics related things yea and making a wall jump system is unbelievably complicated I know that many people have asked about making such systems, but the posts that I have seen don’t really solve these questions in detail
Sorry I don’t have video footage of super blox 64’s mechanics, at least for me roblox only allows me to upload a video that is 5 seconds long due to file size, and I have no idea how to use external videos.
Mb for all the yap, its just that theres a lot that i’m confused about
local function OnRenderStepped(Delta)
if CharState == "WallGrip" then -- When player is in a state of sliding down the wall
local HumanoidState = Humanoid:GetState()
if HumanoidState == Enum.HumanoidStateType.Landed then
-- Player has landed on ground, stop wall sliding
CharState = nil
ResetWallJump() -- Just handles stopping the wall slide
else
-- Some code that allows the player to stop wall sliding if they move away from the wall, not importaant
local MoveDirection = Humanoid.MoveDirection
local LookVector = HumanoidRootPart.CFrame.LookVector
local Dot = LookVector:Dot(MoveDirection)
local Angle = math.acos(Dot)
if Angle > math.rad(120) then
CharState = nil
ResetWallJump()
else
UserSettings():GetService("UserGameSettings").RotationType = Enum.RotationType.MovementRelative
end
end
else
-- Checks if player is going to wall slide
local HumanoidState = Humanoid:GetState()
if HumanoidState == Enum.HumanoidStateType.Jumping or HumanoidState == Enum.HumanoidStateType.Freefall then
WallRaycastParams.FilterDescendantsInstances = CollectionService:GetTagged("Grippable") -- Grippable is a tag for all the walls
local WallCheckResult = workspace:Raycast(HumanoidRootPart.Position, HumanoidRootPart.CFrame.LookVector * 3, WallRaycastParams) -- Checks for any walls in front of the HRP
if WallCheckResult and WallCheckResult.Instance and WallCheckResult.Instance ~= LastWall then -- Last wall isn't too important
local MoveDirection = Humanoid.MoveDirection
local Angle = math.acos(HumanoidRootPart.CFrame.LookVector:Dot(MoveDirection))
if Angle <= math.rad(60) then -- Not important, just checks the angle the player is facing the wall
CharState = "WallGrip"
LastWall = WallCheckResult.Instance
WallCount += 1 -- Also not important
HumanoidRootPart.CFrame = CFrame.lookAt(WallCheckResult.Position + (WallCheckResult.Normal * 1), WallCheckResult.Position)
UserSettings():GetService("UserGameSettings").RotationType = Enum.RotationType.MovementRelative
Humanoid.WalkSpeed = 0 -- Limits player movement
HumanoidRootPart.AssemblyLinearVelocity = Vector3.zero
end
end
end
end
end
This is my code so far, sorry for being a little messy The current system, after checking if the player is ready to start wall sliding, will set the player’s walkspeed to 0 so that they can’t move towards or away from the wall.
Might not be the best way of handling the limitation of movement though, what I want to know mainly is how to handle the player falling down slowly and the player being unable to move towards or away from the wall, but can move to the left and right parallel to the wall. While being smooth.
If you’re comfortable with the idea, using LinearVelocity and making your own movement would definitely work well.
You could give the LinearVelocity’s VectorVelocity the HumanoidRootPart’s AssemblyLinearVelocity when you enter the wall slide state, and then you could use the normal of the wall you’re sliding on to tell the velocity where to go along the wall. You could achieve that using the Cross product of the normal of the wall and a unit vector along the Y axis. You could also use the Humanoid’s MoveDirection property to give influence to how fast or slow you go along the wall, as the MoveDirection vector is camera-relative.
Also, if you’re really going for super blox 64 style movement, I suggest implementing a cap on how fast you can slide down the wall vertically.
As for exiting the state and jumping out, you could just disable the LinearVelocity and then change the Humanoid’s state to Jumping afterward.
You could do all of this without LinearVelocity and directly edit the AssemblyLinearVelocity of the HumanoidRootPart of course, but I suggest using LinearVelocity as it makes custom movement like this much easier to work with.
err im confused sorry theres a lot of information going in mb
what is this linearvelocity used for? im not sure if the linearvelocity you mentioned is for slowing the player’s fall, or limiting the player’s movement
is this supposed to achieve the moving of the player along the wall? im not sure what this is trying to say?
sorry for asking a lot of questions, but is it ok if you explain what cross does, im still not very familiar with cframes and vector3s
what is this linearvelocity used for? im not sure if the linearvelocity you mentioned is for slowing the player’s fall, or limiting the player’s movement
The LinearVelocity is used for movement regarding horizontal and vertical movement (X, Y, and Z components of the Vector). You can use LinearVelocity for everything I said in there.
If you set the VelocityConstraintMode of the LinearVelocity to Vector, it lets you represent your Velocity in the LinearVelocity as a Vector3. That will allow you to edit all axes of movement the same way you would the AssemblyLinearVelocity of the HumanoidRootPart.
is this supposed to achieve the moving of the player along the wall? im not sure what this is trying to say?
Yes, you can use the normal returned by the raycast result you got from the wall detection to limit the player’s movement along the wall.
sorry for asking a lot of questions, but is it ok if you explain what cross does, im still not very familiar with cframes and vector3s
The Cross product of two vectors is simply getting a vector that is perpendicular of two other vectors. Let’s say you have two pencils on your desk. One faces forward, one faces to the right. If you had a third pencil that represented the cross product of those two pencils, the cross product pencil would point upwards, as its perpendicular to the two pencils.
That isn’t the best explanation of Cross product by a long shot, so I suggest you look into vector maths, as they are very essential for coding movement mechanics in game development.
Ah ok I mostly get it now, so the linearvelocity handles both the slowing down of the player’s fall as well as moving against the wall. But if Cross returns the perpendicular vector3, then doesnt that mean that there are 2 possible vector3s that it could return? Which one would it return, and how would I allow the player to move along the wall accordingly?
-- Before this code, make sure the LinearVelocity's VectorVelocity is the same
-- as the AssemblyLinearVelocity of the HumanoidRootPart.
-- Assuming raycastResult is what you got from the wall detection raycast
-- Make sure this runs every frame
game:GetService("RunService").Heartbeat:Connect(function(deltaTime)
local moveVector: Vector3 = Humanoid.MoveDirection
local currentHrpVelocity: Vector3 = HumanoidRootPart.AssemblyLinearVelocity
local yVel: number = currentHrpVelocity.Y
local finalY: number
-- Get the Cross product of the raycast Normal and the global Y axis vector
-- and then make it a Unit vector that way it gives you the direction perpendicular of the wall
local wallDirection: Vector3 = raycastResult.Normal:Cross(Vector3.yAxis).Unit
-- Reverse the wall direction if the MoveDirection isn't aligned with
-- wallDirection. Dot product tells us how 'aligned' two vectors are.
-- It tells us as follows;
-- 1 if the two vectors are perfectly aligned, 0 if they are perpendicular,
-- and -1 if they are facing opposite from each other.
if moveVector:Dot(wallDirection) < 0 then
wallDirection = -wallDirection
end
-- Here is some psuedo falling movement, I suggest writing your own
-- falling code.
local fallFactor: number = deltaTime * 100
if yVel > 0 then
-- going upward
finalY = yVel - (deltaTime * 100)
else
-- going downward, we clamp the downwards velocity that way we dont fall normally
finalY = math.clamp(yVel - (deltaTime * 50), -50, yVel)
end
-- Flatten the VectorVelocity and then get the size (Magnitude) of the speed, that way
-- we can multiply with wallDirection for an accurate representation of the
-- speed we want to go at.
local flatSpeed: number = Vector3.new(LinearVelocity.VectorVelocity.X, 0, LinearVelocity.VectorVelocity.Z).Magnitude
-- Final velocity assignment, combining everything we have to make everything work.
LinearVelocity.VectorVelocity = (wallDirection * flatSpeed) + Vector3.new(0, finalY, 0)
end
I tried to rewrite and change the code based on what I understand from your code, based on what I need. The wall slide works now (yippee ) but it looks very janky and unsmooth. Theres no momentum, as in the player’s downward velocity immediately changes to a constant, and when the player changes their direction of sliding along the wall, they just immediately switch direction instead of having a brief “slow down” before the direction changes. How can I make it more smooth?
if CharState == "WallSlide" then -- When player is in a state of sliding down the wall
local HumanoidState = Humanoid:GetState()
if HumanoidState == Enum.HumanoidStateType.Landed then
-- Player has landed on ground, stop wall sliding
CharState = nil
ResetWallJump() -- Just handles stopping the wall slide
else
local MoveDirection = Humanoid.MoveDirection
local WallNormal = HumanoidRootPart.CFrame.LookVector -- Direction facing from player towards the wall
local NormalDot = WallNormal:Dot(MoveDirection) -- Not important for now
local Angle = math.acos(NormalDot) -- Also not important
if Angle > math.rad(130) then
CharState = nil
ResetWallJump()
else
local SlideDirection = WallNormal:Cross(Vector3.yAxis).Unit -- Direction you slide along the wall
local SlideDot = SlideDirection:Dot(MoveDirection) -- How similar the player's movedirection is to the current slide direction
if SlideDot < 0 then
SlideDirection = -SlideDirection
SlideDot = -SlideDot
elseif SlideDot == 0 then
SlideDirection = Vector3.zero
end
WallSlideForce.VectorVelocity = (SlideDirection * 30 * SlideDot) + Vector3.new(0, -15, 0)
UserSettings():GetService("UserGameSettings").RotationType = Enum.RotationType.MovementRelative
end
end
You should look into linear interpolation (AKA “Lerp”) or other interpolation methods to suit your needs.
The reason why everything feels so rigid is because you’re setting the velocity to a constant. When you set your velocity to a constant, the speed will instantly go to said constant.
I suggest saving the HumanoidRootPart’s AssemblyLinearVelocity into a variable before initiating the wall slide, and inside of the wallslide update loop, make the targetVelocity (the velocity you assign the linearvelocity’s vectorvelocity) interpolate from the AssemblyLinearVelocity variable to your desired speed. Be sure to update the variable that you assigned to the HumaoidRootPart’s AssemblyLinearVelocity in the update loop, that way you aren’t interpolating from a static value.
Here is an example,
-- Before the slide loop
local previousVelocity: Vector3 = HumanoidRootPart.AssemblyLinearVelocity
-- This represents how quickly the character reaches the desiredVelocity
local interpolationSpeed: number = 10
-- In the assignment section of the slide loop
local desiredVelocity: Vector3 = (SlideDirection * 30 * SlideDot) + Vector3.new(0, -15, 0)
-- Instead of static lerping, I use Exponential interpolation for framerate consistency
-- Be cautious about using Lerp in loops that are bound to framerate, as Lerp can be
-- inconsistent across different framerates if not handled properly
local alpha: number = 1 - math.exp(-interpolationSpeed * deltaTime)
local targetVelocity: Vector3 = previousVelocity:Lerp(desiredVelocity, alpha)
-- Update previousVelocity, that way we aren't lerping from a static value
previousVelocity = targetVelocity
WallSlideForce.VectorVelocity = targetVelocity