Gaming Your Way

May contain nuts.

So how does the NPC AI in Outpost:Swarm work ?

Now Outpost:Swarm is live I thought it may be an idea to explain how I did your in-game partners AI.

If you've ever read up on Boids you'll know they have 3 simple rules,

Separation

Alignment

Cohesion

And all the examples you'll see are bird like objects flying around, maybe towards your mouse pointer, maybe avoiding obstacles. All seems simple enough. Adding them to a real game however quite a bit trickier.

For separation we have to ensure the NPC is avoiding both the player and all the baddies. Firstly we find the distance to the players sprite using a simple

distance=dx*dx + dy*dy;

Like you would in your usual circle to circle tests. If we're too close then we need to repel the NPC from the player, via:

tmpPoint1.x+=dx+dx;

tmpPoint1.y+=dy+dy;


Where tmpPoint1 is just a new Point(0,0);

That's part 1 of the test done, the second is checking against all the baddies, and there can be a load at any one time. What I did was use a flip flop, every even frame we get a list of all the possible neighbours ( Baddies which are close enough to care about, if they're on the other side of the screen then we can skip them ), every odd frame we do exactly the same distance check as we did above.


Finally we divide our Point value,


tmpPoint1.x/=speedDivisor;

tmpPoint1.y/=speedDivisor;

( private var speedDivisor:Number=20; )

 

This keeps the values within a respectable range so we don't move too fast.

Outpost:Swarm

The next rule is alignment. Lucky for us we don't care about that in this case, we're not creating a flock of birds or swarm of insects, we just want one guy to look fairly smart and follow his friend around.

Cohesion in this case means following. We do another distance check to the player, but this time with a greater radius ( We want them close and for the NPC not to lose sight of the player, but we don't want them virtually kissing. That's planned for the sequel, Outpost:Date&Fuck ).

var dx:Number=bodyXPos-targetX;

var dy:Number=bodyYPos-targetY;

var distance:Number=dx*dx + dy*dy;

if(distance<3000){

  tmpPoint2.x=tmpPoint2.y=0;

  return tmpPoint2;

}

 

tmpPoint2.x=(targetX-bodyXPos)/speedDivisor;

tmpPoint2.y=(targetY-bodyYPos)/speedDivisor;


Note the use of targetX/Y, rather than PlayerX/Y, we'll come back to that at the end of my presentation. As you can see, it's pretty much the same thing as before, and something I'm sure most of you have done with your circle to circle checks.


Right, we've got two Points after running our rules, time to calculate the NPC's velocity.


velocity.x=rule1V.x+rule2V.x;

velocity.y=rule1V.y+rule2V.y;


Then we do a quick test to make sure the velocity in both directions isn't greater than the max speed we've set for the NPC, we don't want him outrunning the player ( Or actually, not to outrun the player enough that people will notice ).

The next part was the tricky one, we can move him around fine, but what about the walls ? It turned out to be surprisingly simple. We use good ol' tile based checks. If there's a wall on the horizontal of the NPC we set velocity.x=0; and then the same with the y. 

We're finally there, we just finish off with

sprite.x+=velocity.x;

sprite.y+=velocity.y;


Cool, and that's how you can use Boids in real life ( The same principle handles the baddie movement ).
 
Let's test that out in game. Excellent it works, and if I manage to lose the NPC behind a wall then... oh tits, he'll just sit there. That's not a great look for a hard as nails space guy with a big ass gun.
 
Check this level map out,  

AI path

Within each level we lay down a path for the AI. Every couple of frames we look to see if the NPC can "see" the player using a line of sight. If he can't it means we've ditched him behind a wall.
When that happens we fire out 4 rays from the NPC until it hits one of those path tiles ( Remember at the heart of the game is a tile engine running alongside the purely physics based one ). Once we've done that we can easily find out which is the nearest part of the path, we change the TargetX/Y ( From above ) to our tile and the same code that moves him towards the player moves him towards the path.
All the time we're moving towards the path we're doing our line of sight checks to the player, if we spot him again we break off from the path and go back to following the player.
 
It's not fool proof, if he gets lost then he's only going to make his way to the path and then not do anything really clever, but because the levels are small enough you should soon bump into him again to get him following the player. Also, he doesn't really need to be that smart when he's off on his own, we just want to create the illusion that he's not a dumb arse, he doesn't need to be Stephen Hawking ( Perhaps not the best example when talking about movement ). Finally, we're still using Flash, we don't have all the CPU time in the world to do something really fantastic.
 
And that's how we move the NPC.
 
Squize.
 
PS. Wow, looks like I've got about 40 fonts / sizes in this, nasty, but it took enough time to write this up, I'm going to swallow it looking slightly ugly.
Comments are closed