Hello! First time poster, and having some issues with my Rigidbody based player controller. I’m trying to avoid any shortcuts here as much as possible, and building something that physically accurate.
public virtual void Move()
{
Vector2 Input = biped.movementInput.ReadValue<Vector2>();
//Don't hit the breaks on an empty input. Drag does that.
if (Input == Vector2.zero) return;
Vector3 DesiredVelocity = new Vector3(Input.x, 0, Input.y) * GetMovementSpeed.Invoke();
Vector3 planarVelocity = biped.transform.InverseTransformDirection(biped.rb.velocity);
planarVelocity.y = 0;
float dragScalar = 1.0f - biped.rb.drag * Time.fixedDeltaTime;
if (dragScalar < 0.0f) dragScalar = 0.0f;
Vector3 DesiredNoDragVelocity = DesiredVelocity / dragScalar;
Vector3 RequiredVelocityChange = DesiredNoDragVelocity - planarVelocity;
//Make sure the force and the input direction match.
//Exit case - Input 0,1 -> 0,0.3 = Trying to go low speed, let drag handle it.
//Exit case - Speed 999 = The velocity change would be mega neg to try to slow us down. Don't.
//Stay case - Input 0,1 -> 0,-1 = Switching direction.
if (Vector2.Dot(new Vector2(RequiredVelocityChange.x, RequiredVelocityChange.z).normalized, Input.normalized) < 0) { return; }
Vector3 MovementForce = biped.rb.mass * RequiredVelocityChange / Time.fixedDeltaTime;
float AccelerationLimit = GetAccelerationLimit.Invoke();
if(MovementForce.magnitude > AccelerationLimit)
{
MovementForce = MovementForce.normalized * AccelerationLimit;
}
biped.rb.AddRelativeForce(MovementForce, ForceMode.Force);
}
This is my movement function.
float dragScalar = 1.0f - biped.rb.drag * Time.fixedDeltaTime;
if (dragScalar < 0.0f) dragScalar = 0.0f;
Vector3 DesiredNoDragVelocity = DesiredVelocity / dragScalar;
This snippet of code is based on this forum thread, and when tested it seemed to be correct, as long as drag is applied before the movement of the object in a given tick.
Undoing the drag in this way gets our player up to the correct speed, where before they would be limited to the target speed after one tick of drag is applied (Target speed of 2 with a drag value of 5 would cap us at 1.8 when our fixed update ticks at 50hz).
Vector3 MovementForce = biped.rb.mass * RequiredVelocityChange / Time.fixedDeltaTime;
The movement force calculated here would get us up to our target velocity in a single fixed update. We limit this value to (the poorly named) acceleration limit, which is calculated as mass * (v / t) where mass is the rigidbody’s mass, v is the target speed, and t is the time we want it to take for the player to accelerate up to their movement speed from rest. With no drag value, this works perfectly. However, we also need to increase this limit to compensate for the drag slowing us down. Since drag is dependent on our velocity, we would need to calculate the drag force’s magnitude during the fixed update and add it to the acceleration limit.
What I’m struggling with is how to calculate that drag force. Obviously F=ma, and a = delta_v/delta_t, and so it’d be something like
Vector3 VelocityWithDrag = biped.rb.velocity;
Vector3 VelocityWithoutDrag = VelocityWithDrag / dragScalar;
var dragForce = biped.rb.mass * (VelocityWithoutDrag - VelocityWithDrag) / Time.fixedDeltaTime;
float AccelerationLimit = GetAccelerationLimit.Invoke() + dragForce.magnitude;
but this isn’t working as we accelerate a little too slowly. Clearly, I need a different velocity value in there, but my brain is fried trying to figure this out, and I think I need a second set of eyes. Any help is greatly appreciated.