How do you retain a player character's velocity mid-air?

Hey! So I’ve been looking into this topic for a while but the questions that have asked this before seem to always lead to a dead end, so I want to try asking again but being more direct with trying to ask it to avoid confusion.

The main issue is that the player character’s velocity will come to a screeching halt while mid air as if they were on the ground.

You can test this for yourself in almost any game, get a running start, jump and then let go of your movement input(s) while mid air.

While this generally works well for platforming purposes, I wish to avoid this sort of behavior as I want to keep a sense of momentum in my game.

I also cannot use methods such as platformstand, the physics humanoid state type and the flying state type either as I wish to keep control of other things such as animations, rotation and general player input.

If someone could help me figure out the general approach or what I’d need to do in order to achieve an effect like this I’d greatly appreciate it.

I WOULD LIKE TO RE-ITERATE THAT I AM LOOKING FOR THE METHOD ON HOW TO ACHIEVE SAID TASK AND NOT THE ANSWER, WHILE YOU CAN WRITE OUT THE CODE FOR IT IF YOU FEEL LIKE IT IS NESSECARY I’D MUCH MORE APPRECIATE IF I WAS TOLD HOW SOMETHING LIKE THIS WOULD FUNCTION SO I CAN DO IT MYSELF!

Many thanks to those who offer their support with this and have a nice day! :+1:

3 Likes

To fix this in my game I just added a body velocity into the character and set the velocity to the humanoidRootPart velocity and multiplied it by Vector3.new(1,0,1) to stop it from making the player go up or down.

I added the velocity when they jumped and removed it when they hit the ground.

1 Like

So in this case the solution would be to do something similar but instead to multiply it by Vector3.new(1,1,1)?

EDIT: I just realized that it’d still be Vector3.new(1,0,1), my bad!

1 Like

no the same. Its just so it doesn’t make add velocity up or down but side to side.

1 Like

Gotcha, I’ll try this out and give an update of the results!

1 Like

ya so idk if i can test this but i think i have idea

so whilst player not moving then constantly raycast down and if he in air and not moving that mean he freeze

so if that happen then fake the “velocity” by caculate with the raycast hit position on what the humanodrootpart would be if it is on the ground

then after that tween the players huminoidrootpart to the ground and the way u can use speed is using the local Time = math.sqrt((2 * Distance) / game.Workspace.Gravity)

when the player complete tween then unanchor rootpart

1 Like

Hi! I’m also trying to figure it out. The only idea I have is to try and modify the character’s scripts somehow.

My previous attempt was to take the LOST velocity from the previous frame, and return it back to the current velocity. Worked good. But… you’ll keep your velocity even if you go into a wall.
Which can be avoided by shooting 4 rays in all 4 directions, and if there is a wall, then remove the velocity, but for the game that I’m working on, that won’t work.

I’ll try to let you know if I find an answer!

I figured it out!
Well, I did so a long time ago. Gonna tell you all now.

!!!(Edit: short explanation at the bottom of the post. My ways of explaining have thankfully improved, the code below is laid out quite ruff)!!!

Basically what I did, is I gave the player back his velocity, that was lost from the previous frame.
I ALSO had to write extra code to handle movement in air, which makes it “slippery” mid-air. But it works perfectly!

– edit:
You also have to do some checks, for when NOT to keep velocity. How I did it, is:

if (Re.vec3.noY(CData.vel.prev).Unit - Re.vec3.noY(humR.AssemblyLinearVelocity).Unit).Magnitude > 0.4
			or (CData.vel.prev.Unit.Y - humR.AssemblyLinearVelocity.Unit.Y) > 0.42 then return end

It’s done so when you hit a wall, you don’t keep velocity INTO the wall :expressionless:. You can adjust the value “0.4” and the “0.42” values. It’s the max allowed difference between the velocities :+1:

NOTE:
Re” - is a module script with functions that are used in almost all scripts
Re.vec3.noY” function just returns a vector without the “Y” value:

local vec3 = Vector3.new(3, 4, 5)
local newVec3 = Re.vec3.noY(vec3) -- Vector3.new(3, 0, 5)

CData - is a module script for the character
In it, there is “CData.vel.prev”, which is the velocity on the previous frame. The only problem with keeping it, is that when you do a move such as jump, or a “parkour” move mid-air, you need to set the value (CData.vel.prev) to humR.AssemblyLinearVelocity
Example:

local function Jump()
    humR.AssemblyLinearVelocity += Vector3.new(0, 40, 0)
    CData.vel.prev = humR.AssemblyLinearVelocity
end

Also, for simplicity, I made a function “CData.vel.changeHumRVel” that basically does what the test function “Jump” does above. Also added “CData.vel.changeHumRVelHor” for change of horizontal velocity (with a limit), and ALSO updates the “CData.vel.prev”
Examples:

local function Jump()
    CData.vel.changeHumR(Vector3.new(0, 40, 0)) -- changes velocity, and updates the 'CData.vel.prev' value, to fix the velocity keeping mid-air
    CData.vel.changeHumRHor(humR.CFrame.LookVector * 5, 50) -- adds velocity to the player. If it goes over the limit, it doesn't add any more. If already over the limit, just slightly changes direction
end

Here is the ‘CData.vel.changeHumRHor’ function for those curious (it’s located in a ModuleScript, so I didn’t add the “local” thing):

changeHumRVelHor = function(velChange: Vector3, maxMag: number)
	maxMag *= CData.char.scale
	local startMag = Re.vec3.noY(humR.AssemblyLinearVelocity).Magnitude
	humR.AssemblyLinearVelocity += velChange * CData.char.scale
	local newMag = Re.vec3.noY(humR.AssemblyLinearVelocity).Magnitude
	if startMag < newMag then
		humR.AssemblyLinearVelocity = ((Re.vec3.noY(humR.AssemblyLinearVelocity).Unit) * math.max(maxMag, startMag))
			+ Re.vec3.onlyY(humR.AssemblyLinearVelocity)
		CData.vel.updatePrevVel()
	end
end;

Hope if helped someone!
Sorry if I didn’t show all the code. I still want to keep some stuff private from everyone, which is hopefully understandable, but if you request something, I’ll think about it :slight_smile:

QUICK EDIT. Simple explanation for those wanting to understand how it essentially works:
We essentially just cancel out Roblox’s character physics by removing all of their changes in velocity by storing the previous speed, while not doing that in case the change in angle is too much.
For that to work properly, I had to create some simple function to manage the speed, which would at the same time reset the previous velocity value.
If you’re confused about the random 0.4 or 0.42 in the code above where I talked the check when the velocity should not get restored, that’s just a random value that fit me well. You can instead replace it with an actual angle calculator between two vectors, using this function:

-- Returns the angle in degrees between two vectors
getAngle = function(vec1: Vector3, vec2: Vector3)
   return math.deg(2 * math.asin(math.clamp((vec1 - vec2).Magnitude / 2, 0, 1)))
end;

Lots of people liked the post, and I highly appreciate that! I really hope my answer helped you guys.

14 Likes

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