Tutorials

February 12, 2009

AS3 Flash Games for Beginners: Learn How to Make Enemies with Basic AI!!!

More articles by »
Written by: Par
Tags: , , , , , ,
basics3_tn

Share on TwitterShare on TumblrSubmit to StumbleUponSave on DeliciousDigg ThisSubmit to reddit

All games need enemies…. okay, maybe not tetris. But if you’re in some game where you fly a spaceship it is more than likely that there’s going to be an enemy or two. So in this tutorial we’re going to learn to make our enemy class, making enemies randomly appear on the stage and move, and make them fire bullets at us.

This is the 6th article in the AS3 Flash Games for Beginners series. If you have no read the previous articles you may want to consider returning to the beginning, here, and following from there. However, if you would like to start this tutorial without returning to the beginning… you’re going to need to download the source code from the last tutorial.

Source Code: Firing Weapons with Delay Zip Archive

Step 1: Making our Enemy

So this is always the fun part. If you are not an artist this will drive you insane.  Big colored squares tend to work well, you can always grab the Rectangle tool and make one of those.  But shooting squares isn’t fun at all… So you can try to make something like my ship. It’s just a bunch of shapes distorted to make a basic spaceship.  I do this by drawing a Square with the Rectangle Tool (R) and manipulating it by holding ALT and clicking on the outside edge and dragging.  ALT clicking creates a new point in the vector, try it out.

Anyway, after a couple minutes of tweaking I get this:

basics1_6step1-1

My Enemy Spaceship, the Stinger.

Make this guy a MovieClip with the registration point moved to near the back center… For my ship it’s where the red comes to a point in the center. If you’re wondering how to adjust the registration point of a MovieClip, it’s simple. Go inside the clip and select all your artwork and move it around until the Registration (it’s the black and white plus sign) is placed correctly. So really you never move the Registration point, you move your artwork to suit the Registration. Name the MovieClip “stinger”.

Now set up your linkage.  Do this by right clicking “stinger” in the library selecting Linkage… Export for ActionScript and set our class to com.asgamer.basics1.Stinger. OK it.

Now we need to create our Stinger class. So in our com/asgamer/basics1 folder let’s make a Stinger.as file and fill it with this code:

package com.asgamer.basics1
{
 
	import flash.display.MovieClip;
	import flash.display.Stage;
	import flash.events.Event;
 
	public class Stinger extends MovieClip
	{
 
		private var stageRef:Stage;
		private var vy:Number = 3; //y velocity
		private var ay:Number = .4; //y acceleration
		private var target:Ship;
 
		public function Stinger(stageRef:Stage, target:Ship) : void
		{
			this.stageRef = stageRef;
			this.target = target;
 
			x = Math.random() * stageRef.stageWidth;
			y = -5;
 
			addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
		}
 
		private function loop(e:Event) : void
		{
			vy += ay;
			y += vy;
 
			if (y > stageRef.stageHeight)
				removeSelf();
		}
 
		private function removeSelf() : void {
 
			removeEventListener(Event.ENTER_FRAME, loop);
 
			if (stageRef.contains(this))
				stageRef.removeChild(this);
		}
 
	}
 
}

Alright… the breakdown:

  • First some imports, our Stinger class extends MovieClip, then our class variables. Let’s talk about those class variables.
  • stageRef:Stage. Seriously, as much as we pass this around, you know this by now… it’s a reference to our stage.
  • vy:Number. Velocity in the y axis.
  • ay:Number. Acceleration on the y axis… Stinger will increase his speed by this much every frame.
  • target:Ship. If you’re paying attention you’ll notice this target variable is an object of Class Ship. That what the Ship we control is an object of. Well that’s what the target variable will keep… a reference to our ship. This way our Stinger ship knows who to “target”.
  • Our constructor function isn’t anything different than what we’ve done before. We take the parameters passed into our function and put them in class variables (stageRef and target). We pick a random position across the stage in x to start our stinger ship. We set y to -5, this is because we want stinger to be off stage when he starts and move on stage. So it doesn’t look like he appears from no where.  Last we setup our event listener for on enter frame.
  • Inside our loop function we put the velocity and acceleration to work.
  • vy += ay; Our velocity increases by acceleration.
  • y += vy; y increases by our velocity.
  • Then we see if y is off the bottom of the stage… if so call removeSelf();
  • private function removeSelf(). This is the same removeSelf function we use for our lasers.  Honestly theirs not a single difference. We kill our Event Listener, then we check that the MovieClip is on the stage, if so we remove it.

So we have a working ship. Now we need to spawn our ship randomly.

Step 2: Randomly Spawning Enemies

So where do we want to add our enemy from? Probably the same place we added the ship we control, the Engine class.  Here’s the new Engine class code… I removed all the old comments so any comments in this class are related to what we have changed or added:

