Custom movement system bugs out when walking into walls

I have a custom movement system for my game that uses a LinearVelocity (not using Humanoid:Move() because it jitters when starting to walk with uncapped fps) to control player movement, but when i walk into a wall it bugs out, the only way I thought of to fix this is to reduce the LinearVelocity’s MaxForce but this makes it move painfully slow.

I’m wondering if anyone has any ideas or alternative methods that would work identically but without the wall glitching. If nothing else works I’ll probably have to go back to Humanoid:Move() since it does almost exactly what I want but again, stutters.

Here’s the movement script (local script)

local inputService = game:GetService("UserInputService")
local runService = game:GetService("RunService")
local debrisService = game:GetService("Debris")

local walkSpeed = script:WaitForChild("WalkingSpeed")
local currentSpeed = script:WaitForChild("CurrentSpeed")

local humanoid = script.Parent:WaitForChild("Humanoid")
local animator = humanoid:WaitForChild("Animator")
local rootPart = script.Parent:WaitForChild("HumanoidRootPart")

local velocity = Instance.new("LinearVelocity")
velocity.Parent = rootPart
local attachment = Instance.new("Attachment")
attachment.Parent = rootPart
attachment.SecondaryAxis = Vector3.new(0,0,1)
velocity.MaxForce = 1000
velocity.Attachment0 = attachment
velocity.RelativeTo = Enum.ActuatorRelativeTo.Attachment0
velocity.VelocityConstraintMode = Enum.VelocityConstraintMode.Plane

local slideAnim = animator:LoadAnimation(script:WaitForChild("Slide"))
local slideSound = script:WaitForChild("Sound")

local sliding = false
local state = "Standing"
local canSlide = true

local movement = Vector3.zero
local targetMovement = Vector3.zero
local baseSpeedChange = 8
local speedChangeRate = baseSpeedChange

local wasdDirection = Vector2.zero

local jump = false

local timer = 0

inputService.InputBegan:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.W then
		wasdDirection = wasdDirection + Vector2.new(0, 1)
	end
	if input.KeyCode == Enum.KeyCode.A then
		wasdDirection = wasdDirection + Vector2.new(-1, 0)
	end
	if input.KeyCode == Enum.KeyCode.S then
		wasdDirection = wasdDirection + Vector2.new(0, -1)
	end
	if input.KeyCode == Enum.KeyCode.D then
		wasdDirection = wasdDirection + Vector2.new(1, 0)
	end
	if input.KeyCode == Enum.KeyCode.Space then
		jump = true
	end
end)
inputService.InputEnded:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.W then
		wasdDirection = wasdDirection + Vector2.new(0, -1)
	end
	if input.KeyCode == Enum.KeyCode.A then
		wasdDirection = wasdDirection + Vector2.new(1, 0)
	end
	if input.KeyCode == Enum.KeyCode.S then
		wasdDirection = wasdDirection + Vector2.new(0, 1)
	end
	if input.KeyCode == Enum.KeyCode.D then
		wasdDirection = wasdDirection + Vector2.new(-1, 0)
	end
	if input.KeyCode == Enum.KeyCode.Space then
		jump = false
	end
end)

runService.RenderStepped:Connect(function(step)
	timer += step
	
	local wasdMovement = Vector3.zero
	
	if wasdDirection.X == 0 and wasdDirection.Y == 0 then
		wasdMovement = Vector3.zero
		speedChangeRate = baseSpeedChange * 1.5
	else
		wasdMovement = Vector3.new(wasdDirection.X, 0, -wasdDirection.Y).Unit
		speedChangeRate = baseSpeedChange
	end
	
	targetMovement = wasdMovement * walkSpeed.Value
	if script.Parent.ViewModelScript.Aiming.Value then
		targetMovement = targetMovement * 0.8
	end
	
	movement = movement:Lerp(targetMovement, speedChangeRate * step)
	currentSpeed.Value = movement.Magnitude
	
	velocity.PlaneVelocity = Vector2.new(movement.X, movement.Z)
	
	if jump then
		humanoid.Jump = true
	end
	
	--humanoid.WalkSpeed = movement.Magnitude
	--humanoid:Move(movement.Unit, true)
end)

One solution to avoid the player character glitching when it hits a wall is to use a function that detects when the character is colliding with a wall and applies a force in the opposite direction to push the character away from the wall. This will prevent the character from getting stuck in the wall and allow it to continue moving smoothly.

Here is an example of how you could implement this in your movement script:

  1. Define a new function, handleWallCollision , that will detect when the character is colliding with a wall and apply a force to push it away from the wall:
local function handleWallCollision(step)
  -- Get the current velocity of the character
  local velocity = rootPart.Velocity

  -- Check if the character is colliding with a wall by checking if the
  -- magnitude of the velocity in the X and Y directions is greater than 0
  -- (this indicates that the character is moving against a wall)
  if velocity.X ~= 0 or velocity.Y ~= 0 then
    -- Calculate the direction in which the character is moving
    local direction = velocity.Unit

    -- Calculate the force to apply to the character in the opposite direction
    -- to push it away from the wall
    local force = direction * -500

    -- Apply the force to the character
    velocity.Force = force
  end
end
  1. Call the handleWallCollision function in the runService.RenderStepped event handler, after updating the character’s movement and velocity:
