Creating an Asteroids Flash Game Using the Display List Part 2: Creating and Firing bullets, and making objects “wrap around” the screen

Chris Moeller Asteroids Flash Game Part 5- bullets and wrapping

I’m going to finish up the tutorial section on creating an asteroids game in FlashDevelop using the display list. This series will run parallel to the tutorial set for creating an asteroids game using blitting.

In the last section, we added keyboard input to the ship, so that we could rotate in any direction, and move forward.

In this section, we’ll create bullets that are fired from the front of the ship at a limited rate, and make both the ship and the bullets wrap around when they reach the edge of the screen.

Making the ship wrap to the other side of the screen

To have the ship wrap around to the other side of the ship, we first need to be able to have it find out how wide and tall the screen is, to tell when it is no longer on it.

So we need to modify ‘Game.as’ and create both as static variables than any object will be able to access.

Excerpt from Game.as

package
{
	import Entities.Ship;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.MovieClip;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.KeyboardEvent;

	public class Game
	{
		public var stage:Stage;
		public var ship:Ship;

		private const LEFT:int = 37;
		private const UP:int = 38;
		private const RIGHT:int = 39;
		private const SPACE:int = 32;
		private const DOWN:int = 40;

		public static var stageWidth:int; //new line
		public static var stageHeight:int; //new line

		public function Game(stage:Stage)
		{
			this.stage = stage;
			stageHeight = stage.stageHeight; //new line
			stageWidth = stage.stageWidth; //new line
			trace("Game created");

			//create the ship
			ship = new Ship(stage.stageWidth / 2 - 5, stage.stageHeight / 2 - 10);
			stage.addChild(ship);

			//now for input
			stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDown);
			stage.addEventListener(KeyboardEvent.KEY_UP, KeyUp);
		}

The only thing we need to do here is create static variables for both the stage width and height.

Static variables are good when you want to access the variable outside of the object by referencing the class. So whenever we want to find the stagewidth, we’ll just call the class name. variable, IE Game.stageWidth.

Line 22-23: Static variables for the stage/screen width and height.
Line 28-29: Assigning the stageWidth and stagHeight from the stage object to our static variables.

Now we need to setup the ship to wrap around the screen when it reaches the edge. To do this, we’ll need to modify “Ship.as“.

Excerpt from Ship.as

		public function Update():void
		{
			x += speed.x;
			y += speed.y;
			speed.x *= friction;
			speed.y *= friction;

			if (thrust != 0)
				Thrust();

			if (rotation_amount != 0)
				rotation += rotation_amount;

			if (x + width <= 0) 				x = Game.stageWidth - width; 			else if(x >= Game.stageWidth)
				x = 0;

			if (y + height <= 0) 				y = Game.stageHeight - height; 			else if(y >= Game.stageHeight)
				y = 0;
		}

We only updated the Update.as function here.

line 55: Check to see if the right side of the ship is off of the left side of the screen, and place it on the right side(so it looks like it goes through the right side and comes through the left side)

line 60: We do the same thing with the height- check if the top or bottom of the ship is off screen, then move it to the other side of the screen.

Creating Bullets and firing

The next thing we need to do is create a “bullet” class, and have the ship fire them from the front/center of the ship, in the same angle it is facing.

So again, right click on the ‘Entities’ folder, add->new class, name it “Bullet’, browse for the base class (MovieClip) and click the checkbox for “Generate constructor matching base class”.

So we create Bullet.as:

package Entities
{
	import flash.display.MovieClip;
	import flash.geom.Point;

	/**
	 * ...
	 * @author Chris Moeller
	 */
	public class Bullet extends MovieClip
	{
		private var speed:Point;
		private var max_speed:Number;
		public var life:Number;
		public var angle:Number;

		public function Bullet(x:int, y:int, life:Number, angle:Number = 0)
		{
			super();
			this.x = x;
			this.y = y;
			this.life = life;
			this.angle = angle;

			max_speed = 10;
			this.speed = new Point(max_speed * Math.sin(angle), -max_speed * Math.cos(angle));

			graphics.beginFill(0xFFFFFF);
			graphics.drawRect(0, 0, 2, 2);
			graphics.endFill();
			this.width = this.height = 2;
		}
		public function Update():void
		{

			x += speed.x;
			y += speed.y;	

			if (x + width <= 0) 				x = Game.stageWidth - width; 			else if(x >= Game.stageWidth)
				x = 0;

			if (y + height <= 0) 				y = Game.stageHeight - height; 			else if(y >= Game.stageHeight)
				y = 0;
		}
	}
}

The only really new things we’re doing here is passing in the “life” variable. This is will just be the “current_time” variable from the Game class, and will let the bullet know when it was created.

Using this, we can see how long the bullet has been “alive”, and remove it when it has been around for too long.

The final step is updating Game.as to create the bullets when the spacebar has been pressed:

Excerpt from Game.as

package
{
	import Entities.Bullet;
	import Entities.Ship;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.MovieClip;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.KeyboardEvent;

	import flash.utils.getTimer;

	public class Game
	{
		public var stage:Stage;
		public var ship:Ship;

		private const LEFT:int = 37;
		private const UP:int = 38;
		private const RIGHT:int = 39;
		private const SPACE:int = 32;
		private const DOWN:int = 40;

		public static var stageWidth:int;
		public static var stageHeight:int;

		public static var current_time:Number; //used to track the time of the current frame
		private var bullets:Array; //used to hold all the bullets
		private var firing_delay:Number; //the delay before creating a new bullet when the space bar is held down
		private var last_fired:Number; //the time the last bullet was created
		private var bullets_max_life:Number; //the max life of the bullets
		private var space_down:Boolean; //Either set to true if the spacebar is being held down, or false if released

		public function Game(stage:Stage)
		{
			this.stage = stage;
			stageHeight = stage.stageHeight;
			stageWidth = stage.stageWidth;
			trace("Game created");

			//create the ship
			ship = new Ship(stage.stageWidth / 2 - 5, stage.stageHeight / 2 - 10);
			stage.addChild(ship);

			//now for input
			stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDown);
			stage.addEventListener(KeyboardEvent.KEY_UP, KeyUp);	

			bullets = new Array();
			firing_delay = 200;
			last_fired = 0;
			bullets_max_life = 1000;
			space_down = false;
		}
		public function Update(e:Event):void
		{
			current_time = getTimer();

			ship.Update();

			if (space_down && current_time-last_fired > firing_delay)
			{
				var angle_rad:Number = ship.rotation * Math.PI / 180;
				bullets.push(new Bullet(ship.x, ship.y, current_time, angle_rad));
				last_fired = current_time;
				stage.addChild(bullets[bullets.length - 1]);
			}

			var bullets_length:int = bullets.length - 1;
			for (var i:int = bullets_length; i > -1;i-- )
			{
				bullets[i].Update();
				if (current_time-bullets[i].life > bullets_max_life)
				{
					stage.removeChild(bullets[i]);
					bullets.splice(i, 1);
				}
			}
		}
		public function KeyDown(e:KeyboardEvent):void
		{
			if(e.keyCode==RIGHT)
				ship.rotation_amount = 15;
			if(e.keyCode==LEFT)
				ship.rotation_amount = -15;
			if(e.keyCode==UP)
				ship.thrust = 1;
			if(e.keyCode==DOWN)
				ship.thrust = -1;
			if(e.keyCode==SPACE)
				space_down = true;
		}
		public function KeyUp(e:KeyboardEvent):void
		{
			if(e.keyCode==RIGHT||e.keyCode==LEFT)
				ship.rotation_amount = 0;				

			if(e.keyCode==UP||e.keyCode==DOWN)
				ship.thrust = 0;
			if(e.keyCode==SPACE)
				space_down = false;
		}
	}
}

