In this section we'll talk about creating your own AI, as opposed to using AI algorithms found elsewhere. The information presented here does not even scratch the surface of learning how to create any AI that's another subject for a much longer, more technical book. Here the information covers a specific AI.
Rules for Controlling Characters
Open run_away.fla in the Chapter09 directory. This file contains the enemy AI for an unfinished game called Grave Robber. Here, we will look at how enemies (also called "baddies") behave. The baddies are zombies, and the good guy (well, as good as a thief can be) is human he is controlled by you. You walk around trying to rob graves, and every time you do, the zombies try to "get" you. There are walls that you cannot pass through. In this file, there is no collision detection between the hero and the baddies, since we are only illustrating behavior. Warning: This file (and in fact every example file we use in this chapter) uses tiles, so if you haven't yet familiarized yourself with tile-based worlds, then you might want to take the time to do so (see Chapter 7). We will only discuss the ActionScript used in the AI for this file, not the world creation or how we handle wall collisions. Use Test Movie to take a look at this world. Move around and try to notice the behavior of the enemy zombies. The AI used here is very similar to the AI used in the Shark Attack! game. There is a script within the code that instantaneously changes the direction of the enemy's motion. To be concise, I'll call the running of this script an "update." Now let's look at the rules that the zombies follow in this update to produce their behavior: homing in on the thief.
So now we know the logic that is performed when the update script is executed. But when is it executed? Here are the conditions for which the update script can be executed:
Drawbacks and Solutions
Enemy ActionScriptOK, now it's finally time to look at the ActionScript used in this AI. This function, baddyAI(), is called in every frame. It loops through an array of enemies and determines if it is time for an update. If it is, then it performs the update. 1 function baddyAI() { 2 for (var i = 0; i<game.baddies.length; ++i) { 3 var ob = game.baddies[i]; 4 ++ob.time; 5 var cell_x = Math.ceil(ob.x/game.cellWidth); 6 var cell_y = Math.ceil(ob.x/game.cellWidth); 7 var cell_over = game.tiles[cell_x][cell_y]; 8 v-ar cell_x_temp = Math.ceil(ob.tempx/game.cellWidth); 9 var cell_y_temp = Math.ceil(ob.tempy/game.cellWidth); 10 var cell_over_temp = game.tiles[cell_x_temp][cell_y_temp]; 11 if (!cell_over_temp.empty || ob.time == ob.maxtime) { 12 ob.time = 0; 13 ob.maxtime = 30+random(30); 14 ob.tempx = ob.x; 15 ob.tempy = ob.y; 16 var tempDir = ob.dir; 17 var xmov = 0; 18 var ymov = 0; 19 var speed = Math.abs(ob.speed); 20 var xsign = (game.char.x-ob.x)/ Math.abs((game.char.x-ob.x)); 21 var ysign = (game.char.y-ob.y)/ Math.abs((game.char.y-ob.y)); 22 if (random(10) == 0) { 23 var xsign = -1*xsign; 24 var ysign = -1*ysign; 25 } 26 if (xsign == ysign || xsign == -ysign) { 27 var ran = random(2); 28 if (ran == 0) { 29 var xsign = 0; 30 } else { 31 var ysign = 0; 32 } 33 } 34 if (xsign != 0) { 35 var ymov = 0; 36 var xmov = xsign*speed; 37 if (xmov>0) { 38 var dir = "right"; 39 } else { 40 var dir = "left"; 41 } 42 } else if (ysign != 0) { 43 var xmov = 0; 44 var ymov = ysign*speed; 45 if (ymov>0) { 46 var dir = "down"; 47 } else { 48 var dir = "up"; 49 } 50 } 51 ob.dir = dir; 52 ob.clip.gotoAndStop(dir); 53 ob.xmov = xmov; 54 ob.ymov = ymov; 55 } 56 } 57 } This is a pretty long function, but don't panic there is a lot of reappearing information. That is mostly because of the several if statements and because everything we do for the x direction we also do for the y direction. As with many of the files created in this book, here we have an object called game that stores information about the game. There is an array called baddies stored in game that contains one object for each enemy ("baddy") in the game. This function loops through the baddies array and checks out each baddy object to determine if it is time to run an update. Line 3 sets a temporary reference called ob to the current enemy that we are inspecting in the baddies array. In the next line we increment the time variable in ob. Remember that one of the conditions to determine if it is time for an update is if maxtime is the same as time. We will perform this check further down (line 11). In lines 5 and 6 we determine which cell the enemy is currently over, and in lines 8 and 9 we determine which cell the enemy would be over at the end of the frame. The cell that the enemy would be over at the end of the frame is given a temporary reference called cell_over_temp. In line 11 we check two conditions to determine if it is time for an update. First, if cell_over_temp is not empty (that is, if it contains an object), then we perform an update. Second, if the time variable is the same as the maxtime variable on the enemy object, then we also do an update. Let's look at the update (starting in line 12). First we set time back to 0 so that the counter will start over. Next we semi-randomly set a new maxtime value. There is nothing special about the numbers chosen for this randomization. You can change them and get different behaviors of the enemies. If you are interested in repurposing this AI and want some control over its difficulty level, this is one line of code you might want to play around with. In the next two lines we set the enemy's position to where it was at the beginning of the frame (lines 14 and 15). Then we store the current direction of the enemy as tempDir. This is a string value that is "left", "right", "up", or "down". We then set the values of the x and y velocities to 0 so that we can reassign them from scratch (lines 17 and 18). In lines 20 and 21 we determine the sign for the x and y directions, specifying where the hero is with respect to this enemy. Remember, these can have values of 1, 0, or 1. In lines 22 24 we insert the random dumbing-down process mentioned earlier in this section. This will cause the script to reverse the direction of an enemy approximately one out of every ten times it runs. In line 26 we determine whether the enemy is in the same row or column as the hero. If he is not, then we randomly choose either the x direction or the y direction to move in. The direction we do not want to move in we set to 0. So, when xsign is set to 0, we will move toward the hero in the y direction. Next, in lines 34 39 we perform similar tasks for either the x or the y direction, depending on which is non-zero. For the direction that is non-zero we set the speed in that direction and also set the temporary variable dir to store the string value of the direction of motion. This is then used in line 52 to display a certain frame in the enemy movie clip so that the zombie appears to be walking in the correct direction. In lines 53 and 54 we store the newly established x and y velocities on the enemy object.
That's all of it! As far as AIs go, this one is elementary, but for simple games it is good enough. |