The function below works fine, when the timesteps of the physicsengine are short enough for the function to update often. It determinates when the ship(the object) needs to start decelerating to reach 0 angular velocity by the time it reaches the target rotation. (Always using maximum torque of the ship to turn)
However, I want to simulate this in a scenario with longer timesteps. Since my function checks for when to start decelerating each update, it will miss this moment when the timesteps are longer and move past the target rotation. The ship then starts swaying back and forth (obviously).
What I need is some way to determinate the torque I need to apply to the body for it to end up at that sweetspot where it needs to decelerate. That way the ship can start decelerating when the next update occurs and hopefully end up facing the target rotation with almost 0 angular velocity!
private void TurnTowardsTarget(Vector2 target, float deltaTime)
{
Vector2 delta = target - body.Position;
float targetRot = (float)Math.Atan2(delta.Y, delta.X);
float dRot = (float)Math.Atan2(Math.Sin(targetRot - body.Rotation),
Math.Cos(targetRot - body.Rotation));
float angularAcceleration = maxTurnForce / body.Inertia;
float avgDecelerationVelocity = body.AngularVelocity / 2;
float time = dRot / avgDecelerationVelocity;
float decelerationRequired = body.AngularVelocity / time;
//Target rotation is to the right (positive rotations are to the right)
if (dRot > 0)
{
//If the ship is rotating to the right
if (body.AngularVelocity >= 0)
{
//Should we start decelerating?
if (decelerationRequired >= angularAcceleration)
{
//Decelerate
TurnLeft();
}
else
{
//Keep on accelerating towards the target
TurnRight();
}
}
else
{
//The ship is rotating the wrong way, turn towards the target
TurnRight();
}
}
//Target rotation is to the left (negative rotations are to the left)
if (dRot < 0)
{
if (body.AngularVelocity <= 0)
{
//Should we start decelerating?
if (-decelerationRequired >= angularAcceleration)
{
//Decelerate
TurnRight();
}
else
{
//Keep on accelerating towards the target
TurnLeft();
}
}
else
{
//The ship is rotating the wrong way, turn towards the target
TurnLeft();
}
}
}
I hope I made myself quite clear, all the TurnLeft() and TurnRight() do is to apply "maxTurnForce" in the corresponding direction. Please respond if you find a solution, my brain is melting (x.
/Kimmeh!
Edit:
This is how the code looks now after implementing Sam's suggestion, it slightly overshoots the target rotation though, no matter the update-rate. And jitters back and forth from the target rotation.
I've put Math.Abs() around the time-calculation, changed sign of decelerationDecelerationRequired and changed sign of angularAcceleration depending on dRot.
Vector2 delta = turnToTarget - body.Position;
float targetRot = (float)Math.Atan2(delta.Y, delta.X);
float dRot = (float)Math.Atan2(Math.Sin(targetRot - body.Rotation),
Math.Cos(targetRot - body.Rotation));
float angularAcceleration = maxTorque * -Math.Sign(dRot) / body.Inertia;
float avgDecelerationVelocity = body.AngularVelocity / 2;
float time = Math.Abs(dRot / avgDecelerationVelocity);
float decelerationRequired = -body.AngularVelocity / time;
float torque = (decelerationRequired - angularAcceleration) * body.Inertia;
torqueToApply = MathHelper.Clamp(torque, -maxTorque, maxTorque);
Just to clarify, I'm using Farseer physics engine, and I apply the torque of the body right before calling the step() function of farseer. The body has a slight angular damping, so it should actually rotate a bit less than expected.
I think it's because it now smoothly adjusts to the decelerationRequired. (say if the decelerationRequired == angularAcceleration, the change of torque would be 0, when it should be +-maxTorque in order to stop in time.