Calculating the angle towards another object...

Started by
2 comments, last by Toolmaker 17 years, 5 months ago
I have a weird problem with my game, namely that units all the sudden bend towards a different angle when they're approaching said object/location. This is the idea: The game is a 2D tile-based RTS, and when units are moving, they calculate their angle towards to target(-area) and update their position accordingly. This happens every frame to make sure the unit arrives at the exact center of the tile. My code to calculate the angle is:

        protected float CalculateDesiredAngle(float opposite, float adjacent, out float hypotenuse)
        {
            // Calculate the desired angle to the unit
            hypotenuse = (float)Math.Sqrt(opposite * opposite + adjacent * adjacent);
            float desiredAngle = Math.Abs((float)Math.Atan(opposite / adjacent));

            // Correct the angle along the unit circle
            if (opposite < 0.0f)
            {
                if (adjacent == 0.0f)
                    desiredAngle += (float)Math.PI;
                else if (adjacent > 0)
                    desiredAngle += (float)Math.PI;
                else
                    desiredAngle += (float)Math.PI * 1.5f;
            }
            else if (opposite > 0.0f)
            {
                if (adjacent > 0)
                    desiredAngle += (float)Math.PI * 0.5f;
            }
            else
            {
                if (adjacent > 0.0)
                    desiredAngle += (float)Math.PI;
            }

            // Correct the desiredAngle so it's not negative or higher than 2 PI
            if (desiredAngle >= (float)Math.PI * 2)
                desiredAngle -= (float)Math.PI * 2;
            else if (desiredAngle < 0.0f)
                desiredAngle += (float)Math.PI * 2;

            return desiredAngle;
        }

It checked by not moving the unit, and it worked, except that 0.0 points eastwards. Not a problem, since I can change how the unit is position when drawing. During update, I do this:

Point next = path[0];
nextTile.X = next.X * Tile.Size + Tile.Size / 2;
nextTile.Y = next.Y * Tile.Size + Tile.Size / 2;
float hypotenuse;
float desiredAngle = CalculateDesiredAngle((next.X * Tile.Size + Tile.Size / 2) - Position.X, (next.Y * Tile.Size + Tile.Size / 2) - Position.Y, out hypotenuse);
if (angle != desiredAngle) // Make sure we don't rotate too much
{
    if (Rotate(desiredAngle, (float)Math.PI, deltaTime) != desiredAngle)
    {
        AdjustDirection();
        return false; // We haven't reached the correct angle yet
    }

    AdjustDirection();
}

// ... Code related to reserving tiles, checking if movement can occur, etc.

float x = ((float)Math.Cos(angle) * speed) * deltaTime;
float y = ((float)Math.Sin(angle) * speed) * deltaTime;
// Some code related to rounding down movement
position.X += x;
position.Y += y;

Now, it starts out perfectly, and moves up:

Angle = 270,7619
Angle = 270,7822
Angle = 270,7832
Angle = 270,7843
Angle = 270,7854
...
Angle = 276,9893
Angle = 277,5833
Angle = 278,2739
Angle = 279,061
Angle = 279,9727
Angle = 280,9644
Angle = 282,0956
Angle = 283,4937
As you can see, the further it moves towards the target tile, the more the angle bends off to the east. I have no idea what I am doing wrong here, so any help might be appreciated. Toolmaker

Advertisement
I didn't look any further than this in your code, but using an inverse tangent to compute the angle can be problematic (mostly due to precision/stability problems, and handling of quadrants).

I can't say for sure if that's the cause of the problem, but if you have an 'atan2' function available you should use it instead.
What does the code for your AdjustDirection() function look like? My hunch is that something odd may be going on in there.

Also, when you do the comparison "if (angle != desiredAngle)" you may be falling into that if-block unintentionally. Since you are comparing floats, which were calculated using sin/cos/tan, it's a bit unlikely that they will be exactly equal as you might be expecting.
The AdjustDirection() function is used to change the visual angle of the unit. I'm using the tiles from Dune 2, and those units had 8 different directions. The AdjustDirection compares the various angles and based on the angle, it determines which part of the sprite it needs by moving the source rectangle around. It doesn't touch the angle to write too.

About the angle != desiredAngle, in the Rotate() function, I first calculate the radians that is turned, then check if the unit would overturn, and limit it if that is the case. It always results in the angle being exactly the desired one.

I'll try atan2.

Toolmaker

This topic is closed to new replies.

Advertisement