Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

This question already has an answer here:

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

share|improve this question

marked as duplicate by Byte56, Sean Middleditch, bummzack, Josh Petrie, Trevor Powell Feb 20 '13 at 8:36

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

1  
Microsoft XNA Examples provides an excellent method. –  Dialock Feb 19 '13 at 1:17
1  
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. –  user1075940 Feb 19 '13 at 9:55
    
@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. –  Ree Feb 19 '13 at 22:17
    
@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. –  Ree Feb 21 '13 at 1:33

5 Answers 5

up vote 1 down vote accepted

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.

share|improve this answer
    
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. –  Ree Feb 18 '13 at 23:12
    
Ahh, thanks for the correction. I'll edit my post. –  Gigggas Feb 19 '13 at 5:11

@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);
        }
share|improve this answer

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º.

share|improve this answer
    
I am still having the same problem with the rotation going the long way round. –  Ree Feb 19 '13 at 0:11

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.)

share|improve this answer

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));
    }
}
share|improve this answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.