- Home /
Trying to limit air velocity
I've been having a lot of trouble figuring out how to limit horizontal air velocity. For example, when I'm grounded I can do.
if(IsGrounded() && !_isJumping)
{
_velocity = _rigidBody.velocity;
_velocity += _groundVelocity;
// Limit velocity to my run speed
if(_velocity.magnitude > runSpeed)
{
_velocity.Normalize();
_velocity *= runSpeed;
}
_rigidBody.velocity = _velocity;
}
But when I'm jumping I have gravity to worry about. so I can't limit my horizontal velocity without messing with my vertical velocity. I'm not great at math and really don't know too much about vector math. My gravity is attracting towards a sphere like planet gravity so my up and forward vectors always change which makes it even harder for me to visualize what I need to do.
I'm using the standard jump velocity formula
Mathf.Sqrt(-gravity * targetJumpHeight * 2);
So this is what I have so far (this is in FixedUpdate() ):
// Apply velocity changes
if(IsGrounded() && !_isJumping)
{
// Not sure if this should be here
// but everything seems to work.
_velocity = _rigidBody.velocity;
_velocity += _groundVelocity;
// Limit velocity to run speed
if(_velocity.magnitude > runSpeed)
{
_velocity.Normalize();
_velocity *= runSpeed;
}
// I know people say don't change this directly, but I'm looking for
// pretty instant move speed and this does what I need. I am open to
// suggestions :)
_rigidBody.velocity = _velocity;
}
else // Air control
{
// If I add _velocity = _rigidbody.velocity here
// and then do _velocity += _airVelocity then when I try to limit
// my velocity to the run speed it slows down the gravity
// since I'm normalizing it and multiplying by the run speed. Gravity needs
// to be faster than the run speed over time.
_velocity = _airVelocity;
if(_velocity.magnitude > airSpeed)
{
_velocity.Normalize();
_velocity *= airSpeed;
}
// this just keeps adding velocity (obviously :) )
// which makes the player go extremely fast
// How do I make the horizontal movement velocity be limited
// by the run speed but allow gravity to keep it's previous
// values and continue to work as needed.
_velocity += _rigidBody.velocity;
_rigidBody.velocity = _velocity;
}
// Add Jump velocity after movement velocity
if(_isJumping && !_hasJumped)
{
// This gets me the right jump velocity but then I
// screw it up next frame when I try and limit
// the horizontal air velocity
_rigidBody.velocity = _velocity + _jumpVelocity;
_hasJumped = true;
}
// Call Attractor update
gravityAttractor.Attract(_transform, _rigidBody);
// Clear our velocities so we can recalculate next frame
_velocity = _groundVelocity = _airVelocity = Vector3.zero;
I'm more of a hobbyist programmer and don't really have all the concepts down so any help is greatly appreciated :)
Thanks,
Artie
Answer by robertbu · May 19, 2013 at 03:31 PM
Just take the 'Y' out and put it back. Conceptually:
var v3 = rigidbody.velocity;
v3.y = 0;
if (v3.magnitude > airSpeed)
v3 = v3.normalized * airSpeed;
v3.y = rigidbody.velocity.y;
rigidbody.velocity = v3;
I'm assuming you are using a rigidbody somewhere, but if not, the concept is the same. Zero out the 'Y' component, do you limiting calculation, put 'Y' back.
Wouldn't that only work if the up direction was always the world Vector3.up?
I'm using Faux Gravity so gravity is based on the normal of my sphere or surface normal of whichever surface I want.
Is there a way do do this same concept but account for the current gravity direction?
Thanks,
Interesting. I misses the sphere/gravtiy, and it was the whole point of your question. $$anonymous$$y vector math is self taught and I don't have the grounding it I like. So I think you can do it this way. Project the velocity onto the normal to the surface of the sphere at the current point. Subtract that from the velocity. Limit the resulting vector and then add the up velocity back in. Something like:
// v3WorldUp - sphere normal at the current position
// velocity - current velocity
var v3UpVelocity = Vector3.Project(velocity, v3WorldUp);
var v3T = velocity - v3UpVelocity;
if (v3T.magnitude > airSpeed)
v3T = v3T.normalized * airSpeed;
velocity = v3T + v3UpVelocity;
This seems to be working great. Thank you so much! Someday I'll start catching on to these cool little tricks. (starting today :) )
Answer by Owen-Reynolds · May 19, 2013 at 05:56 PM
The standard math trick is to convert to local coords, do the work, then convert back. Visually local coords are a spaceman standing with the fake gravity planet below her feet, at -Y, and whichever way she happens to be looking is +Z. Aim one finger away from the grav source, and the other in your speed. Then spin so your "away" finger is pointing up. That spin is toUpSpin
below. After, the "speed" finger is your local velocity for how the spaceman is standing.
Something like (this type of math works, but exact math here untested):
Vector3 upFromGravSource = (trans.pos - grav.pos).normalized;
Quaternion toUpSpin = Quaternion.rotateTowards(upFromGravSource, Vector3.up);
Vector3 localVel = toUpSpin * rigidbody.velocity;
// test toUpSpin is correct. Should put cube "up" from fake grav source:
someCube.position = trans.pos + toUpSpin*Vector3.up*5;
If that works, localVel.y
is now away from the fake gravity source. x and z will be correct, but on sort of a "random" x and z axis flat to y (but not a problem.)
After the math, turn that back into the real (world) velocity with:
worldVel = Quaternion.Inverse(upFromGravSrc)*localVel;
Looks like it might work. However Quaternion.Inverse doesn't take a vector it takes a Quaternion. I'm guessing that upFromGravSrc is supposed to be the Vector3 from above?
Thanks
Ah...should be toUpSpin
. Velocity gets rotated by toUpSpin, then rotated back by the opposite, which is Inverse(toUpSpin).
This seemed to be close, the inverse was was flipping the left and right and I didn't quite get how to switch that back. $$anonymous$$aybe I was doing something wrong. Robertbu's solution seems to be working as I am intending. Thanks for your help. Also, It's good to think about things in local coordinates and convert them back, I never thought of that before. Thanks!
-Artie
$$anonymous$$athematically, Robertu's code does the same thing -- decomposes into a Y and a left-over X/Z.
Local coordinates are easier when you have a target transform aimed the correct way. But then most problems like this are mostly a matter of visualizing them.
Your answer
Follow this Question
Related Questions
Anti-Gravity isn't working 1 Answer
Jumping a specific height using Velocity & Gravity 1 Answer
Gravity trouble: Falling slow, jumping fast 1 Answer
In-Air Movement for Custom Controller? 0 Answers