Jul 23 2008

Jetman and the Helicopter Game Tutorial - Part 2

Published by Andy G. Cook at 11:56 am under Actionscript 3, Flash, Game Design, Intermediate

This is the continuation and final chapter of the Jetman and the Helicopter Game Tutorial series .

Read part one here.

Wall collision:

Now it is time to make the helicopter actually check for the walls.




The Wall Class:

package
{
	import flash.display.MovieClip;
	import flash.events.Event;
 
	public class Wall extends MovieClip
	{
		public var bPlaying:Boolean = false;
 
		public function Wall ( BStarting:Boolean ) : void
		{
			bPlaying = BStarting;
 
			this.addEventListener ( Event.ENTER_FRAME, onEnterFrameHandler );
		}
 
		private function onEnterFrameHandler ( E:Event ) : void
		{
			if ( bPlaying )
			{
				this.x -= 15;
			}
		}
 
		public function EndGame ( ) : void
		{
			this.removeEventListener ( Event.ENTER_FRAME, onEnterFrameHandler );
		}
	}
}

Lines 25-28: A public function that removes the event listener for the instance of the Wall class.

The Document Class:

package
{
	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.utils.setInterval;
	import flash.utils.clearInterval;
 
	public class Main extends Sprite
	{
		private var TitleScreen:StartScreen;
		private var bGameStarted:Boolean;
		private var mcWallContainer:MovieClip;
		private var uintAddABrick:uint;
		private var PreviousBrickTop:Wall;
		private var PreviousBrickBottom:Wall;
		private var uintLevelUp:uint;
		private var nLevel:Number = 1;
		private var Helicopter:Chopper;
		private var nPower:Number = 2;
		private var nSpeedY:Number = 0;
		private var bMouseDown:Boolean;
		private var bGameOver:Boolean;
 
		public function Main ( ) : void
		{
			mcWallContainer = new MovieClip ();
			this.addChild(mcWallContainer);
 
			addTitleScreen();
 
			stage.addEventListener ( MouseEvent.MOUSE_DOWN, mouseDownHandler );
			stage.addEventListener ( MouseEvent.MOUSE_UP, mouseUpHandler );
		}
 
		private function addTitleScreen ( ) : void
		{
			nLevel = 1;
 
			while (  mcWallContainer.numChildren > 0 )
			{
				mcWallContainer.removeChildAt(mcWallContainer.numChildren-1);
			}
 
			addStartingBricks();
 
			TitleScreen = new StartScreen ();
			TitleScreen.x = stage.stageWidth/2 + 100;
			TitleScreen.y = stage.stageHeight/2;
			this.addChild(TitleScreen);
 
			Helicopter = new Chopper ();
			Helicopter.x = stage.stageWidth/3;
			Helicopter.y = stage.stageHeight/2;
			this.addChild(Helicopter);
			Helicopter.rotation = 5;
 
			bGameStarted = false;
			bGameOver = false;
		}
 
		private function startGame ( ) : void
		{
			stage.addEventListener ( Event.ENTER_FRAME, onEnterFrameHandler );
 
			uintAddABrick = setInterval ( addABrick, 100 );
			uintLevelUp = setInterval ( levelUp, 3000 );
 
			addABrick();
			for ( var i = 0; i < mcWallContainer.numChildren; i++ )
			{
				var mcWall:Object = mcWallContainer.getChildAt(i);
				mcWall.bPlaying = true;
			}
 
		}
 
		private function mouseDownHandler ( ME:MouseEvent ) : void
		{
			if ( bGameStarted == false )
			{
				bGameStarted = true;
				startGame();
 
				bMouseDown = true;
				Helicopter.rotation = -5;
 
				this.removeChild(TitleScreen);
			}
 
			if ( bGameOver == false )
			{
				bMouseDown = true;
				Helicopter.rotation = -5;
			}
			else
			{
				this.removeChild(Helicopter);
				addTitleScreen();
			}
		}
 
		private function mouseUpHandler ( ME:MouseEvent ) : void
		{
			if ( bGameOver == false )
			{
				bMouseDown = false;
				Helicopter.rotation = 5;
			}
		}
		private function addStartingBricks ( ) : void
		{
			for ( var i = 0; i < Math.floor( ( stage.stageWidth * 2  )/ 50); i++ )
			{
				var Brick:Wall = new Wall ( false );
 
				if ( i < Math.floor( ( stage.stageWidth * 2  ) / 50) / 2)
				{
					Brick.y = 0;
					Brick.x = i * 50 + 25;
					PreviousBrickTop = Brick;
				}
				else
				{
					Brick.y = stage.stageHeight;
					Brick.x = i * 50 + 25 - stage.stageWidth;
					PreviousBrickBottom = Brick;
				}
 
				mcWallContainer.addChild(Brick);
			}
		}
 
		private function onEnterFrameHandler ( E:Event ) : void
		{
			if ( bGameOver == false )
			{
				if ( bMouseDown )
				{
					if ( nSpeedY > -8 )
					{
						nSpeedY -= nPower;
					}
				}
				else
				{
					if ( nSpeedY < 12 )
					{
						nSpeedY += 3;
					}
				}
 
				Helicopter.y += nSpeedY;
 
				for ( var i = 0; i < mcWallContainer.numChildren; i++ )
				{
					if ( mcWallContainer.getChildAt(i).x < -20 )
					{
						mcWallContainer.removeChildAt(i);
					}
					else if ( Helicopter.mcBlades.hitTestObject ( mcWallContainer.getChildAt(i) ) || Helicopter.mcBody.hitTestObject ( mcWallContainer.getChildAt(i) ) || Helicopter.mcFeet.hitTestObject ( mcWallContainer.getChildAt(i) ) )
					{
						trace ( "You are dead!" + mcWallContainer.getChildAt(i).name );
						bGameOver = true;
						endGame();
					}
				}
			}
		}
 
		private function addABrick ( ) : void
		{
			var nRandom:Number = Math.random();
 
			var BrickTop:Wall = new Wall( true );
			BrickTop.x = stage.stageWidth + 25;
			BrickTop.scaleY *= nLevel;
 
			var BrickBottom:Wall = new Wall( true );
			BrickBottom.x = stage.stageWidth + 25;
			BrickBottom.scaleY *= nLevel;
 
			if ( nRandom < .5 )
			{
				if ( PreviousBrickTop.y > 3 )
				{
					BrickTop.y = PreviousBrickTop.y - Math.floor ( Math.random() * 3 );
					BrickBottom.y = PreviousBrickBottom.y - Math.floor ( Math.random() * 3 );
				}
				else
				{
					BrickTop.y = PreviousBrickTop.y + Math.floor ( Math.random() * 3 );
					BrickBottom.y = PreviousBrickBottom.y + Math.floor ( Math.random() * 3 );
				}
			}
			else
			{
				if ( PreviousBrickBottom.y < 397 )
				{
					BrickTop.y = PreviousBrickTop.y + Math.floor ( Math.random() * 3 );
					BrickBottom.y = PreviousBrickBottom.y + Math.floor ( Math.random() * 3 );
				}
				else
				{
					BrickTop.y = PreviousBrickTop.y - Math.floor ( Math.random() * 3 );
					BrickBottom.y = PreviousBrickBottom.y - Math.floor ( Math.random() * 3 );
				}
			}
 
			if ( nRandom >= .98 )
			{
				BrickTop.scaleY += 2.5 - nLevel/2;
				BrickBottom.scaleY += 2.5 - nLevel/2 ;
			}
 
			PreviousBrickTop = BrickTop;
			PreviousBrickBottom = BrickBottom;
 
			mcWallContainer.addChild(BrickTop);
			mcWallContainer.addChild(BrickBottom);
		}
 
		private function endGame ( ) : void
		{
			for ( var i = 0; i < mcWallContainer.numChildren; i++ )
			{
				var mcWall:Object = mcWallContainer.getChildAt(i);
				mcWall.EndGame();
			}
 
			clearInterval(uintAddABrick);
			clearInterval(uintLevelUp);
			stage.removeEventListener ( Event.ENTER_FRAME, onEnterFrameHandler );
		}
 
		private function levelUp ( ) : void
		{
			if ( nLevel < 3 )
			{
				nLevel += .2;
			}
		}
	}
}