package com.asgamer.basics1
{
	import flash.display.MovieClip;
	import flash.display.Stage;
	import flash.events.Event; //don't forget to make sure this is imported
 
	public class Engine extends MovieClip
	{
 
		private var numStars:int = 80;
		// we need to keep track of our enemies.
		private var enemyList:Array = new Array();
		// moved ourShip to a class variable.
		private var ourShip:Ship;
 
		public function Engine() : void
		{
			//removed the var ourShip:Ship because we declared it above.
			ourShip = new Ship(stage);
			stage.addChild(ourShip);
 
			ourShip.x = stage.stageWidth / 2;
			ourShip.y = stage.stageHeight / 2;
 
			for (var i:int = 0; i < numStars; i++)
			{
				stage.addChildAt(new Star(stage), stage.getChildIndex(ourShip));
			}
 
			//running a loop now.... so we can keep creating enemies randomly.
			addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
 
		}
 
		//our loop function
		private function loop(e:Event) : void
		{
			//run if condition is met.
			if (Math.floor(Math.random() * 90) == 5)
			{
				//create our enemy
				var enemy:Stinger = new Stinger(stage, ourShip);
 
				//listen for enemy being removed from stage
				enemy.addEventListener(Event.REMOVED_FROM_STAGE, removeEnemy, false, 0, true);
 
				//add our enemy to the enemyList
				enemyList.push(enemy);
 
				stage.addChild(enemy);
			}
		}
 
		//run when an enemy is removed from stage
		private function removeEnemy(e:Event)
		{
			enemyList.splice(enemyList.indexOf(e.currentTarget), 1);
		}
 
	}
 
}

Alright… break this down:

  • private var enemyList:Array = new Array(); We’re creating an array. This will keep up with all our enemy ships… Right now we just have the Stinger class of enemy but if we created more, we’d add them to this array as well. We need this so we can check if our lasers hit the enemy later.
  • private var ourShip:Ship; we just moved our ships declaration to outside the constructor function. We needed to do this so we can pass ourShip through as the target for our enemy ship. You’ll need to remove declaring the ship in our constructor so it uses our class variable instead (like the above code shows)
  • We added a simple event listener so we can run code on every enter frame.
  • if (Math.floor(Math.random() * 90) == 5). So we’re wanting Math.floor(Math.random()*90) to equal 5… tend we can run the code inside our conditional. Math.random returns us a number between 0 and 1.  It’s likely not a whole number so if we multiple it by 90 we likely won’t get a whole number either. That means the chances of us getting 5 are really really really really slim(Considering it would have to be .055555555555555555…  to work)  So we floor(or round down) the number Math.random()*90 gives us. So 5.15 & 5.76 would become 5. Now our odds are a lot better that the condition will be met. If you want to better the odds still, lower 90 to a lesser number. If you want them to be worse, make 90 a higher number.
  • var enemy:Stinger = new Stinger(stage, ourShip); Sweet, this is the class we just made… remember we needed the stage and our target. We pass through the stage first. The target is the second parameter, so we pass through our ship… because we want the enemy to fire at us.
  • enemy.addEventListener(Event.REMOVED_FROM_STAGE, removeEnemy, false, 0, true); Nothing too complicated here, just different. Event.REMOVED_FROM_STAGE fires when a DisplayObject is removed from the stage… so when we call stageRef.removeChild(this) inside of our Stinger class… we fire the event.
  • enemyList.push(enemy); Add a new enemy to the enemyList array. the push function just drops the parameter being added as the last item in the array.
  • stage.addChild(enemy); Yeah, just add him to the stage.
  • enemyList.splice(enemyList.indexOf(e.currentTarget), 1); Okay this probably looks complicated because it’s a lot of new stuff, but it’s not.  Let’s take it apart in pieces.
  • e.currentTarget. We get the e from the parameter sent into removeEnemy. It’s our event object. Inside the event object is a variable called currentTarget that holds the object that caused the event. In our case it will hold the enemy ship that just got removed from the stage.
  • enemyList.indexOf(e.currentTarget). indexOf lets us accepts a parameter which it will search for in the array. If it finds the parameter, it will return it’s index in the array. So we are searching for where in our enemyList array that our enemy is located. In this case it will always be 0 because all our Stinger ships move at the same speed so the first one created will be the first one deleted. Just in case you didn’t know, the first index of an array is 0.
  • enemyList.splice(enemyList.indexOf(e.currentTarget), 1); the splice function of an array needs two parameters. First where to start it’s splice and second how far it needs to splice. We want to start at the object that just got removed from stage and we want to just remove it so we only need to splice 1 object.

Alright… a lot to explain there. Hope it wasn’t too confusing. Here’s our game:

Step 3: Creating the Enemy’s bullet

