YoYo Games Wiki

Basic AI

From YoYoGames Wiki

Portions of content from this page are originally found in issue 9 of GMTech Magazine, written by bendodge.

This tutorial teaches you the logic of a simple AI system for a top-down shooter. Since making a good top-down shooter takes some skill, I'll assume that you know what you're doing. So if you haven't gotten your game's basic movement and shooting down this tutorial probably isn't for you.

Getting Started

First, we need to think about what we want our AI to do for us. For this tutorial, we'll make an AI that runs around and tries to shoot the player while strafing keeping his own health above a set minimum. The AI will also have a setting that adjusts the accuracy of its shots. Next, let's outline the environment our AI has to live in. Let's assume you have walls, ammo and health. To help our AI navigate, you need to set up an A* pathfinding system. The A* algorithm uses a grid to find the shortest route around obstacles, and it does this by examining a grid. The result of the algorithm is stored in a path. I recommend that you use the potential step method once the AI is close to the target. I will assume that you can make your own A* algorithm, but I have included a basic example in case you are having trouble.

Scripts

scr_ai_init()

   globalvar bot_health; //declare our bot's
   //health as a global var
   //A* setup code
   //init vars
   bot_health = 100;
   bot_ammo = 25;

(You'll need to integrate bot_ammo into your shooting system.)

Now that you have your pathfinding set up, we need to decide how we want to design our AI's brain. We'll use a variable status to indicate what the bot's current objective is. The status actions are arranged by priority, with 0 being the lowest and 4 being the highest. This code should be in the initialization script.

   //status variable
   //0 means doing nothing
   //1 means finding the enemy
   //2 means engaging the enemy
   //3 means looking for ammo
   //4 means looking for health
   status = 0; //the bot starts doing nothing

scr_ai_brain()

Now let's make a main script that is run on a repeating alarm or when certain events trigger it. The script will check the bot's priorities and current environment to see if the status need changed. I'll leave the movement up to you, since there are so many different ways you could have implemented your movement.

   //scr_ai_brain
   var cenemy; //closest enemy
   cenemy = instance_nearest(x,y,obj_enemy);
   if status == 0 //if doing nothing
   {
   if bot_health < 30 then {scr_ai_health();exit}
   else
   if bot_ammo < 10 then {scr_ai_ammo(); exit}
   else
   if point_distance(x,y,cenemy.x,cenemy.y) < 100 then {scr_ai_engage(); exit}
   else
   scr_ai_find();
   }
   if status == 1 //if finding the enemy
   {
   if point_distance(x,y,cenemy.x,cenemy.y) < 100 then scr_ai_engage(); //adjust distance
   // as needed
   //some sort of stuck movement checker here
   else {exit}
   }
   if status == 2 //if engaging the enemy
   {
   if bot_health < 30 then {scr_ai_health();
   exit}
   else
   if bot_ammo < 10 then {scr_ai_ammo(); exit}
   else
   {scr_ai_engage(); exit}
   }
   if status ==3 //if looking for ammo
   {
   if bot_health < 30 then {scr_ai_health();
   exit}
   }
   //nothing is needed for status 4
   //reset alarm here

You might notice that several scripts are called here. Specifically, scr_ai_find(), scr_ai_health(), scr_ai_ammo(), and scr_ai_engage(). We'll outline these next. I won't give you these scripts in detail; we'll just cover the concepts. scr_ai_find()

Here you will need to use instance_nearest() to find the nearest enemy player and make a new path to the enemy's location. The brain script should change the AI from “find” to “engage” mode when the bot is somewhere close to the target. If it doesn't, you may need to shorten the repeat timer for the brain script.

scr_ai_health()

In this script you need to use instance_nearest() to find the nearest health powerup, and then compute a new path to get to it. The AI's brain should still be running on its timer. You'll need to add to the bot_heath variable in the collision event with the health object and then call the brain script.

scr_ai_ammo()

Same idea here; you need to use instance_nearest() to find the nearest ammo powerup and then compute a new path to get to it. The AI's brain should still be running on its timer during this. You'll need to use to the bot_ammo variable in the collision event with the ammo box and then call the brain script.

scr_ai_engage()

Ok, this one will be a little more tricky. What you want to do is use the choose() function to choose between strafing or shooting at the enemy. Obviously, you will want there to be many more chances to choose shooting than moving. The shooting should be done with a bit of randomization. Here's an example:

   //random shooting accuracy
   var cenemy, denemy;
   cenemy = instance_nearest(x,y,obj_enemy); //the
   //nearest enemy
   denemy = point_direction(x,y,cenemy.x,cenemy.y);
   //direction of enemy
   randomize();
   denemy+=random(20); //lower this to change
   //accuracy
   randomize();
   denemy-=random(20); //lower this to change
   //accuracy


Conclusion

So the overall idea is that your AI frequently runs a priority scripts and takes action based on its environment. Here are some things you need to watch out for and tweak:

1. Your A* implementation. This is a whole different subject, so I'm leaving it out of this short tutorial. The included example can help get you started.

2. The repeat interval of the AI brain script.

3. scr_ai_engage() needs to have good balance between moving and shooting.

4. Adjust the priority triggers to suit your game.

I hope that this can help you jumpstart your game's AI, and I'll look forward to playing it!