- Home /
Rotate a 2D Rigidbody to a desired angle using AddTorque?
In my game I have several rigidbodies that interact with eachother using physics. The objects will be rotating, but I can't get the objects to rotate to any desired angle because the angles just don't seem to be reliable for any calculations (to my level of knowledge).
Is there anyway I could make a 2d PID controller so then the rigidbody will rotate properly to a given angle.
Or could I at least get some help on making calculations with angles.
Answer by MelvMay · Jan 22, 2016 at 01:26 PM
I think the best way I can help you is to describe how, when applying a single torque value, it is used during the next fixed-update.
When you call Rigidbody2D.AddTorque, the value you use is added to an internal torque value and is only used (and subsequently reset back to zero) during the fixed-update.
During the fixed-update, this accumulated torque value (or only a single one if you only called AddTorque once) will be added to the Rigidbody2D.angularVelocity using the formula:
Rigidbody2D.angularVelocity += Time.fixedTimeDelta x (1 / Rigidbody2D.inertia) x Torque
It's worth pointing out that the angular velocity of a body will stay at this value unless a force acts on the body to change it and/or Rigidbody2D.angularDrag is used so it should be set to zero if you don't require such drag.
The angular velocity changes the Rigidbody2D.rotation using the formula:
Rigidbody2D.rotation += Time.fixedTimeDelta x Rigidbody2D.angularVelocity
In simpler terms; the torque value is what modifies the angular velocity of the body by being multiplied by the time-step and the inverse body rotational inertia. This angular velocity changes the body rotation by being multiplied simply by the time-step.
Alternately, if you don't want to use forces and directly manipulate the Rigidbody2D.angularVelocity then everything becomes much simpler. Simply setting the angular velocity means setting a value in degrees/second the object will rotate. If you calculate how many degrees (positive or negative) you need to move then simply set the angular velocity and wait for the appropriate amount of time. This methods makes it easy to set an angular speed (it's the angular velocity itself).
Another way is to use Rigidbody.MoveRotation. After you've calculated how many degrees you need to move (as above) then simply call the above method during fixed-update by linearly interpolating (Mathf.Lerp) from the starting angle to the target angle.
wait so the angular velocity will be dependant on the torque? shouldn't it be the other way around? like the torque should be proportional to the difference between the target and current angular velocity, and the target angular velocity will be proportional to the difference in the target and current angle of the rigidbody?
Sorry if I totally missed something obvious. I'm trying to understand all this at 2A$$anonymous$$
Answer by manesh · Mar 31, 2016 at 03:34 AM
Here's my quick and dirty solution, after being flummoxed trying to figure this out. Using Unity 5.4b12.
The most important thing I figured out is rotation
is in degrees. It can go above 360 (e.g. 720, 1080, etc.) as well as be negative (-360, -720, etc.), depending on how the object was rotated. Some simple math makes it consistent.
float angle = this.rigidbody2d.rotation % 360;
float torque = 0.25f;
if(angle < 0)
angle += 360;
if(angle < 1f || angle > 359f)
{
this.rigidbody2d.transform.rotation = Quaternion.identity;
}
else if(angle > 180f)
{
this.rigidbody2d.AddTorque(torque);
}
else
{
this.rigidbody2d.AddTorque(-torque);
}
Answer by felres · Jan 25 at 09:46 PM
For anyone that happens to stumble upon this thread Ive made this method that makes a 2d rigidbody rotate to face a specific angle, using only AddTorque.
Heres an example of how I use it in code:
private void FixedUpdate()
{
float horizontalMove = horizontalInput * (isUnderwater ? underwaterSpeedX : inlandSpeed);
float verticalMove = verticalInput * (isUnderwater ? underwaterSpeedY : 0f);
forceToAdd = new Vector2(horizontalMove, verticalMove);
GameManager.TorqueTo(transform.right, forceToAdd, rb, maxTorque, torqueDampFactor);
}
The actual code:
/// <summary>
/// Use AddTorque() to face a specific angle. For 2D physiscs. Should be called in every FixedUpdate() frame.
/// </summary>
/// <param name="currentVec"> vector representing the direction we are currently pointing at. (transform.right) </param>
/// <param name="targetVec"> vector representing the direction we want to point at. </param>
/// <param name="rb"> Rigidbody to affect. </param>
/// <param name="maxTorque"> Max torque to apply. </param>
/// <param name="torqueDampFactor"> Damping factor to avoid undershooting. </param>
/// <param name="offsetForgive"> Stop applying force when the angles are within this threshold (default 0). </param>
public static void TorqueTo(Vector3 currentVec, Vector3 targetVec, Rigidbody2D rb, float maxTorque, float torqueDampFactor, float offsetForgive = 0)
{
float targetAngle = FindAngle(targetVec);
float currentAngle = FindAngle(currentVec);
float angleDifference = AngleDifference(targetAngle, currentAngle);
if (Mathf.Abs(angleDifference) < offsetForgive) return;
float torqueToApply = maxTorque * angleDifference / 180f;
torqueToApply -= rb.angularVelocity * torqueDampFactor;
rb.AddTorque(torqueToApply, ForceMode2D.Force);
}
This method does use other "helper" methods, to make my life easier in general. They should be pasted under the main method above. I personally put them all on a global "helper" script to use them everywhere (this is possible by making them static). Here they are:
/// <summary>
/// Returns the angle (in degrees) in which the vector is pointing.
/// </summary>
/// <returns>0-360 angle </returns>
public static float FindAngle(Vector2 vec)
{
return FindAngle(vec.x, vec.y);
}
public static float FindAngle(float x, float y)
{
float value = (float)((System.Math.Atan2(y, x) / System.Math.PI) * 180);
if (value < 0) value += 360f;
return value;
}
public static float AngleDifference(float a, float b)
{
return ((((a - b) % 360f) + 540f) % 360f) - 180f;
}