I’m not going to elaborate too much here, we’ve done this process a few times before, and we pretty have done this exactly when we made our ships laser.  I created a new MovieClip of a red ball with a pink dot in the center. I set it’s Registration to center and in the linkage I made it link to the com.asgamer.basics1.StingerBullet class. Now create a new as file and we’re going to drop this code into it:

package com.asgamer.basics1
{
 
	import flash.display.MovieClip;
	import flash.display.Stage;
	import flash.events.Event;
 
	public class StingerBullet extends MovieClip
	{
 
		private var stageRef:Stage;
		private var target:Ship;
 
		private var vx:Number;
 
		public function StingerBullet(stageRef:Stage, target:Ship, x:Number, y:Number, vx:Number) : void
		{
			this.stageRef = stageRef;
			this.target = target;
			this.x = x;
			this.y = y;
			this.vx = vx;
 
			addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
		}
 
		private function loop(e:Event) : void
		{
			x += vx;
 
			if (x > stageRef.stageWidth || x < 0)
				removeSelf();
		}
 
		private function removeSelf() : void
		{
			if (stageRef.contains(this))
				stageRef.removeChild(this);
		}
 
	}
 
}

This doesn’t need much of a breakdown but…

  • Our constructor function is looking for 5 parameters: our stage, a target(our ship), starting x, starting y, velocity in x. It needs the target so when we setup our hitTest in the next tutorial we know who to check if we are hitting. the velocity in x means that we can set the velocity we want our bullet to move at when we create the StingerBullet object.
  • Past this it’s self explanatory. The bullet moves at vx speed until it runs off the stage and at the point it is removed. simple.

Step 4: Making Flash Artificial Intelligence (AI)

That sounds really good, huh. Making artificial intelligence, sounds smart… sounds hard. Well our ship isn’t going to be thinking and it’s not that smart so it’s not that hard. But the enemy does know where we are and he will fire at us and that is all the AI we need. Make sure your Stinger class is open we’re going to add a few lines. In your loop function at the bottom of it add:

			if (y - 15 < target.y && y + 15 > target.y)
				fireWeapon();

breakdown:

  • if the enemy’s y location minus 15 is less than our ships y and the enemy’s y location plus 15 is greater than our ships y then our ship is within 15 pixels of the enemy either up or down.  So the enemy is near our ship in y, we want to fire the weapon…. let’s call fireWeapon().

Now we need to write a fireWeapon() function. Here it is:

		private function fireWeapon() : void
		{
			stageRef.addChild(new StingerBullet(stageRef, target, x, y, -5));
			stageRef.addChild(new StingerBullet(stageRef, target, x, y, 5));
		}

And the breakdown:

  • We simply add a bullet to the stage twice… the difference is one bullet has a vx of 5 and the other -5.  This is so the bullet will fire in both horizontal directions.

Now the last two snippets of code are all we need to fire our bullet with some intelligence… I know it’s not rocket science, it’s not even difficult, but it’s the  basic AI to make an effective enemy. He knows what he is firing at and he uses it to his advantage.   You’ll notice that he’ll sometimes fire two bullets on both sides… it’s because we’re not delaying the bullet fire. If you want him to only be able to fire once… you can set a delay or a canFire variable that you set to false once he fires. Personally I’m fine with the multiple fires but it’s your game :) so change it if you want.

Here’s the SWF:

Alright… part 6 is finished. Make sure to come back for the next tutorial as we will learn how to make the bullets hit our ship and the lasers hit our enemies. Here’s the link: Registering Hit Tests

Here’s the final source for this tutorial: Learn How to Make Enemies with Basic AI!!! Zip Archive

Thanks a lot for your time, leave me a comment or two… and post some links to what you are doing with the tutorial, especially if you’ve spiced it up a bit. I’d love to see it. Your version may even make it into a special article on the website :D

Similar Posts:


Share on TwitterShare on TumblrSubmit to StumbleUponSave on DeliciousDigg ThisSubmit to reddit


About the Author

Par
Hey! Don't be surprised, I'm a flash developer. While Flash is definitely one of my favorite languages to develop in, most of all I just like making games. If you want to see the games I've developed so far head over to my website, DigitallyBold, in the link below. If you want to know more about what I'm working on now and in the future be sure to follow me on twitter.




