Jul 23 2008
Jetman and the Helicopter Game Tutorial - Part 2
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