There’s several new things here.

Line 28-33: Creating the variables used to create and update the bullets

Line 50-54: Initializing the variables for firing. We’ll have the time in between bullets set to 200ms, or 5 bullets a second. We’ll also have the bullets max life be 1 second.

Line 58: Setting the current time in milliseconds using the flash built in function ‘GetTimer’ (which was imported at the top of the file).

Line 62: Used to check if the spacebar is held down, and enough time has passed since the last bullet was fired

Line 64: Getting the ship rotation in radians to pass to the bullet class to know where to spawn the bullets, and which direction to move.

Line 67: Adding the last bullet created to the display list so that it is visible.

Line 70: Finding the bullets length before the for loop.
This allows us to only have to find the length once. Afterwards we will go through the loop backwards, so that when we need to ‘splice’/remove an object from the array, it won’t affect any of the next elements when the array shifts.
I learned this technique from the book written by the creators of ‘8bitrocket’, ‘The Essential Guide to Flash Games: Building interactive Entertainment with Actionscript”.

Line 74: Check if the difference between the current game time, and the time when the bullet was created (so the time the bullet has been around), and see if it is greater than the bullet max life.
If so, remove it from the stage, and splice/ remove it from our array of bullets.

Line 91: Check if the key pressed was the spacebar, and set our boolean ‘space_down’ value to true, so that the update loop will be able to fire/ create bullets.

Line 101: Check if the spacebar was released. If so, change our ‘space_down’ boolean to false, so that we can’t create bullets from our Games Update function.

Resouce for rotation around a point:
http://en.wikipedia.org/wiki/Rotation_(mathematics)

Tutorial Demo

In the next section we’ll make the asteroids, so that we have some “opponents!”.

Download the source Code and please leave me any comments or feedback you have.

Other Articles in this Series

Bookmark the permalink.

Leave a Reply

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