# 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!

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.

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 . You can adjust the value “0.4” and the “0.42” values. It’s the max allowed difference between the velocities

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

8 Likes

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