runService.RenderStepped:Connect(function(step)
  ...

  -- Update the character's movement and velocity
  movement = (1 - math.min(step * 8, 1)) * movement + targetMovement * math.min(step * 8, 1)
  velocity.Velocity = movement

  -- Handle wall collisions
  handleWallCollision(step)

  ...
end)

This is just one possible solution to the problem, and there may be other ways to implement it. You may need to adjust the parameters of the handleWallCollision function, such as the force applied to the character and the conditions for detecting a wall collision, to achieve the desired behavior in your game.

Raycasts may work in this case. Cast a ray in the direction the player is moving to and make sure there are no obstacles. If there is any then set the X and Z axis velocity to 0 (keep the Y so jump isn’t interrupted). Remember to only check in case the player is actually trying to move otherwise the player would get stuck forever.

Sorry it’s been a while but adding that function to my script doesn’t seem to have changed the movement or fixed the wall glitching but makes a “Force cannot be assigned to” error be spammed in the console.
Here is my script with your function added to it:

local inputService = game:GetService("UserInputService")
local runService = game:GetService("RunService")
local debrisService = game:GetService("Debris")

local walkSpeed = script:WaitForChild("WalkingSpeed")
local currentSpeed = script:WaitForChild("CurrentSpeed")

local humanoid = script.Parent:WaitForChild("Humanoid")
local animator = humanoid:WaitForChild("Animator")
local rootPart = script.Parent:WaitForChild("HumanoidRootPart")

local velocity = Instance.new("LinearVelocity")
velocity.Parent = rootPart
local attachment = Instance.new("Attachment")
attachment.Parent = rootPart
attachment.SecondaryAxis = Vector3.new(0,0,1)
velocity.MaxForce = 10000000
velocity.Attachment0 = attachment
velocity.RelativeTo = Enum.ActuatorRelativeTo.Attachment0
velocity.VelocityConstraintMode = Enum.VelocityConstraintMode.Plane

local slideAnim = animator:LoadAnimation(script:WaitForChild("Slide"))
local slideSound = script:WaitForChild("Sound")

local sliding = false
local state = "Standing"
local canSlide = true

local movement = Vector3.zero
local targetMovement = Vector3.zero
local baseSpeedChange = 8
local speedChangeRate = baseSpeedChange

local wasdDirection = Vector2.zero

local jump = false

local timer = 0

inputService.InputBegan:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.W then
		wasdDirection = wasdDirection + Vector2.new(0, 1)
	end
	if input.KeyCode == Enum.KeyCode.A then
		wasdDirection = wasdDirection + Vector2.new(-1, 0)
	end
	if input.KeyCode == Enum.KeyCode.S then
		wasdDirection = wasdDirection + Vector2.new(0, -1)
	end
	if input.KeyCode == Enum.KeyCode.D then
		wasdDirection = wasdDirection + Vector2.new(1, 0)
	end
	if input.KeyCode == Enum.KeyCode.Space then
		jump = true
	end
end)

inputService.InputEnded:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.W then
		wasdDirection = wasdDirection + Vector2.new(0, -1)
	end
	if input.KeyCode == Enum.KeyCode.A then
		wasdDirection = wasdDirection + Vector2.new(1, 0)
	end
	if input.KeyCode == Enum.KeyCode.S then
		wasdDirection = wasdDirection + Vector2.new(0, 1)
	end
	if input.KeyCode == Enum.KeyCode.D then
		wasdDirection = wasdDirection + Vector2.new(-1, 0)
	end
	if input.KeyCode == Enum.KeyCode.Space then
		jump = false
	end
end)


--changed velocity in here to velocity2 since I have a non-number variable above already set to have it's name as velocity
local function handleWallCollision(step)
	-- Get the current velocity of the character
	local velocity2 = rootPart.Velocity

	-- Check if the character is colliding with a wall by checking if the
	-- magnitude of the velocity in the X and Y directions is greater than 0
	-- (this indicates that the character is moving against a wall)
	if velocity2.X ~= 0 or velocity2.Y ~= 0 then
		-- Calculate the direction in which the character is moving
		local direction = velocity2.Unit

		-- Calculate the force to apply to the character in the opposite direction
		-- to push it away from the wall
		local force = direction * -500

		-- Apply the force to the character
		velocity2.Force = force
	end
end

runService.RenderStepped:Connect(function(step)
	timer += step
	
	local wasdMovement = Vector3.zero
	
	if wasdDirection.X == 0 and wasdDirection.Y == 0 then
		wasdMovement = Vector3.zero
		speedChangeRate = baseSpeedChange * 1.5
	else
		wasdMovement = Vector3.new(wasdDirection.X, 0, -wasdDirection.Y).Unit
		speedChangeRate = baseSpeedChange
	end
	
	targetMovement = wasdMovement * walkSpeed.Value
	if script.Parent.ViewModelScript.Aiming.Value then
		targetMovement = targetMovement * 0.8
	end
	
	movement = movement:Lerp(targetMovement, speedChangeRate * step)
	currentSpeed.Value = movement.Magnitude
	
	velocity.PlaneVelocity = Vector2.new(movement.X, movement.Z)
	
	if jump then
		humanoid.Jump = true
	end
	
	handleWallCollision(step)
	
	--humanoid.WalkSpeed = movement.Magnitude
	--humanoid:Move(movement.Unit, true)
end)

And here is a screen shot of the console:

this sounds like it would work but only for only for solid walls that encompass the entire height of the player, if I sent a raycast at torso height then this bugging would happen with obstacles at waist height or very thin height-wise obstacles