After some time fiddeling with my animation function, it does again
what i want it to do. However, I'm not really happy with it. I know, this is
some ugly piece of code, but i give my best to document what is going on.
// this function is called every frame in my game, to determine the new
// position and rotation of the unit.
void
Unit::Animate (float TimeSpan)
{
// Position is the current position of the unit, Course is its destination.
// Both are vector2f's.
if (Position == Course) return;
// The angle between the position and the course
float angle = atan2 (Course.y - Position.y, Course.x - Position.x) * 180.f / PI + 90.f;
if (angle < 0) angle += 360.f;
// The distance, the unit should move towards its destination
float distance = Speed * TimeSpan * 3.6;
// Get the actual Rotation (the Rotation will be set to the Sprite, before it is drawn)
Rotation = Sprite.GetRotation ();
// If the unit is oriented towards its destination, move straight
if (abs (abs (angle) - abs (Rotation)) < 0.1)
{
// If the unit is almost at its destination, meaning closer then
// distance away, just put it directly to its destination
if (pow (distance, 2) > distance_pow_2 (Course, Position))
{
Position = Course;
}
// Else, move towards its destination
else
{
sf::Vector2f direction = Course - Position;
normalize (direction);
direction *= distance;
Position += direction;
}
}
// Else, update the Rotation and move in that direction
else
{
// turn is the amount in degrees, by which the unit should turn.
// The formula is non-trivial, and i can't explain it without making
// a drawing. Please just believe, that this is doing the right thing.
float turn = asin (distance / (2.f * Probs->TurningRadius)) * 180.f / PI;
// Similar to above, if the unit is almost oriented towards its
// destination, just set the angle.
if (abs (abs (Rotation) - abs (angle)) < abs (turn))
{
Rotation = angle;
}
// Else, rotate the unit. First, the direction in which the unit should
// turn must be computed, it should choose the smaller turning circle.
// This is also non-trivial, i didn't even
// bothered to understand the proof, why this is doing the right thing,
// but it works.
else
{
float rot = 360.f - Rotation;
float x = sin (rot * PI / 180.f);
float y = cos (rot * PI / 180.f);
sf::Vector2f front (x, y);
front += Position;
// Sarros' Rule
float det = front.x * Course.y + Position.x * front.y + Course.x * Position.y -
Position.y * front.x - front.y * Course.x - Course.y * Position.x;
// Finally, update the Rotation
Rotation += (det > 0) ? -turn : turn;
}
// Now move by distance along the new Rotation
float rot = 360.f - Rotation;
float x = sin (rot * PI / 180.f);
float y = cos (rot * PI / 180.f);
sf::Vector2f front (x, y);
normalize (front);
front *= distance;
Position -= front;
}
}
Ok, so this code works, i can see the unit moving on my screen as i want it
to. However, there are several things in the code, that don't make sense to
me, they are only there, because it works that way.
I have the strong feeling, that SFML and the standard C++ trigonometry
functions have a different understanding of angles. And SFML 2.0 made it
even worse!
The first thing to notice is that C++ functions deal with angles in radians,
whereas SFML Rotations use degrees. This can be dealt with fairly easy
with all these ugly PI / 180.f resp. 180.f / PI.
But now to the things that don't make sense to me.
Up in the beginning, when i compute 'angle', i have to add 90 degrees,
to make it match with the SFML Rotation. Why?
In the turning block, before i can feed the Rotation to the C++
trigonometry functions, i have to invert the Rotation.
(float rot = 360.f - Rotation;) Why?
In the end, when updating the Position, i have to use -=, whereas when
moving straight, i use +=. Why?
I'm open to the possibility, that i'm doing something retarded here.
I that case, i hope someone will explain it to me.