How to prevent player getting stuck/glitching when pushing towards a wall?

I have a custom movement system that essentially overrides the player’s X and Z velocity in order to move. The movement is heavily based off of source engine movement. However the problem I’m getting is that when pushing towards a wall, the player gets stuck and doesn’t slide off naturally.

Video example:

This is the movement I am trying to replicate (Garry’s Mod):

As you can see, in the Garry’s Mod example, the character smoothly slides of the wall, and all the velocity pushing against the wall is negated. One solution I’ve tried is doing a raycast shooting out in the direction of the player movement and essentially reflecting the velocity, however it doesn’t work the way I intended it. (you can sort of see the ray cast in the video, it’s the red line).

Any help is appreciated.

8 Likes

Could you maybe show some code and what kind of bodymovers you’re using and for what?

I can’t show much, but here’s what I can show:

-- GetMoveVector: gets moveVector and wishVector and stores the values
local function GetMoveVector()
	-- starting move vector
	local newMoveVector = Vector3.zero

	-- check if the player is pressing any of the movement keys
	if util.GetInput(shared.keybindsMovement["forward"]) then
		newMoveVector += Vector3.new(0, 0, -1)
	end
	if util.GetInput(shared.keybindsMovement["left"]) then
		newMoveVector += Vector3.new(-1, 0, 0)
	end
	if util.GetInput(shared.keybindsMovement["back"]) then
		newMoveVector += Vector3.new(0, 0, 1)
	end
	if util.GetInput(shared.keybindsMovement["right"]) then
		newMoveVector += Vector3.new(1, 0, 0)
	end
	
	-- unit move vector 
	newMoveVector = newMoveVector.Unit

	-- checks nan 
	if newMoveVector ~= newMoveVector then
		newMoveVector = Vector3.zero
	end

	-- applys new vector to value object
	moveVector.Value = newMoveVector

	-- get vector relative to camera
	wishVector.Value = GetVectorRelativeToCamera(moveVector.Value)
end

TLDR, the script gets the raw move vector depending on the input, and then converts the vector to be relative to the character. That vector is then used to override the characters velocity.

Are you using vectorForce or linearVelocity for movements? And are the collisions custom too or are you overlaying that capsule and using the default collisions?

local MoveVectors = { -- Store Vector Types to use for Input
    ["foward"] = -Vector3.zAxis;
    ["back"]   = Vector3.zAxis;
    ["right"]  = Vector3.xAxis;
    ["left"]   = -Vector3.xAxis
}

-- when applying Data (MoveVector)


newMoveVector += MoveVectors[input]

Even easier, you can just get the direction from the playerModule (and it’s also mobile compatible by getting the direction of the trackpad)

local Players = game:GetService("Players")

local localPlayer = Players.LocalPlayer
local playerScripts = localPlayer:WaitForChild("PlayerScripts")
local playerModule = require(playerScripts.PlayerModule)

local newMoveVector = playerModule:GetControls():GetMoveVector()
print(newMoveVector )

It would be more difficult actually, due to all the calculations, and the obscurity of this method.

wym by all the calculations and obscurity of this method?

Calculations, as in it already does a lot, and it basically already has this information setup for you, plus, they are using a custom movement system, so it would be a bit different than the normal.

Obscurity in this context, I mean unknown, nobody would even know this exists.

I make custom movement systems all the time and it’s always compatible because it just listens to the corescripts movement input system. So it’s really efficient because all that math is already calculated for you anyways so you might aswell use it instead of rewriting code.

Also idk what you mean by nobody would even know this exist? Why is that an issue thing?

Are you using vectorForce or linearVelocity for movements?

I am overriding the characters velocity via HumanoidRootPart.AssemblyLinearVelocity = Vector3

I’ll also try the MoveVector methods you guys provided even though it doesn’t directly solve the issue I am having. It might help me in the long run anyways

Yeah, setting the characters velocity directly sometimes leads to physics bugs which could be like the one you’re experiencing. You could try replacing it with a LinearVelocity and see how that works out

I tried LinearVelocity, but the character still get stuck on walls. The character also moves very slowly on ground.

I was actually doing some research on this for almost whole year :melting_face:

Well, there is 2-3 ways of doing that right now…

I will talk only about first 2 methods as the third one is extremely complicated

1st Method will make you rewrite most of your current code, but PROBABLY will help you achieve your goal perfectly! [SLIGHTLY COMPLICATED AND PERFOMANCE EXPENSIVE!]

2nd Method is a little easier but idk i still don’t trust roblox physics yk :skull:

First method requires some knowledge of “AABB” Collisions

Sorry but bean shape will turn into a square, its just not possible without some extra math which im really bad at

Second method will require you to do some raycasting