Lines 24: Another variable

Line 39: Setting nLevel back to 1. Very important otherwise you are going to have your walls be very big after the game is reset.

Lines 41-44: A while loop that removes all the walls when the title screen is added. This effectively resets the game.

Line 60: bGameOver = false. Happens whenever the title screen is added.

Lines 162-168: Runs a collision check against every wall on the helicopter. You could run a collision check on the entire helicopter, but since Flash uses bounding boxes it wouldn’t be that accurate. To combat this problem, I divided the helicopter up into three parts inside the Helicopter movieclip, and run the collision on each part.

Lines 223-234: Runs a for loop on all the bricks that runs the EndGame function inside all the Bricks. The EndGame function inside the Wall class just removes the ENTER_FRAME event listener for every instance of Wall. It stops the walls from moving. This also clears in the intervals for adding a new brick, leving up, and clears the ENTER_FRAME event for the stage, making the helicopter not move.

The Obstacles:

In the real helicopter game, there are random blocks that are in the middle of the stage. I call these obastacles. They are called Obstacle in the library and have linkage of Obstacle.




The Obstacle Class:

package
{
	import flash.display.MovieClip;
	import flash.events.Event;
 
	public class Obstacle extends MovieClip
	{
		public function Obstacle ( ) : void
		{
			this.addEventListener ( Event.ENTER_FRAME, onEnterFrameHandler );
		}
 
		private function onEnterFrameHandler ( E:Event ) : void
		{
			this.x -= 15;
		}
 
		public function EndGame ( ) : void
		{
			this.removeEventListener ( Event.ENTER_FRAME, onEnterFrameHandler );
		}
	}
}

