Character bouncing while sliding off slopes

I’m trying to create a slope sliding mechanic for a movement system I’m working on. However, there seems to be a bouncing problem while setting the MaxSlopeAngle on the humanoid. I’d like to avoid legacy body movers. I’ve got a band-aid fix, but I’m not confident in it.

Video showing the problem:

Code here:

local DecelerateRate = 500

RunService.RenderStepped:Connect(function(DeltaTime)
	local DeltaScale = DeltaTime * 60
	local Raycast = workspace:Raycast(RootPart.Position, RootPart.CFrame.UpVector * -6, RaycastParam)
	
	if Raycast then
		local Normal = Raycast.Normal	
		local Angle = math.acos(Normal.Y)

		if Angle > 0 then
            --// This is my fix 
			RootPart:ApplyImpulse(RootPart.CFrame.UpVector * -workspace.Gravity * 15) 

			Humanoid.MaxSlopeAngle -= DeltaTime * DecelerateRate
		else
			Humanoid.MaxSlopeAngle = 57
		end
	end
end)

That’s because the velocity of the character is unbalanced that it goes forward more than downwards. Try to force some velocity downwards as well.

Edit: I fixed the issue by using a LinearVelocity and applying a downwards force similar to workspace.Gravity.

That’s actually what my fix was, but I wasn’t sure if it was a good solution to the problem. Even with my fix sometimes it still bounces and I’m not sure if that amount of force downwards might cause physics issues later down the line.

Here’s the code I used for this project. (R6 Only):

--// Services
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")

--// Variables
local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local RootPart = Character:WaitForChild("HumanoidRootPart")
local Humanoid = Character:WaitForChild("Humanoid")

local Attachment = Instance.new("Attachment", RootPart)
local LinearVelocity = Instance.new("LinearVelocity", RootPart)
LinearVelocity.Attachment0 = Attachment
LinearVelocity.RelativeTo = Enum.ActuatorRelativeTo.World
LinearVelocity.VectorVelocity = Vector3.zero
LinearVelocity.MaxForce = 0

local HipHeight = Character["Left Leg"].Size.Y + (0.5 * RootPart.Size.Y) + Humanoid.HipHeight --// R15 Version: (0.5 * RootPart.Size.Y) + Humanoid.HipHeight
local SlideStartTime = 1 --// How long it takes for the slide to reach max velocity. (In Seconds)
local SlideEndTime = 2 --// How long it takes for the slide to stop. (In Seconds)
local SlideMaxSpeed = 50 --// Maximum slide speed
local Sliding = false

local RayParams = RaycastParams.new()
RayParams.FilterDescendantsInstances = {Character}

--// Functions
function CheckGround() --// Raycasts towards the ground and allows for an margin of error in HipHeight
	local Raycast = workspace:Raycast(RootPart.Position, RootPart.CFrame.UpVector * (-HipHeight * 2), RayParams)
	local Length = Raycast and (Raycast.Position - RootPart.Position).Magnitude or HipHeight * 2

	return Length <= HipHeight + 0.25, Raycast
end

function SlideLerp(RayInstance, Goal)
	local StartTick = tick()
	local TimePassed = 0
	local Start = LinearVelocity.VectorVelocity --// The LinearVelocity starting velocity

	local function SanityChecks(DetectSlope) --// Use this function to change sanity checks easily
		local _, GroundCheck = CheckGround()
		if not GroundCheck then return true end
		--if math.abs(GroundCheck.Normal.X) + math.abs(GroundCheck.Normal.Z) < 0.25 and DetectSlope then print("FOUND NON SLOPE") return true end
		if not UserInputService:IsKeyDown(Enum.KeyCode.C) then return true end
		
		if math.abs(GroundCheck.Normal.X) + math.abs(GroundCheck.Normal.Z) < 0.25 then
			if DetectSlope then
				return true
			else
				return false
			end
		end

		return false
	end

	while TimePassed <= SlideStartTime do --// Lerps LinearVelocity within given time span
		if SanityChecks(true) then break end

		TimePassed += RunService.Heartbeat:Wait()
		LinearVelocity.VectorVelocity = Start:Lerp(RayInstance.CFrame.LookVector * Goal, TimePassed/SlideStartTime) + (Vector3.yAxis * -workspace.Gravity)
	end
	
	while true do
		if SanityChecks(true) then break end

		RunService.Heartbeat:Wait()
	end
	
	Start = LinearVelocity.VectorVelocity
	TimePassed = 0
	while TimePassed <= SlideEndTime do
		if SanityChecks() then break end

		TimePassed += RunService.Heartbeat:Wait()
		LinearVelocity.VectorVelocity = Start:Lerp(Vector3.zero, TimePassed/SlideEndTime)
	end

	--// Resets everything to prepare for another slide
	LinearVelocity.VectorVelocity = Vector3.zero
	LinearVelocity.MaxForce = 0
	Sliding = false
	
	print("Slide Finished:", (tick() - StartTick))
end

