Sign in to follow this  
xDan

heading towards enemy

Recommended Posts

xDan    194
Hi how can I get my enemy to follow the player? I have the enemy's position, enemy rotation and players's position. This code is from the enemies perspective (so the player is the enemy).
        dReal *myPos = getPosition();
        
        f32 z = (nextPos[2]-myPos[2]);
        f32 x = (nextPos[0]-myPos[0]);
        dReal bearingToEnemy = atan( z/x );
        
        // convert to radians
        dReal heading = (getRotation().Y) * 3.1415926535897932384626433832795 / 180.0;
        
        printf("%f, %f\n",heading,bearingToEnemy);
        
        if ( bearingToEnemy < heading)
        {
            goLeft();
        }
        else
        {
            goRight();
        }
        
        goForwards();


This doesn't seem to work... the enemy seems to swerve towards the player but then goes off in a straight line. I wondered if the problem is with my trigonometry or something to do with the angles when they go past 360 degrees and jump to zero again (or whatever the radian equivilant is) Cheers.

Share this post


Link to post
Share on other sites
Winegums    286
at a first glance i notice your if statement looks a bit sus...

since if the players bearing is less than the heading they go left, but if not they ALLWAYS go right, is that intentional? what about when bearing == heading?

Share this post


Link to post
Share on other sites
JWalsh    498
Based on an "if" statement you are either going left or right, but then you're always going forward.

I suspect you need a third case for the "forward" indicating that bearing == heading.

Edit: Looks like winegums beat me to it. Good show, sir!

Cheers!

Share this post


Link to post
Share on other sites
xDan    194
if it's facing to the left of the target it should turn right, and vice versa.

(so it is intentional, at the moment any way)

If it goes past the target then it should turn back to the target. But it doesn't. I'm certain it has something to do with wrapping around of the angles. But there might also be problems with my trigonometry.

Share this post


Link to post
Share on other sites
Winegums    286
Quote:
Original post by xDan
if it's facing to the left of the target it should turn right, and vice versa.


yeah but thats not what your code is doing.

if facing right of enemy
{
goLeft();
}
else (it may be facing the enemy, or facing left of it)
{
goRight();
}


i can see how it may sort itself out, but i still feel that it should only check its trajectory if its not facing the target directly. this may or may not fix your problem, but at the very least it will avoid future problems.

Share this post


Link to post
Share on other sites
jyk    2094
First the usual suggestion: use atan2() if it's available, rather than atan().

It sounds like what you want is the signed relative angle between the enemy's forward vector and the vector to the player, which can be found as follows:
vector2 diff = player.pos - enemy.pos;
float relative_angle = atan2(perp_dot(player.forward, diff), dot(player.forward, diff));
If you're unsure about any of those terms, just ask.

Getting the actual angle could be useful in that you could have the enemy turn faster or slower depending on how far it needs to turn, or apply an ease-in-ease-out function. But for a simple binary result (is the player to the left, or to the right), all you need is the sign of perp_dot(player.forward, diff).

Share this post


Link to post
Share on other sites
xDan    194
I'm not sure exactly what the perp_dot is... I googled it and have this code:


dReal heading = (getRotation().Y + 90) * 3.1415926535897932384626433832795 / 180.0;

vector2df diff;
diff.X = nextPos[0] - myPos[0];
diff.Y = nextPos[2] - myPos[2];

vector2df player = vector2df(cos(heading), sin(heading));
float perp_dot = player.X*diff.Y - player.Y*diff.X;

float relative_angle = atan2(perp_dot, player.dotProduct(diff));

//printf("%f, %f\n",heading,bearingToEnemy);
printf("%f\n",relative_angle);

if ( relative_angle < 0 )
{
goLeft();
}
else
{
goRight();
}





It works all the while the player is in front of the enemy (edit: by "in front" I mean rotation of zero). Go about 90 degrees to the enemy and it will start spinning. When behind the enemy, it moves in the opposite direction to what it should to compensate.

The rotation is caused because the relative_angle is always negative. I disabled the moving forwards so I could test.

Without the +90 in "getRotation().Y + 90" it keeps spinning when you are in front, but stabilises around 90 degrees.

Share this post


Link to post
Share on other sites
Zahlman    1682
"perp_dot" is simply the so-called "2d cross product". Because a normal dot product between two lines gives you the cosine of an angle, rotating one of the lines 90 degrees gives you the cosine of (90 degrees minus the angle), which by the trig identities is the sine of the angle. Since the sign of the sine (pun intended) is positive for a small positive angle and negative for a small negative angle, it tells you whether the angle between the vectors is "positive" (i.e. you rotate counterclockwise, i.e. turn left, to get from the first to the second) or "negative". In fact, for small angles sin(x) is approximately equal to x (with x being in *radians* - in any event, it's approximately *proportional to* x with any measurement, as long as the angle is small), so you can use that to decide how hard to turn.

You might want to require a certain (probably very small) "threshold" deviance from being exactly aligned before the turning kicks in. Otherwise, depending on the frame rate and the "steering ratio", the enemy might appear to "wobble" slightly and rapidly while on a direct course (due to turning left and right on alternate frames).

Share this post


Link to post
Share on other sites
xDan    194
Thanks for the info on 2d crossproducts. I will probaly add some threshold, but I can't even think of doing that until I get the above code working...

Share this post


Link to post
Share on other sites
xDan    194
I am still crying to myself throughout the night over this problem. :-(
Can someone see what's wrong with my code?

I also tried this after reading through some more vector tutorials:
        dReal *myPos = getPosition();

// convert to radians
dReal heading = (getRotation().Y) * 3.1415926535897932384626433832795 / 180.0;

vector2df enemyVec = vector2df(nextPos[0] - myPos[0], nextPos[2] - myPos[2]);
vector2df playerVec = vector2df(cos(heading), sin(heading));

enemyVec.normalize();
playerVec.normalize();

f32 dot = enemyVec.X*playerVec.X + enemyVec.Y*playerVec.Y;

f32 relative_angle = acos(dot) - 3.142/2.0;

//float perp_dot = player.X*diff.Y - player.Y*diff.X;
//float relative_angle = atan2(perp_dot, player.dotProduct(diff));

printf("%f\n",relative_angle);

if ( relative_angle < 0 )
{
goLeft();
}
else
{
goRight();
}


But it still spins like a mad spinny thing at some angles :-(

Share this post


Link to post
Share on other sites
ItsDoh    162
This link demonstrates dot product:

http://www.falstad.com/dotproduct/


Checking if the dot product is < or > 0 isn't what you want I think, since it's only <0 when the objet is actually behind (more than 90degrees away from) the used vector.

Share this post


Link to post
Share on other sites
jyk    2094
Quote:
Original post by xDan
I am still crying to myself throughout the night over this problem. :-(
Can someone see what's wrong with my code?

I also tried this after reading through some more vector tutorials:
*** Source Snippet Removed ***

But it still spins like a mad spinny thing at some angles :-(
As mentioned above, the dot product and acos() probably isn't what you want here. The perp_dot()/atan2() code that you have commented out looks correct though; did that not work?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this