3
\$\begingroup\$

Okay so i havebeen rotating my enemy to face the player using

float targetrotation = Math.Atan2(playerpos-enemypos);
enemy.rotation = targetrotation; (<this line of code i want to change)

This simple method has worked fine so far for getting the enemy to look at the player's position. However i decided to add some extra code to get the enemy to gradually rotate towards the player using this method:

if(enemy.rotation < targetrotation)
{
   enemy.rotation++;
}

if(enemy.rotation > targetrotation)
{
   enemy.rotation--;
}

Now the problem i am having is that when the player moves for example from 360 degree target to 1 degree then the enemy rotates the long way round to move to the 1 degree instead of simply adding 1 degree. Any help on how to correct this problem would be welcomed. I have a picture below:

Rotate Enemy

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Microsoft XNA Examples provides an excellent method. \$\endgroup\$
    – Dialock
    Commented Feb 19, 2013 at 1:17
  • 1
    \$\begingroup\$ Have you tried Quaternions SLERP? en.wikipedia.org/wiki/Slerp. It may looks scary ,but don't worry!The whole structure is already implemented in XNA math lib. \$\endgroup\$ Commented Feb 19, 2013 at 9:55
  • \$\begingroup\$ @Dialock Thanks For the suggestion it works like a charm for rotating 2d sprites. However i also have a body in farseer that needs to be rotated using forces and since this function sets the rotation some extra work will need to be done to get it to work properly by converting to force. \$\endgroup\$
    – Ree
    Commented Feb 19, 2013 at 22:17
  • \$\begingroup\$ @Dialock Just replaced all my rotations with your suggested method, it now works fine with farseer for steering. I forgot about the old SROT method when applying transformations it appears that i can get much more efficiency with farseer by setting rotation first for my enemies. Sorry about all of the notifications. \$\endgroup\$
    – Ree
    Commented Feb 21, 2013 at 1:33

5 Answers 5

3
\$\begingroup\$

You could try using this method instead (assuming rotation is represented in degrees):

//get difference in degrees between both angles
float rotationDifference = enemy.rotation - targetRotation;

//if difference is greater than 180 degrees, reverse rotating direction
//by adding or subtracting 360 degrees
if(Math.abs(rotationDifference) > 180){
    rotationDifference += rotationDifference > 0 ? -360 : 360; 
}

//based on difference, rotate enemy angle
if(rotationDifference < 0){
    enemy.rotation++;
}
else if(rotationDifference > 0){
    enemy.rotation--;
}

This method would require some editing if your rotations are not always whole numbers or if the angles are not represented in degrees.

\$\endgroup\$
2
  • \$\begingroup\$ there's a problem with your suggestion in that the enemy gets stuck in a rotation. However you managed to help me figure the problem out. Instead of multiplying the difference by -1 its better to make an if statement for when the rotation is >180 and another for when its<180. When the difference is >180 we need to minus 360, if it is < 180 then we need to add 360. This approach gives us the residual left and right turning <=180. Thanks for your help :)... ooh and the rest of your solution is fine, just had to remove the if(math.abs) bit. \$\endgroup\$
    – Ree
    Commented Feb 18, 2013 at 23:12
  • \$\begingroup\$ Ahh, thanks for the correction. I'll edit my post. \$\endgroup\$
    – Gigggas
    Commented Feb 19, 2013 at 5:11
3
\$\begingroup\$

You're probably better off not representing your orientations as angles in the first place, for a couple of good reasons: for one, because there's an inevitable discontinuity of representation somewhere; but even more importantly, because it leads to a host of unnecessary trig calls — the Atan2() call here is one example of that, but there are no doubt a host of Cos() and Sin() calls down in the rendering engine somewhere. Instead, I'd recommend representing orientations (and in particular, the enemy's facing) as a (normalized) vector of the direction they're pointing in; this lets you determine which side of your enemy's facing the player is on by doing a simple dot-product test between the vector from enemy to player and the normal to the enemy's facing, and since the normal to a 2d vector (x,y) is just the vector (y, -x), this leads to the following code that replaces all of the awkward code above with two multiplies and a compare:

