## Box2D C++ tutorials - Rotating to a given angle

Last edited: July 14 2013Chinese version -> 中文

### Rotating a body to a given angle

This topic is similar to the previous topic but deals with rotating a body instead of linear movement. Rotating a body can be also done by setting the angle directly or by using torque/impulse methods, with the same point to note that setting the angle directly means the body is not participating correctly in the physics simulation.

To experiment with these all we need is one dynamic body, and it would be nice if there was no gravity so it stays on the screen for us. We will need to give the body a fixture which has a distinct direction, so we can check if it's facing the right way. Let's set it up as a polygon with one 'pointy' vertex:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | //class member variable b2Body* body; FooTest() { //body definition b2BodyDef myBodyDef; myBodyDef.type = b2_dynamicBody; //hexagonal shape definition b2PolygonShape polygonShape; b2Vec2 vertices[6]; for (int i = 0; i < 6; i++) { float angle = -i/6.0 * 360 * DEGTORAD; vertices[i].Set(sinf(angle), cosf(angle)); } vertices[0].Set( 0, 4 ); //change one vertex to be pointy polygonShape.Set(vertices, 6); //fixture definition b2FixtureDef myFixtureDef; myFixtureDef.shape = &polygonShape; myFixtureDef.density = 1; //create dynamic body myBodyDef.position.Set(0, 10); body = m_world->CreateBody(&myBodyDef); body->CreateFixture(&myFixtureDef); //zero gravity m_world->SetGravity( b2Vec2(0,0) ); } |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //class member variable b2Vec2 clickedPoint; //in class constructor clickedPoint = b2Vec2(0,20);//initial starting point //override parent class void MouseDown(const b2Vec2& p) { //store last mouse-down position clickedPoint = p; //do normal behaviour Test::MouseDown( p ); } //inside Step() glPointSize(4); glBegin(GL_POINTS); glVertex2f( clickedPoint.x, clickedPoint.y ); glEnd(); |

### Setting angle directly

This is as simple as using SetTransform to set the angle, but first we need to know what the angle should be, given the location of the body and the location of the target point. Add this to the Step() function to do it every time step:

1 2 3 4 5 6 7 8 9 10 | //in Step() function float bodyAngle = body->GetAngle(); b2Vec2 toTarget = clickedPoint - body->GetPosition(); float desiredAngle = atan2f( -toTarget.x, toTarget.y ); //view these in real time m_debugDraw.DrawString(5, m_textLine, "Body angle %.3f", bodyAngle * RADTODEG); m_textLine += 15; m_debugDraw.DrawString(5, m_textLine, "Target angle %.3f", desiredAngle * RADTODEG); m_textLine += 15; |

1 | body->SetTransform( body->GetPosition(), desiredAngle ); |

1 | body->SetAngularVelocity(0); |

1 2 3 4 | float totalRotation = desiredAngle - bodyAngle; float change = 1 * DEGTORAD; //allow 1 degree rotation per time step float newAngle = bodyAngle + min( change, max(-change, totalRotation)); body->SetTransform( body->GetPosition(), newAngle ); |

1 2 | while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD; while ( totalRotation > 180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD; |

### Using torques

For a more physically realistic method, a torque can be applied to turn the body: We could try this to start with:

1 2 3 4 | float totalRotation = desiredAngle - bodyAngle; while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD; while ( totalRotation > 180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD; body->ApplyTorque( totalRotation < 0 ? -10 : 10 ); |

Since the problem is caused by the current angular velocity affecting future time steps, we need to take that into account. We can calculate the angle of the body in the next time step without applying any torque - this is what would happen if we just left it alone - and use that in place of the current angle (with the default testbed framerate of 60Hz):

1 2 3 | float nextAngle = bodyAngle + body->GetAngularVelocity() / 60.0; float totalRotation = desiredAngle - nextAngle;//use angle in next time step body->ApplyTorque( totalRotation < 0 ? -10 : 10 ); |

1 | float nextAngle = bodyAngle + body->GetAngularVelocity() / 3.0;// 1/3 second |

Using the formula T = Iv/t where T is the torque we want to know, I is the rotational inertia of the body, v is the rotational velocity and t is the time we will apply the torque, as before:

1 2 3 4 5 6 7 | float nextAngle = bodyAngle + body->GetAngularVelocity() / 60.0; float totalRotation = desiredAngle - nextAngle; while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD; while ( totalRotation > 180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD; float desiredAngularVelocity = totalRotation * 60; float torque = body->GetInertia() * desiredAngularVelocity / (1/60.0); body->ApplyTorque( torque ); |

Update: this will not work correctly for bodies with a center of mass that is not on their origin, like the one we have here :( Currently the Box2D API only allows access to the inertia about the body origin, whereas we are applying a torque about the center of mass. I am hoping future releases of the API will make the inertia about the center of mass available.

### Using impulses

As in the last topic, instantaneous movement using impulses is the same as the above code, but without the time factor:

1 2 3 4 5 6 7 | float nextAngle = bodyAngle + body->GetAngularVelocity() / 60.0; float totalRotation = desiredAngle - nextAngle; while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD; while ( totalRotation > 180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD; float desiredAngularVelocity = totalRotation * 60; float impulse = body->GetInertia() * desiredAngularVelocity;// disregard time factor body->ApplyAngularImpulse( impulse ); |

1 2 3 4 5 6 7 8 9 | float nextAngle = bodyAngle + body->GetAngularVelocity() / 60.0; float totalRotation = desiredAngle - nextAngle; while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD; while ( totalRotation > 180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD; float desiredAngularVelocity = totalRotation * 60; float change = 1 * DEGTORAD; //allow 1 degree rotation per time step desiredAngularVelocity = min( change, max(-change, desiredAngularVelocity)); float impulse = body->GetInertia() * desiredAngularVelocity; body->ApplyAngularImpulse( impulse ); |

Not seeing the any comments below? Try changing the 'https' in the page URL to just 'http'

Click here: http://www.iforce2d.net/b2dtut/rotate-to-angle

Click here: http://www.iforce2d.net/b2dtut/rotate-to-angle