Conserving Momentum in the Air

The idea here is mostly straightforward.
Using the default Roblox physics engine, players lose all velocity a few seconds after jumping (provided they let go of directional keys), which can tend to be less than optimal for games based around momentum. Is there a simple way to make sure that the player keeps moving in the air even after they stop pressing any keys.
I already have some basic ideas for this, such as:

  • BodyVelocities / BodyMovers
  • The humanoid state type “Physics”

I have a slight idea on how to check if the player is grounded or not, and it appears that using a bodymover might be the better option if RunService is going to be used.
While there have been some other posts regarding this topic, most of them may be outdated or have been left unanswered.

If anyone has any information on how to perform this please reply, thanks.

Humanoid physics state should work, however I think it’ll mess with the animations though it should be solvable.

Perhaps I would try to add a counter to the humanoid drag forces like the one below and see if it would work.

I’ll try this method later and give an update if it works.

1 Like

The animations aren’t really what I’m concerned with for the physics state. It’s mostly because since I’m using RunService for ground checks, changing the state to physics keeps rapidly toggling the state. Is there any way to only toggle it once per ground check?
I was thinking connecting the event to a humanoid.Jumping then just waiting for the humanoid’s floormaterial to switch back from nil, however I do not know if this works.

Could you disable the other states it tries to change to using SetStateEnabled?

Alright, so it looks like this works, however physics state is a bit weird and does not do what I thought it would do, which is conserver momentum mid air
Maybe it could just be the code, however:

local RS = game:GetService("RunService")

local plr = game.Players.LocalPlayer
local chr = plr.Character
local hum = chr:WaitForChild("Humanoid")

RS.RenderStepped:Connect(function()
	if hum.FloorMaterial == Enum.Material.Air then
		hum:SetStateEnabled(Enum.HumanoidStateType.Physics, true)
	else
		hum:SetStateEnabled(Enum.HumanoidStateType.Physics, false)
	end
end)

I think you still need to use SetState as well to cause the humanoid to be in the physics state. Also have you tried this on the server rather than the client? I’m not sure exactly how the replication works for humanoid state but I can imagine it may override local changes.

Ok so that works now, all I need to do now is just to figure out how to disable the physics mode only once considering how everything runs on a runservice loop
Trying to set the player’s state to running just makes them essentially trip and not be able to get back up

Alright so I have just looked it up and looks like there is a way to detect general state changes, so I’ll try using that

So it looks like after the player jumps I need to find another method to check if the player is grounded, as the new state is physics. I think there is a way to check if a property changes so I can connect that to potentially the statechanged and see if the floormaterial changes then I can disable physics

The player just jumps once then gets stuck on the floor, any ideas on how to fix it?

local RS = game:GetService("RunService")
local Debris = game:GetService("Debris")

local plr = game.Players.LocalPlayer
local chr = plr.Character
local root = chr:WaitForChild("HumanoidRootPart")
local hum = chr:WaitForChild("Humanoid")

hum.StateChanged:Connect(function(old, new)
	
	if new == Enum.HumanoidStateType.Jumping then
		hum:ChangeState(Enum.HumanoidStateType.Physics)
		
	elseif new == Enum.HumanoidStateType.Physics then
		hum:GetPropertyChangedSignal("FloorMaterial"):Connect(function()
			if hum.FloorMaterial ~= Enum.Material.Air then
				hum:ChangeState(Enum.HumanoidStateType.Physics)
			end
		end)
	end
end)

just wanted to say youve probably already figured this out but uhh it stops working after the point were it checks if new == physics it gets past that but hum:GetPropertyChangedSignal(“FloorMaterial”):Connect(function() breaks the rest

do you have a working script because when i try it doesnt work

local oldVelPref = 140 -- Determines how strongly to maintain momentum, higher values maintain old velocity, but can cause wall sticking.
local airAcceleration = 2 -- Determines how quickly to accelerate in the air

local RunService = game:GetService("RunService")

local char = script.Parent
local Humanoid = char:FindFirstChildWhichIsA("Humanoid")
local RootPart = Humanoid.RootPart or char:WaitForChild("HumanoidRootPart")

Humanoid.FreeFalling:Connect(function(active)
	while Humanoid:GetState() == Enum.HumanoidStateType.Freefall do
		local oldVel = RootPart.AssemblyLinearVelocity
		local dT = RunService.PostSimulation:Wait()
		local curVel = RootPart.AssemblyLinearVelocity
		
		-- Allows mid-air control
		local moveVel = Humanoid.MoveDirection*Humanoid.WalkSpeed
		local addVel = Vector3.new(
			curVel.X > 0 and math.max(moveVel.X-curVel.X, 0) or math.min(moveVel.X-curVel.X, 0),
			0,
			curVel.Z > 0 and math.max(moveVel.Z-curVel.Z, 0) or math.min(moveVel.Z-curVel.Z, 0)
		) * dT * airAcceleration -- Gradual acceleration

		local pref = oldVelPref * dT
		RootPart.AssemblyLinearVelocity = Vector3.new(
			math.clamp(oldVel.X, curVel.X-pref, curVel.X+pref),
			math.max(curVel.Y, -200),
			math.clamp(oldVel.Z, curVel.Z-pref, curVel.Z+pref)
		) + addVel
	end
end)