Vector VecToPlayer = Player.Position-Enemy.Position;
if ( (VecToPlayer.X * Enemy.Orientation.Y) > (VecToPlayer.Y * Enemy.Orientation.X) ) {
  Enemy.TurnRight();
} else {
  Enemy.TurnLeft();
}

(Note that this code assumes a world where X points rightward and Y points up - that is, where the positive Y axis is 'left' of the positive X axis. If your world works the other way, you'll want to flip the TurnRight() and TurnLeft() calls. Also, you'll need to complicate this slightly to handle the case where your enemy is already facing in the correct direction, but I presume that's already being handled somewhere else.)

\$\endgroup\$
2
  • \$\begingroup\$ Wow! That looks like a very nice solution! I used cross-vectors before, but this solution seems much simpler. Does this approach also work if you don't have a point to focus, but a direction instead? \$\endgroup\$
    – Tara
    Commented May 3, 2015 at 17:12
  • \$\begingroup\$ NVM, made a mistake. Your answer should be the accepted one since it's a lot simpler. Also, to fix the case when the enemy is already facing the desired direction: Weight the rotation speed by the dot product between the currend direction and the desired one. \$\endgroup\$
    – Tara
    Commented May 3, 2015 at 17:20
1
\$\begingroup\$

@giggas thank you. here is how i managed to get it to work:

float rotationDifference = enemy.rotation - targetrotation;

        //if difference is greater than or less than 180 degrees, reverse rotating direction
        if ((rotationDifference) > 180)
        {
            rotationDifference -= 360;
        }
        if ((rotationDifference) < -180)
        {
            rotationDifference += 360;
        }


        //based on difference, rotate enemy angle
        if (rotationDifference < 0)
        {
            enemy.rotation ++;
        }
        else if (rotationDifferencedeg > 0)
        {
            enemy.rotation --;
        }

if you are using radians here is how i did it, i had to use radians btw.

        //this gets the target rotation based on the player and enemy position in radians
        float targetrotation = (float)Math.Atan2((double)(playerposition.Y - Tanker.Position.Y), (double)(playerposition.X - Tanker.Position.X));

        //calculate the amount of rotaation required
        float rotationDifference = hubrotation - targethubrotation;

        //if difference is greater than or less than pi radians, reverse rotating direction
        if ((rotationDifference) > Math.PI)
        {
            rotationDifference -= (float)Math.PI*2;
        }
        if ((rotationDifference) < -Math.PI)
        {
            rotationDifference += (float)Math.PI*2;
        }
        int rotationDifferencedeg = (int)(MathHelper.ToDegrees(rotationDifference));

        //based on difference, rotate enemy angle
        if (rotationDifferencedeg < 0)
        {
            hubrotation += MathHelper.ToRadians(1);
        }
        else if (rotationDifferencedeg > 0)
        {
            hubrotation -= MathHelper.ToRadians(1);
        }
\$\endgroup\$
1
\$\begingroup\$

I'd recommend simply doing the following:

    if(enemy.rotation % 360 < targetrotation)
    {
       enemy.rotation++;
    }

    if(enemy.rotation % 360 > targetrotation)
    {
       enemy.rotation--;-
    }

This is because, when dealing with degrees, starting from 360 it goes back to 0. Therefore, while 361º may seem to be greater than 30º, 361 is actually just 1º.

\$\endgroup\$
1
  • \$\begingroup\$ I am still having the same problem with the rotation going the long way round. \$\endgroup\$
    – Ree
    Commented Feb 19, 2013 at 0:11
1
\$\begingroup\$

I rotate by the result of:

const float Mob::getAngleToVector(irr::core::vector3df target)
{
    irr::core::vector3df mobDirection = IRR.getRotatedVector(irr::core::vector3df(0,0,1), Rotation);
    irr::core::vector3df targetDirection = target - Position;

    mobDirection.normalize();
    targetDirection.normalize();

    irr::core::vector3df targetNormal(-targetDirection.Z, 1, targetDirection.X);

    if(mobDirection.dotProduct(targetNormal) > 0)
    {
        return acos(mobDirection.dotProduct(targetDirection));
    }
    else
    {
        return 0-acos(mobDirection.dotProduct(targetDirection));
    }
}
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged .