Physics based sliding

Hello Devs,

 I am currently making a multiplayer survival type game and have a custom movement system with bhop, and sliding. It is physics based but currently my sliding is not. I would like to have it so that sliding on the ground gives you little to no boost of speed but going down slopes gives you speed like titanfall, apex legends, karlson, etc. My current movement system sets a velocity value to the players walkspeed and for my sliding currently it just takes the players current speed and multiplies it by 1.8. I have messed around with resources I have found looking on the web but have not been able to calculate the angle of slopes correctly or add velocity to my player in the way I want. Any help would be appreciated!

Sliding code:

local player = game.Players.LocalPlayer
local character = script.Parent
local humanoid = character:WaitForChild("Humanoid")
local rootPart = humanoid.RootPart
local Camera = workspace.CurrentCamera

--services
local RunService = game:GetService("RunService")
local UIS = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ContextActionService = game:GetService("ContextActionService")
local GuiService = game:GetService("GuiService")

--sliding varibles
local isSliding = false
local slideForce = 2
local slideCooldown = .8
local grounded = true
local justSlided = false
local slideDebounce = false

local CameraInfo = TweenInfo.new(0.3)
local CameraTweenDown = game:GetService("TweenService"):Create(player.Character.Humanoid, CameraInfo, {CameraOffset = Vector3.new(0,-2.5,1)})
local CameraTweenUp = game:GetService("TweenService"):Create(player.Character.Humanoid, CameraInfo, {CameraOffset = Vector3.new(0,0,0)})

local slidingBindable = ReplicatedStorage:WaitForChild("slidingBindable")
local stoppedSlidingBindable = ReplicatedStorage:WaitForChild("stoppedSlidingBindable")
local slidingEvent = ReplicatedStorage:WaitForChild("Sliding")
local stoppedSlidingEvent = ReplicatedStorage:WaitForChild("stoppedSliding")

local SlideAnim = humanoid:LoadAnimation(script:WaitForChild("SlideAnim"))

--sound
local slideSound = game.ReplicatedFirst.Sounds.sliding:Clone()
local landSound = game.ReplicatedFirst.Sounds.land:Clone()

slideSound.Volume = .38
slideSound.RollOffMode = Enum.RollOffMode.InverseTapered
slideSound.RollOffMaxDistance = 15
slideSound.RollOffMinDistance = 5
slideSound.TimePosition = 0

slideSound.Parent = rootPart

landSound.Volume = .4
landSound.RollOffMode = Enum.RollOffMode.InverseTapered
landSound.RollOffMaxDistance = 15
landSound.RollOffMinDistance = 5
landSound.PlaybackSpeed = .9
landSound.TimePosition = 0

landSound.Parent = rootPart

--check if grounded and handle jump sounds
humanoid.StateChanged:Connect(function(oldState, newState)
	if newState == Enum.HumanoidStateType.Jumping then
		grounded = false
	end
end)

humanoid.StateChanged:Connect(function(oldState, newState)
	if newState == Enum.HumanoidStateType.Landed then
		grounded = true
		landSound:Play()
		landSound.Ended:Connect(function()
			landSound.TimePosition = 0
		end)
	end
end)

--actual sliding code
local function Slide(name, state, input)
if not slideDebounce then
	if state == Enum.UserInputState.Begin and not isSliding and grounded and humanoid.MoveDirection.Magnitude > 0 then
		isSliding = true
		SlideAnim:Play()
		local hVel = character.HorizontalVelocity
		hVel.Value = hVel.Value * slideForce
		CameraTweenDown:Play()
		slidingBindable:Fire()
		slidingEvent:FireServer()
		slideSound:Play()
		--humanoid.Sit = true	
	elseif state == Enum.UserInputState.Begin and not isSliding and not grounded then
		isSliding = true
		CameraTweenDown:Play()
		SlideAnim:Play()
		slidingBindable:Fire()
		slidingEvent:FireServer()
		slideSound:Play()
		--humanoid.Sit = true	
	else
		isSliding = false
		CameraTweenUp:Play()
		SlideAnim:Stop()
		stoppedSlidingBindable:Fire()
		stoppedSlidingEvent:FireServer()
		slideSound:Pause()
		slideSound.TimePosition = 0
		slideDebounce = true	
		--humanoid.Sit = false	
		task.wait(slideCooldown)
		slideDebounce = false
		end
	end
end

--binding
ContextActionService:BindAction("Sliding", Slide, true, Enum.KeyCode.LeftShift)
ContextActionService:SetPosition("Sliding", UDim2.new(-.1, 0, .5, 0))
ContextActionService:SetTitle("Sliding", "Slide")

--check if on mobile
if UIS.TouchEnabled and not UIS.KeyboardEnabled and not UIS.MouseEnabled and not UIS.GamepadEnabled and not GuiService:IsTenFootInterface() then
	local CrouchButton = ContextActionService:GetButton("Sliding")
	CrouchButton.Size = UDim2.new(.45,0,.45,0)
end
2 Likes

Have you tried constantly creating a raycast that checks if the ground is rotated a certain degree?

I have tried to raycast to get the angle but it has been inaccurate and I’m not the best with maths

1 Like

you just get the object underneath the player and do if RaycastPart.Orientation.Y > 60 then speed up or something like that

I want something more dynamic, that could work but it’s very simple

1 Like

You’re better off checking the surface normal and not the orientation, also checking the Y axis would be incorrect anyways, Roblox is Y-up.

This post describes what a surface normal is, especially in the context of a raycast.


A more “dynamic” solution could be the client changing the custom physics properties of whatever parts they are currently in contact with (and may come into contact with throughout the slide) to allow the player to slide upon them, this won’t mess with anyone else and should work fine as the client has control over the physics of their own character.

I also have reason to believe this is what Phantom Forces used to do given how they mentioned speed exploits worked in tandem with the sliding system.

The caveat is that the server would otherwise need to sanity check the speed of a player at all times to ensure they’re not abusing this by doing it on surfaces that they’re not even sliding on.

Remember, never trust the client!

Thing is custom physical properties don’t work with my character controller as the main hit box for collisions is a sphere. The only idea I have in mind is checking if the player is sliding and then get the angle of the surface beneath them and somehow convert that angle into a number velocity to apply to the character. But I don’t know how I could do that.

1 Like

the most efficent way of doing it would be looping a raycast that would check the RaycastResult.Normal and devide it a bunch of times to adapt to player speed. (I personally haven’t worked with normals)

something like that could work Im also not the most experience with using cross and dot and how that all works.

I know I’m a bit late to the party, but you can make a raycast to check if your player is on the ground.
Then if the raycast returns false you multiply your velocity by a value that gets higher if you are on a slope.
Else if the raycast returns true you multiply your velocity by a value that gets lower if you are on a slope.

I misspoke do not raycast! It does not work. JK

I bet there’s a way to use raycast it’s just out of my expertise. What I’m trying to achieve is something like this video: https://mobile.twitter.com/ChrythmDev/status/1572793119162380289/mediaviewer

You can turn your character into a invisible sphere and have it roll.

Thats what EgoMoose and X_O has done for their sliding system:

2 Likes

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