The same thing as the Wall class but just a different name.

The Document Class:

package
{
	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.utils.setInterval;
	import flash.utils.clearInterval;
 
	public class Main extends Sprite
	{
		private var TitleScreen:StartScreen;
		private var bGameStarted:Boolean;
		private var mcWallContainer:MovieClip;
		private var uintAddABrick:uint;
		private var PreviousBrickTop:Wall;
		private var PreviousBrickBottom:Wall;
		private var uintLevelUp:uint;
		private var nLevel:Number = 1;
		private var Helicopter:Chopper;
		private var nPower:Number = 2;
		private var nSpeedY:Number = 0;
		private var bMouseDown:Boolean;
		private var bGameOver:Boolean;
		private var uintAddObstacle:uint;
 
		public function Main ( ) : void
		{
			mcWallContainer = new MovieClip ();
			this.addChild(mcWallContainer);
 
			addTitleScreen();
 
			stage.addEventListener ( MouseEvent.MOUSE_DOWN, mouseDownHandler );
			stage.addEventListener ( MouseEvent.MOUSE_UP, mouseUpHandler );
		}
 
		private function addTitleScreen ( ) : void
		{
			nLevel = 1;
 
			while (  mcWallContainer.numChildren > 0 )
			{
				mcWallContainer.removeChildAt(mcWallContainer.numChildren-1);
			}
 
			addStartingBricks();
 
			TitleScreen = new StartScreen ();
			TitleScreen.x = stage.stageWidth/2 + 100;
			TitleScreen.y = stage.stageHeight/2;
			this.addChild(TitleScreen);
 
			Helicopter = new Chopper ();
			Helicopter.x = stage.stageWidth/3;
			Helicopter.y = stage.stageHeight/2;
			this.addChild(Helicopter);
			Helicopter.rotation = 5;
 
			bGameStarted = false;
			bGameOver = false;
		}
 
		private function startGame ( ) : void
		{
			stage.addEventListener ( Event.ENTER_FRAME, onEnterFrameHandler );
 
			uintAddABrick = setInterval ( addABrick, 100 );
			uintLevelUp = setInterval ( levelUp, 3000 );
			uintAddObstacle = setInterval ( makeObstacle, ( Math.floor ( Math.random() * 3 ) + 3 ) * 1000 );
 
			addABrick();
			for ( var i = 0; i < mcWallContainer.numChildren; i++ )
			{
				var mcWall:Object = mcWallContainer.getChildAt(i);
				mcWall.bPlaying = true;
			}
		}
 
		private function mouseDownHandler ( ME:MouseEvent ) : void
		{
			if ( bGameStarted == false )
			{
				bGameStarted = true;
				startGame();
 
				bMouseDown = true;
				Helicopter.rotation = -5;
 
				this.removeChild(TitleScreen);
			}
 
			if ( bGameOver == false )
			{
				bMouseDown = true;
				Helicopter.rotation = -5;
			}
			else
			{
				this.removeChild(Helicopter);
				addTitleScreen();
			}
		}
 
		private function mouseUpHandler ( ME:MouseEvent ) : void
		{
			if ( bGameOver == false )
			{
				bMouseDown = false;
				Helicopter.rotation = 5;
			}
		}
		private function addStartingBricks ( ) : void
		{
			for ( var i = 0; i < Math.floor( ( stage.stageWidth * 2  )/ 50); i++ )
			{
				var Brick:Wall = new Wall ( false );
 
				if ( i < Math.floor( ( stage.stageWidth * 2  ) / 50) / 2)
				{
					Brick.y = 0;
					Brick.x = i * 50 + 25;
					PreviousBrickTop = Brick;
				}
				else
				{
					Brick.y = stage.stageHeight;
					Brick.x = i * 50 + 25 - stage.stageWidth;
					PreviousBrickBottom = Brick;
				}
 
				mcWallContainer.addChild(Brick);
			}
		}
 
		private function onEnterFrameHandler ( E:Event ) : void
		{
			if ( bGameOver == false )
			{
				if ( bMouseDown )
				{
					if ( nSpeedY > -8 )
 
 
 
 
					{
						nSpeedY -= nPower;
					}
				}
				else
				{
					if ( nSpeedY < 12 )
					{
						nSpeedY += 3;
					}
				}
 
				Helicopter.y += nSpeedY;
 
				for ( var i = 0; i < mcWallContainer.numChildren; i++ )
				{
					if ( mcWallContainer.getChildAt(i).x < -20 )
					{
						mcWallContainer.removeChildAt(i);
					}
					else if ( Helicopter.mcBlades.hitTestObject ( mcWallContainer.getChildAt(i) ) || Helicopter.mcBody.hitTestObject ( mcWallContainer.getChildAt(i) ) || Helicopter.mcFeet.hitTestObject ( mcWallContainer.getChildAt(i) ) )
					{
						bGameOver = true;
						endGame();
					}
				}
			}
		}
 
		private function addABrick ( ) : void
		{
			var nRandom:Number = Math.random();
 
			var BrickTop:Wall = new Wall( true );
			BrickTop.x = stage.stageWidth + 25;
			BrickTop.scaleY *= nLevel;
 
			var BrickBottom:Wall = new Wall( true );
			BrickBottom.x = stage.stageWidth + 25;
			BrickBottom.scaleY *= nLevel;
 
			if ( nRandom < .5 )
			{
				if ( PreviousBrickTop.y > 3 )
				{
					BrickTop.y = PreviousBrickTop.y - Math.floor ( Math.random() * 3 );
					BrickBottom.y = PreviousBrickBottom.y - Math.floor ( Math.random() * 3 );
				}
				else
				{
					BrickTop.y = PreviousBrickTop.y + Math.floor ( Math.random() * 3 );
					BrickBottom.y = PreviousBrickBottom.y + Math.floor ( Math.random() * 3 );
				}
			}
			else
			{
				if ( PreviousBrickBottom.y < 397 )
				{
					BrickTop.y = PreviousBrickTop.y + Math.floor ( Math.random() * 3 );
					BrickBottom.y = PreviousBrickBottom.y + Math.floor ( Math.random() * 3 );
				}
				else
				{
					BrickTop.y = PreviousBrickTop.y - Math.floor ( Math.random() * 3 );
					BrickBottom.y = PreviousBrickBottom.y - Math.floor ( Math.random() * 3 );
				}
			}
 
			if ( nRandom >= .98 )
			{
				BrickTop.scaleY += 2.5 - nLevel/2;
				BrickBottom.scaleY += 2.5 - nLevel/2 ;
			}
 
			PreviousBrickTop = BrickTop;
			PreviousBrickBottom = BrickBottom;
 
			mcWallContainer.addChild(BrickTop);
			mcWallContainer.addChild(BrickBottom);
		}
 
		private function endGame ( ) : void
		{
			for ( var i = 0; i < mcWallContainer.numChildren; i++ )
			{
				var mcWall:Object = mcWallContainer.getChildAt(i);
				mcWall.EndGame();
			}
 
			clearInterval(uintAddABrick);
			clearInterval(uintAddObstacle);
			clearInterval(uintLevelUp);
			stage.removeEventListener ( Event.ENTER_FRAME, onEnterFrameHandler );
		}
 
		private function levelUp ( ) : void
		{
			if ( nLevel < 3 )
			{
				nLevel += .2;
			}
		}
 
		private function makeObstacle ( ) : void
		{
			clearInterval ( uintAddObstacle );
			var WallObstacle:Obstacle = new Obstacle ();
			WallObstacle.x = 550;
			WallObstacle.y = ( PreviousBrickTop.y + PreviousBrickTop.height ) + Math.floor ( Math.random() * ( stage.stageHeight - ( PreviousBrickTop.height + PreviousBrickBottom.height) ));
			mcWallContainer.addChild(WallObstacle);
 
			uintAddObstacle = setInterval ( makeObstacle, ( Math.floor ( Math.random() * 4 ) + 2 ) * 1000 );
		}
	}
}

