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 :slight_smile:

Thanks,

Artie

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.

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;