4

I have created enemies that chase the player around a game, but there is a problem. The enemies are too perfect and quickly converge on each other when they get close to the player. This is because they just move in the direction of the player each time the game updates.

I would like to introduce some randomness into the enemies, probably with the angle of motion. This is what I have so far (I haven't optimized it as it's not done yet, so I'm aware of the high overhead):

Angle = (float)Math.Atan2((HeroPosition - Position).Y, (HeroPosition - Position).X)// Positions are Vector2s
GlobalForce Propellant = new GlobalForce((float)Math.Cos(Angle) / 2, (float)Math.Sin(Angle) / 2);
ApplyForce(Propellant);

If I try to add a random number to the angle there are a couple of problems:

  1. They all get updated so quickly after each other that the seed time is the same for all of them

  2. The random number is so different on each update that the angle of the enemy jumps around erratically.

So what I want to know is: How do most games get around this problem? How should I make the enemies take different paths (without having access to the list of other enemies)?

EDIT:

This is the code I am using after the suggestions below for future passers by:

Angle = (float)Math.Atan2((HeroPosition - Position).Y, (HeroPosition - Position).X);
GlobalForce Propellant = new GlobalForce((float)Math.Cos(Angle) / 2, (float)Math.Sin(Angle) / 2);
ApplyForce(Propellant);

foreach (Enemy e in OtherEnemies)
{
    if (e != this)
    {
        if ((e.Position - Position).Length() < 64)
        {
            float angleBetween = MathHelper.TwoPi-(float)Math.Atan2((e.Position-Position).Y, (e.Position-Position).X);
            GlobalForce avoidance = new GlobalForce((float)Math.Cos(angleBetween)*2, (float)Math.Sin(angleBetween)*2);
            ApplyForce(avoidance);
        }
    }
}
annonymously
  • 4,708
  • 6
  • 33
  • 47

4 Answers4

4

A significant portion of AI tactics (as well as human tactics BTW) is not only knowing and acting on where the enemy is, but also knowing and acting on where your allies are. Most of the simplest and most well-known maneuvers are impossible without that (think encirclement).

Much of this comes down to "dont' get too close to an ally, if you can avoid it".

So basically what you need to do is manage your force in a way, that it is not only attracted by the enemy, but also rejected by an ally (less so). Combine this with a two-stage wayfinder and you're done.

Eugen Rieck
  • 64,175
  • 10
  • 70
  • 92
  • It looks like I'm going to have to give each instance a reference of the list of enemies and avoid the allies after all :) – annonymously Jan 10 '12 at 09:37
  • This depends on your use case: I suspect your characters will have some sort of detection horizon, so you could just use the detected allies as pushback providers. I think, this would work out as a rather realistic movement pattern. – Eugen Rieck Jan 10 '12 at 09:52
2

Most games get around this by having the enemies block each other, so that they don't move onto the same space.

This can be implemented as either a hard constraint (e.g. only one enemy is allowed in each square, no other enemies can move into an already occupied square) or a soft constraint (some hidden "force" that pushes enemies apart if they get too close to each other).

mikera
  • 105,238
  • 25
  • 256
  • 415
  • what about if each enemy is independent and has no access to the positions of any other enemies? – annonymously Jan 10 '12 at 09:31
  • why do they have no access? you should be able to give them access to this data.... perhaps through a "findNearbyUnits()" method that searches the surrounding map region. – mikera Jan 10 '12 at 09:42
  • I've now decided to pass a reference to the list of enemies in the update method and calculate the angle between them, then create a force to push them apart if they're too close – annonymously Jan 10 '12 at 09:45
  • sounds good. although consider putting the list of enemies in some kind of overall "game state" object that gets passed to the update method. This way you will be able to access not only the list of enemies but various other data via this object. In the long run, this will save you from passing a lot of parameters around..... – mikera Jan 10 '12 at 09:52
  • Would be a good idea for most projects but in this case, this is the only thing that these enemies will need. Thanks for the suggestion though :) – annonymously Jan 10 '12 at 09:53
  • Just finished implementing it, I'll post the intermediate code. It works great now though. They circle the player and attack him one at a time (and I'm not actually making them do that!). – annonymously Jan 10 '12 at 09:58
2

An intermediate solution is to have different strategies for the different AI's. e.g. one AI that homes in on where the player is now, another AI that homes in on where the player is going, another that habitually tries to hang out a few units South of the player, except when the player is moving straight for it, ....

It's not as good as having a strategy where your AI's take into account where their friends are, but (when written properly) it will have them behave differently.

1

Last year I had to code a PACMAN game for one of the courses I took

I used the A-Star search algorithm for the enemies

Running this algorithm straight-forward will yield perfect enemies.

The trick is to add some randomness to the outcome of the algorithm - make the enemy 'make mistakes' as the difficulty level goes down (i.e. go to a different direction than the algorithm yielded, as the difficulty level lowers, the enemey makes more 'mistakes')

Shai
  • 25,159
  • 9
  • 44
  • 67