What you want to do for the first method:

  1. Create 2-3 bounding boxes, First one will be player’s, Second will be “predicted bbox” [OPTIONAL], Third one will be used to find all parts for AABB checks (make third bbox a little larger or equal to second bbox position to keep it somewhat accurate)
  2. Find some AABB module on internet that returns volume vector, pretty sure there is plenty of them. (Don’t forget rotation!)
  3. Create a loop in which Second bbox moves in the direction of player’s velocity, Then call :GetPartBoundsInBox() OR :GetTouchingParts() on third bbox and iterate every part it returned, if AABB module returned some vector volume then just push the second bbox to the closest free space.
  4. that should be enough!
    Obviously i didn’t covered many small details here as it would made this reply even bigger :confused: but i hope it will give you somewhere to start from!
    and sorry for not giving code example it would take me way too long for this one

Now let’s talk about second method!

I just thought it would be easiest way for you right now so why not :slight_smile:

  1. Do multiple raycasts in the direction of player’s velocity, length should be ± same size as your bean
  2. From raycast results get raycast normals
  3. And just “push” away player in the direction of normal if you think he is way too close (dont forget to cancel/reduce/freeze the velocity or obviously its gonna look choppy)

I think thats it! Also i did small example for this one

--Dont mind the code btw i made it for a quick test =)
local hrp = script.Parent:WaitForChild("HumanoidRootPart")
local rp = RaycastParams.new()
rp.FilterType = Enum.RaycastFilterType.Blacklist
rp.FilterDescendantsInstances = {hrp.Parent}

game:GetService("RunService").RenderStepped:Connect(function()
	local rey = workspace:Raycast(hrp.Position, Vector3.new(0, 0, 3))
	if rey and rey.Instance and rey.Normal then
		local x, y, z = hrp.CFrame:ToEulerAnglesXYZ()
		hrp.CFrame = CFrame.new(rey.Position+(rey.Normal*3.001)) * CFrame.Angles(x, y, z)
		--I don't know what kind of velocity you are using so just cancel/freeze it somewhere around here idk
	end
end)

Let me know if i could help you!

btwbtw forgot to mention that for the most smoothest result just not let characters move in the direction of walls at all when they are too close
btwbtwbtw forgot to mention again that first method makes meshparts collisions not work at all

6 Likes

Thank you so much. I actually have already found a pretty good solution, which is pretty much how your second method works. I extremely appreciate your effort and time writing this for me, and can’t stress that enough.

If you wanna see how the system is now, its almost flawless:

As you can see, it clips the velocity but also retains the velocity perpendicular to the impacting wall.
Again, thank you for your help, even though I already had the solution. I’ll be marking you as the solution just in case anyone needs any help in the future.

1 Like

Great to hear that it works now!
By the way, could you provide some info about where you start raycasting from and what you do to keep velocity perpendicular to the impacting wall?
it would greatly improve my further research on this topic! :slight_smile:
Obviously you don’t have to if you don’t want to

Hi, I’d be happy to help!

Essentially, I first run:

local getParts = workspace:GetPartBoundsInRadius(origin, radius, overlapParams)

…every frame. As you can probably guess, the “origin” is the position of the player, the “radius” is how big the player is, and “overlapParams” blacklists any player parts. Essentially, this checks to see if the player is near a wall, by doing this if statement:

if #getParts > 0 then

If this is true, then I have multiple raycasts shooting in many different angles towards the movement direction (This method isn’t perfect, as there could easily be a part inbetween these raycasts). If a raycast is hit, then it takes the normal of that raycast and feeds it into a function called “flattenVectorAgainstWall” which calculates resulting movement vector against a wall (Big thanks to @HorseNuggetsXD who wrote this function for me). Here’s that function:

-- flattenVectorAgainstWall: flatten a vector given vector and normal
function flattenVectorAgainstWall(moveVector: Vector3, normal: Vector3)
	-- if magnitudes are 0 then just nevermind
	if moveVector.Magnitude == 0 or normal.Magnitude == 0 then
		return Vector3.zero
	end
	
	-- unit the normal (i its already normalized idk)
	normal = normal.Unit
	
	-- reflect the vector
	local reflected = moveVector - 2 * moveVector:Dot(normal) * normal
	-- add the reflection to the move vector = vector parallel to wall
	local parallel = moveVector + reflected
	
	-- if magnitude 0 NEVERMIND!!!
	if parallel.Magnitude == 0 then
		return Vector3.zero
	end
	
	-- reduce the parallel vector to make sense idk HorseNuggetsXD did all this thank u
	local cropped = parallel.Unit:Dot(moveVector.Unit) * parallel.Unit * moveVector.Magnitude
	return cropped
end

This is how that function basically works:

image

The parallel vector is also “cropped” according to how much the player is facing towards the wall. This is done to prevent the player actually speeding up when hugging a wall diagonally. Additionally, you also have to make sure the raycasts don’t hit the same wall twice. I do this by adding it into a blacklist every frame.

Thats’s pretty much it. Here’s a video with the raycasts visible:

14 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.