29 Comments


  1. How many years do you get photoshop design.
    You flash super.


  2. dimebagplan

    Man thanks for your time, those tutorials are awesome !
    keep up the good work.


  3. Par

    No problem :) Thanks!


  4. now in my rss reader)))


  5. cool sitename man)))


  6. cool site, why you dont create an open source flash as3 gfame engine, sure, I will join with your project,
    “asgamer [arcade/multitype] game engine”
    thx a lot…


  7. rob

    You saved me hours of frustrating research. I am very thankfull.
    If you are interested I can give you a link to my finished game.


  8. Hi, very nice post. I have been wonder’n bout this issue,so thanks for posting


  9. Phil

    I’m new to flash and I’m so happy I came across this tutorial first! I was able to complete the game quite easily as everything was explained nicely.

    I have a question though in regards to tweeking the enemy spawn location. How would I go about making the enemies not spawn on top of each other?


  10. Mark Angelor

    This tutorial is so very very helpful.

    Thanks a lot! : )


  11. As I’m going through this tutorial, I’m modifying certain things to fit the vision I have for my game, and I have a question, which is really hard to explain. I have it set up so that two enemy ships spawn at the same time (inside the same “random” function in Engine.as). Basically, they are supposed to both be the same ship, but from two different angles, and flying different directions. Here’s my problem. I want them both to shoot at a random interval, but both shoot at the same random interval. Unlike spawning the ships, this is a problem because one laser links to one enemy class, and the other laser links to the other enemy class. Is there a way to get the random value from the first laser, save it, and apply it to the other laser? Is there maybe a better way to go about this? I also tried to use the code that makes the “good” ship fire at a fixed interval, which I thought might be a workaround for my “bad” ship issue, but only the first enemy ship fired at all.

    So far, so good, though. Love the tutorials!

    Thanks,
    -Erik-


  12. maralbjo

    Fantastic tutorial. I’ll post my game when it is finished.


  13. Anbin

    I have only one word for you man!!! AWESOME!


  14. ath77

    Many thanks for this tutorial, I honestly appreciate the support offered to many of us we are learning how to make games in Flash. Maybe the best beginner’s tutorial I’ve come across until now.


  15. Zavash

    This is the Best Beginner tutorial Ever :) all very well explained.


  16. Romuhica

    Super boulot ! Depuis le temps que je recherchais un super tuto ! Et oui, un ptit french guy pour changer :)

    In only one word : thx ! ^^


  17. Daniel

    need some help with this game i have it going but i need to make it that when the hit counter counts down from 100 and gets to 0 on the ship the game is over how would i do that just need help.


  18. Hi great techniques here!

    This method of removing the enemies seems to have good encapsulation (something I require for a similar spawning of objects in my game). However I can’t use it because I can’t spawn them in the document class like this: stage.addChild(enemy); when I do it passes an error in the document classes frame loop because I am changing the properties of all display objects from that main document class. Basically the game is built differently to the structure above. So, when I put just removeChild(enemy); inside the Enemy class it can’t because it is not a child of the caller. I would like to know if an alternative way exists to remove the enemies in the display list via a method in their own class as it is done here in the Stringer class. Thanks


  19. Actually, may I ask where and if you explain this stageRef property? I would like to try and implement this to my game it seems an amazing way to monitor all kinds of interactions between different classes. Such as, the way you create a variable of the ship and call it target in the Stinger class. I’m not sure why this doesn’t work for me in a hitTestObject listener I am creating; other than not using this stageRef property.


  20. How many years do you get photoshop design ?


  21. Many thanks for this tutorial,
    I can Try this it…


  22. wwooovv this perfect tutorial thank you for much


  23. hi,

    i am back for learning, my enemies refuse to shoot, but i have to go through it first before complaining ;) i am sure, with your wonderfull tutorial, its just a time-problem to find the solution…so thanx a lot, i am at debugging :P
    cya on the next page when finished

    peace
    jesta


  24. ok, found the problem…name the class-path of the movieclip “com.asplayer.basics1.StingerBullet” instead of “com.asgamer.basics1.StingerBullet” … thx to trace i noticed that the code is all performed 100% correctly, so the only problem could be with the graphics… ;) learning by doing…

    peace jesta


  25. Thank you!

    my game is here:
    apps.facebook.com/billijackson/


  26. Marcinkonys

    This is the best tutorial on making games I have found so far. It’s really easy to follow. I guess I’ll print it out and examine all the code later (at school, that is :D )

    Thanks a lot!


  27. futureProgrammer

    I am trying to get the enemies to generate at a stage width of 800. I have used your example, but the mines are generating in the middle of the stage. Otherwise it works just fine. Thanks.


  28. Chadworthy

    I’ve been following you excellent tutorial and I’ve had to resort to copying all of your code to the exact character because of one error that has continued to pop up, although my entire project is exactly the same as yours.

    TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at StingerBullet/loop()

    The problem is in this line:

    private var target:Ship;

    For some reason this isn’t picking up the target!
    I’ve traced the Ship and it is working and I’ve spent three days making sure that every bit of code is correct and just like yours, and for some reason the target just isn’t working.
    If I could get past this it would be wonderful and I would appreciate your help, because I have LOVED your tutorial thus far.


  29. I am trying to get the enemies to generate at a stage width of 800. I have used your example, but the mines are generating in the middle of the stage. Otherwise it works just fine. Thanks.



Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">


Advertisement