--// Init
UserInputService.InputBegan:Connect(function(Input, GPE)
	if GPE then return end

	if Input.KeyCode == Enum.KeyCode.C then
		local _, GroundCast = CheckGround()
		if GroundCast and not Sliding then
			print("Found Ground")
			local RayInstance = GroundCast.Instance
			local Normal = GroundCast.Normal

			local Steepness = math.abs(Normal.X) + math.abs(Normal.Z)
			if Steepness > 0.25 then
				Sliding = true
				LinearVelocity.MaxForce = 100000
				SlideLerp(RayInstance, 50)
			end
		end
	end
end)

im relatively new to scripting and cant figure out how you would do this can you give me an example of how you used LinearVelocity so I can better understand it and fix my own problem with sliding?

Sorry for the late reply. The solution I did isn’t the best by any means but hopefully, it helps you get a general idea of execution. (This is also R6 btw, may be different for R15)

--// Services
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")

--// Variables
local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local RootPart = Character:WaitForChild("HumanoidRootPart")
local Humanoid = Character:WaitForChild("Humanoid")

local Attachment = Instance.new("Attachment", RootPart)
local LinearVelocity = Instance.new("LinearVelocity", RootPart)
LinearVelocity.Attachment0 = Attachment
LinearVelocity.RelativeTo = Enum.ActuatorRelativeTo.World
LinearVelocity.VectorVelocity = Vector3.zero
LinearVelocity.MaxForce = 0

local HipHeight = Character["Left Leg"].Size.Y + (0.5 * RootPart.Size.Y) + Humanoid.HipHeight --// R15 Version: (0.5 * RootPart.Size.Y) + Humanoid.HipHeight
local SlideStartTime = 1 --// How long it takes for the slide to reach max velocity. (In Seconds)
local SlideEndTime = 2 --// How long it takes for the slide to stop. (In Seconds)
local SlideMaxSpeed = 50 --// Maximum slide speed
local Sliding = false

local RayParams = RaycastParams.new()
RayParams.FilterDescendantsInstances = {Character}

--// Functions
function CheckGround() --// Raycasts towards the ground and allows for an margin of error in HipHeight
	local Raycast = workspace:Raycast(RootPart.Position, RootPart.CFrame.UpVector * (-HipHeight * 2), RayParams)
	local Length = Raycast and (Raycast.Position - RootPart.Position).Magnitude or HipHeight * 2

	return Length <= HipHeight + 0.25, Raycast
end

function SlideLerp(RayInstance, Goal)
	local StartTick = tick()
	local TimePassed = 0
	local Start = LinearVelocity.VectorVelocity --// The LinearVelocity starting velocity

	local function SanityChecks(DetectSlope) --// Use this function to change sanity checks easily
		local _, GroundCheck = CheckGround()
		if not GroundCheck then return true end
		--if math.abs(GroundCheck.Normal.X) + math.abs(GroundCheck.Normal.Z) < 0.25 and DetectSlope then print("FOUND NON SLOPE") return true end
		if not UserInputService:IsKeyDown(Enum.KeyCode.C) then return true end
		
		if math.abs(GroundCheck.Normal.X) + math.abs(GroundCheck.Normal.Z) < 0.25 then
			if DetectSlope then
				return true
			else
				return false
			end
		end

		return false
	end

	while TimePassed <= SlideStartTime do --// Lerps LinearVelocity within given time span
		if SanityChecks(true) then break end

		TimePassed += RunService.Heartbeat:Wait()
		LinearVelocity.VectorVelocity = Start:Lerp(RayInstance.CFrame.LookVector * Goal, TimePassed/SlideStartTime) + (Vector3.yAxis * -workspace.Gravity)
	end
	
	while true do
		if SanityChecks(true) then break end

		RunService.Heartbeat:Wait()
	end
	
	Start = LinearVelocity.VectorVelocity
	TimePassed = 0
	while TimePassed <= SlideEndTime do
		if SanityChecks() then break end

		TimePassed += RunService.Heartbeat:Wait()
		LinearVelocity.VectorVelocity = Start:Lerp(Vector3.zero, TimePassed/SlideEndTime)
	end

	--// Resets everything to prepare for another slide
	LinearVelocity.VectorVelocity = Vector3.zero
	LinearVelocity.MaxForce = 0
	Sliding = false
	
	print("Slide Finished:", (tick() - StartTick))
end

--// Init
UserInputService.InputBegan:Connect(function(Input, GPE)
	if GPE then return end

	if Input.KeyCode == Enum.KeyCode.C then
		local _, GroundCast = CheckGround()
		if GroundCast and not Sliding then
			print("Found Ground")
			local RayInstance = GroundCast.Instance
			local Normal = GroundCast.Normal

			local Steepness = math.abs(Normal.X) + math.abs(Normal.Z)
			if Steepness > 0.25 then
				Sliding = true
				LinearVelocity.MaxForce = 100000
				SlideLerp(RayInstance, 50)
			end
		end
	end
end)

Good luck with your project!

2 Likes

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