Line 25: Variable for the interval to add an obstacle.

Line 70: Setting the variable to add an obstacle.

Line 233: Don’t forget to clear the interval when the game ends!

Lines 246-255: Adding the obstacle. Clears in the interval and resets it at the end with a random time value between 2-6 seconds. The obstacles Y position is determined by the space between the top and bottom walls.

The Score:

What good is a game without a score? This one is easy. There is a movieclip called Scoreboard in the library. It has two dynamic text boxes in it. One of the current score, the other for the best score.




The Document Class:

package
{
	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.utils.setInterval;
	import flash.utils.clearInterval;
 
	public class Main extends Sprite
	{
		private var TitleScreen:StartScreen;
		private var bGameStarted:Boolean;
		private var mcWallContainer:MovieClip;
		private var uintAddABrick:uint;
		private var PreviousBrickTop:Wall;
		private var PreviousBrickBottom:Wall;
		private var uintLevelUp:uint;
		private var nLevel:Number = 1;
		private var Helicopter:Chopper;
		private var nPower:Number = 2;
		private var nSpeedY:Number = 0;
		private var bMouseDown:Boolean;
		private var bGameOver:Boolean;
		private var uintAddObstacle:uint;
		private var Scoreboard:ScoreboardKeeper;
		private var nCurrentScore:Number = 0;
		private var nBestScore:Number = 0;
 
		public function Main ( ) : void
		{
			mcWallContainer = new MovieClip ();
			this.addChild(mcWallContainer);
 
			Scoreboard = new ScoreboardKeeper;
			Scoreboard.x = 0;
			Scoreboard.y = stage.stageHeight - 15;
			this.addChild(Scoreboard);
 
			Scoreboard.tdBestScore.text = "Best: " +  String ( 0 );
 
			addTitleScreen();
 
			stage.addEventListener ( MouseEvent.MOUSE_DOWN, mouseDownHandler );
			stage.addEventListener ( MouseEvent.MOUSE_UP, mouseUpHandler );
		}
 
		private function addTitleScreen ( ) : void
		{
			nLevel = 1;
			nCurrentScore = 0;
			Scoreboard.tdCurrentScore.text = "Distance: " +  String ( 0 );
 
			while (  mcWallContainer.numChildren > 0 )
			{
				mcWallContainer.removeChildAt(mcWallContainer.numChildren-1);
			}
 
			addStartingBricks();
 
			TitleScreen = new StartScreen ();
			TitleScreen.x = stage.stageWidth/2 + 100;
			TitleScreen.y = stage.stageHeight/2;
			this.addChild(TitleScreen);
 
			Helicopter = new Chopper ();
			Helicopter.x = stage.stageWidth/3;
			Helicopter.y = stage.stageHeight/2;
			this.addChild(Helicopter);
			Helicopter.rotation = 5;
 
			bGameStarted = false;
			bGameOver = false;
		}
 
		private function startGame ( ) : void
		{
			stage.addEventListener ( Event.ENTER_FRAME, onEnterFrameHandler );
 
			uintAddABrick = setInterval ( addABrick, 100 );
			uintLevelUp = setInterval ( levelUp, 3000 );
			uintAddObstacle = setInterval ( makeObstacle, ( Math.floor ( Math.random() * 3 ) + 3 ) * 1000 );
 
			addABrick();
			for ( var i = 0; i < mcWallContainer.numChildren; i++ )
			{
				var mcWall:Object = mcWallContainer.getChildAt(i);
				mcWall.bPlaying = true;
			}
		}
 
		private function mouseDownHandler ( ME:MouseEvent ) : void
		{
			if ( bGameStarted == false )
			{
				bGameStarted = true;
				startGame();
 
				bMouseDown = true;
				Helicopter.rotation = -5;
 
				this.removeChild(TitleScreen);
			}
 
			if ( bGameOver == false )
			{
				bMouseDown = true;
				Helicopter.rotation = -5;
			}
			else
			{
				this.removeChild(Helicopter);
				addTitleScreen();
			}
		}
 
		private function mouseUpHandler ( ME:MouseEvent ) : void
		{
			if ( bGameOver == false )
			{
				bMouseDown = false;
				Helicopter.rotation = 5;
			}
		}
		private function addStartingBricks ( ) : void
		{
			for ( var i = 0; i < Math.floor( ( stage.stageWidth * 2  )/ 50); i++ )
			{
				var Brick:Wall = new Wall ( false );
 
				if ( i < Math.floor( ( stage.stageWidth * 2  ) / 50) / 2)
				{
					Brick.y = 0;
					Brick.x = i * 50 + 25;
					PreviousBrickTop = Brick;
				}
				else
				{
					Brick.y = stage.stageHeight;
					Brick.x = i * 50 + 25 - stage.stageWidth;
					PreviousBrickBottom = Brick;
				}
 
				mcWallContainer.addChild(Brick);
			}
		}
 
		private function onEnterFrameHandler ( E:Event ) : void
		{
			if ( bGameOver == false )
			{
				if ( bMouseDown )
				{
					if ( nSpeedY > -8 )
					{
						nSpeedY -= nPower;
					}
				}
				else
				{
					if ( nSpeedY < 12 )
					{
						nSpeedY += 3;
					}
				}
 
				Helicopter.y += nSpeedY;
				nCurrentScore += 1;
				Scoreboard.tdCurrentScore.text = "Distance: " + nCurrentScore.toString();
 
				for ( var i = 0; i < mcWallContainer.numChildren; i++ )
				{
					if ( mcWallContainer.getChildAt(i).x < -20 )
					{
						mcWallContainer.removeChildAt(i);
					}
					else if ( Helicopter.mcBlades.hitTestObject ( mcWallContainer.getChildAt(i) ) || Helicopter.mcBody.hitTestObject ( mcWallContainer.getChildAt(i) ) || Helicopter.mcFeet.hitTestObject ( mcWallContainer.getChildAt(i) ) )
					{
						bGameOver = true;
						endGame();
					}
				}
			}
		}
 
		private function addABrick ( ) : void
		{
			var nRandom:Number = Math.random();
 
			var BrickTop:Wall = new Wall( true );
			BrickTop.x = stage.stageWidth + 25;
			BrickTop.scaleY *= nLevel;
 
			var BrickBottom:Wall = new Wall( true );
			BrickBottom.x = stage.stageWidth + 25;
			BrickBottom.scaleY *= nLevel;
 
			if ( nRandom < .5 )
			{
				if ( PreviousBrickTop.y > 3 )
				{
					BrickTop.y = PreviousBrickTop.y - Math.floor ( Math.random() * 3 );
					BrickBottom.y = PreviousBrickBottom.y - Math.floor ( Math.random() * 3 );
				}
				else
				{
					BrickTop.y = PreviousBrickTop.y