In the last section, we created a way to detect collisions, created particle explosions, and broke the asteroids up into smaller pieces when hit.
In this section, we’ll keep track of the lives, score, make levels, and create a GUI for starting, restarting, and pausing the game.
Tutorial Demo
Creating the GUI System For the Game
We’re going to setup a basic GUI system that allows us to have as many menu screens, buttons, and other GUI elements that we want, for this project and for future ones.
The first thing we’ll need to do, is create a new folder under ‘src’ called ‘UI’.
Next, create a new class in this folder called ‘UI_element.as’, with a basclass of ‘MovieClip’, and enter in the following code:
UI_element.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | package UI { import flash.display.BitmapData; import flash.display.MovieClip; /** * ... * @author Chris Moeller */ public class UI_element extends MovieClip { public var width1:int;//movieclip has it's own width and height that can't be set public var height1:int; public var background:Boolean = false; public var bg_color:uint; public function UI_element(x:Number=0, y:Number=0, width:int=10, height:int=10, background:Boolean=true, bg_color:uint=0x888888) { this.x = x; this.y = y; this.width1 = width; this.height1 = height; this.background = background; this.bg_color = bg_color; //if there is a background, we'll draw it on this movieclip if (background) { graphics.beginFill(bg_color); graphics.drawRect(0, 0, width1, height1); } super(); } public function Update():void { if (!visible) return; } } } |
Next, we will want to create a new class for creating buttons, also with the base class of UI_element.
TextButton.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | package UI { import flash.display.MovieClip; import flash.text.TextField; import flash.text.TextFormat; /** * ... * @author Chris Moeller */ public class TextButton extends UI_element { public var bg_color2:uint; public var text:TextField; public var hovered:Boolean; public var clicked:Boolean; public var func:Function; public function TextButton(x:Number = 0, y:Number = 0, width:int = 10, height:int = 10, text_str:String="Button", format:TextFormat=null, background:Boolean = true, bg_color:uint = 0x333333, bg_color2:uint=0x666666, func:Function=null) { super(x, y, width, height, background, bg_color); this.bg_color2 = bg_color2; text = new TextField(); if (format == null) { format = new TextFormat("Arial", 20, 0x000000, true); format.align = "center"; } text.width = width; text.height = height; text.defaultTextFormat = format; text.text = text_str; text.selectable = false; addChild(text); hovered = false; clicked = false; this.func = func; //to allow the hand pointer on buttons this.buttonMode = true; this.useHandCursor = true; this.mouseChildren = false; } override public function Update():void { if (!visible) return; if (background) { graphics.clear(); if(!hovered) graphics.beginFill(bg_color); else graphics.beginFill(bg_color2); graphics.drawRect(0, 0, width1, height1); graphics.endFill(); } if (clicked) { trace("button Clicked!"); if (func != null) func(); clicked = false; } hovered = false; } } } |
And Finally We’ll want to create a “Screen” class that will contain each of our screens that we could want for the game, including the menu screen, pause screen overlay, and the high scores screen.
Screen.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | package UI { import flash.text.TextField; import flash.text.TextFormat; /** * ... * @author Chris Moeller */ public class Screen extends UI_element { public var texts:Array; public var buttons:Array; public function Screen(x:Number = 0, y:Number = 0, width:int = 10, height:int = 10, background:Boolean = true, bg_color:uint = 0x888888) { super(x, y, width, height, background, bg_color); texts = new Array(); buttons = new Array(); } public function AddText(x:Number, y:Number, width:int, height:int, text_str:String, format:TextFormat=null, color:uint=0xFFFFFF, font:String="Arial", size:int=20, background:Boolean=false, bg_color:uint=0x888888):void { var text:TextField = new TextField(); if (format == null) format = new TextFormat(font, size, color); text.width = width; text.height = height; text.defaultTextFormat = format; text.x = x; text.y = y; text.text = text_str texts.push(text); addChild(text); } public function AddTextButton(x:Number, y:Number, width:int, height:int, text:String, format:TextFormat = null, color:uint = 0xFFFFFF, font:String = "Arial", size:int = 20, background:Boolean = false, bg_color:uint = 0x888888, bg_color2:uint = 0x333333, func:Function=null):void { buttons.push(new TextButton(x, y, width, height, text, format, background, bg_color, bg_color2, func)); addChild(buttons[buttons.length - 1]); } override public function Update():void { for (var i:int = 0; i < buttons.length; i++) { buttons[i].Update(); if (Game.mouse_pos.x >= x + buttons[i].x && Game.mouse_pos.x <= x + buttons[i].x + buttons[i].width) if (Game.mouse_pos.y >= y + buttons[i].y && Game.mouse_pos.y <= y + buttons[i].y + buttons[i].height) { buttons[i].hovered = true; if (Game.mouse_click) buttons[i].clicked = true; } } } } } |
We’ll now need to update “Game.as” to create our screens, and to update and render them when they are selected.
Game.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 | package { import Entities.Asteroid; import Entities.Bullet; import Entities.Explosion; 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.events.MouseEvent; import flash.geom.Point; import flash.text.TextField; import flash.text.TextFormat; import UI.Screen; 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; //PART 5: NEW VARIABLES private const P:int = 80; private const R:int = 82; private const ESC:int = 27; 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 private var asteroids:Array; private var explosions:Array; //PART 5: NEW VARIABLES private var score_txt:TextField; private var score:int; private var level_txt:TextField; private var level:int; private var game_over_txt:TextField; private var game_over:Boolean; private var lives:int; private var starting_lives:int; private var death_delay:Number; private var lives_display:MovieClip; private var level_change_timer:Number; private var level_change_delay:Number; public var ship_start:Point; public var state:int; public const MAIN_MENU:int = 0; public const VIEW_HIGH_SCORES:int = 1; public const ENTER_HIGH_SCORE:int = 2; public const PLAYING:int = 3; public const PAUSED:int = 4; private var main_menu_screen:Screen; private var view_high_score_screen:Screen; private var enter_high_score_screen:Screen; private var paused_screen:Screen; public static var mouse_down:Boolean; public static var mouse_click:Boolean; public static var mouse_pos:Point; 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; asteroids = new Array(); //setup four first asteroids asteroids.push(new Asteroid(stageWidth / 5, 2 * stageHeight / 3, 0, 0, 0)); asteroids.push(new Asteroid(stageWidth / 5, stageHeight / 4, 0, 1, 0)); asteroids.push(new Asteroid(2*stageWidth / 3, 1 * stageHeight / 3, 0, 2, 0)); asteroids.push(new Asteroid(2*stageWidth / 3, 5 * stageHeight / 5, 0, 3, 0)); for (var i:int = 0; i < asteroids.length; i++ ) stage.addChild(asteroids[i]); explosions = new Array(); //PART 5: NEW initializations ship_start = new Point(stage.stageWidth / 2 - 5, stage.stageHeight / 2 - 10); score_txt = new TextField(); score_txt.width = 700; var format:TextFormat = new TextFormat("Courier", 40, 0xFFFFFF, true); score_txt.x = 20; score_txt.y = 10; score_txt.defaultTextFormat = format; score = 0; score_txt.text = String(score); stage.addChild(score_txt); game_over_txt = new TextField(); game_over_txt.width = stageWidth; var format2:TextFormat = new TextFormat("Courier", 40, 0xFFFFFF, true); format2.align = "center"; game_over_txt.x = 0; game_over_txt.y = stageHeight / 2 - 20; game_over_txt.defaultTextFormat = format2; game_over_txt.text = "Game Over Man!\nPress 'R' to start a new game"; game_over = false; stage.addChild(game_over_txt); starting_lives = 4; lives = starting_lives; lives_display = new MovieClip(); lives_display.x = score_txt.x; lives_display.y = 35; stage.addChild(lives_display); death_delay = 2000; level_change_delay = 500; level_change_timer = 0; level_txt = new TextField(); level_txt.width = 700; level_txt.defaultTextFormat = format; level_txt.x = 20; level_txt.y = 55; level_txt.text = "Lvl " + level; stage.addChild(level_txt); level = 1; main_menu_screen = new Screen(0, 0, stageWidth, stageHeight,true, 0x000000); var format3:TextFormat = new TextFormat("Courier", 20, 0xFFFFFF, true); format3.align = "center"; main_menu_screen.AddText(0, 50, stageWidth, 100, "Welcome to Chrismweb.com Asteroids", format3); main_menu_screen.AddTextButton( stageWidth / 2 - 75, 100, 150, 30, "Start!", null, 0x000000, "Courier", 10, true, 0x666666, 0x999999, StartGame); stage.addChild(main_menu_screen); main_menu_screen.AddTextButton( stageWidth / 2 - 75, 150, 150, 30, "View Scores", null, 0x000000, "Courier", 10, true, 0x666666, 0x999999, ShowScores); paused_screen = new Screen(0, 0, stageWidth, stageHeight, false); stage.addChild(paused_screen); paused_screen.AddText(0, 100, stageWidth, 40, "Paused: Press 'P' To Resume", format3); view_high_score_screen = new Screen(0, 0, stageWidth, stageHeight, true, 0x000000); stage.addChild(view_high_score_screen); view_high_score_screen.AddText(0, 50, stageWidth, 20, "High Scores", format3); var format4:TextFormat = new TextFormat("Courier", 16, 0xFFFFFF); format4.align = "left"; view_high_score_screen.AddText( 200, 100, 200, stageHeight - 100, "1. Chrismweb\n2. Chrismweb\n3. Chrismweb\n4. Chrismweb\n5. Chrismweb\n6. Chrismweb\n7. Chrismweb\n8. Chrismweb\n9. Chrismweb\n10. Chrismweb", format4); view_high_score_screen.AddText( 400, 100, 200, stageHeight - 100, "100\n100\n100\n100\n100\n100\n100\n100\n100\n100 ", format4); view_high_score_screen.AddTextButton( stageWidth / 2 - 150, stageHeight-100, 300, 30, "Return to Main Menu", null, 0x000000, "Courier", 10, true, 0x666666, 0x999999, ShowMainMenu); enter_high_score_screen = new Screen(0, 0, stageWidth, stageHeight, true, 0x000000); stage.addChild(enter_high_score_screen); enter_high_score_screen.AddText(0, 50, stageWidth, 100, "You made the high score list! \nEnter Your name:", format3); //button to submit score enter_high_score_screen.AddTextButton( stageWidth / 2 - 100, stageHeight-300, 200, 30, "Submit Score", null, 0x000000, "Courier", 10, true, 0x666666, 0x999999); //button to return to main menu enter_high_score_screen.AddTextButton( stageWidth / 2 - 150, stageHeight-100, 300, 30, "Return to Main Menu", null, 0x000000, "Courier", 10, true, 0x666666, 0x999999, ShowMainMenu); mouse_down = false; mouse_click = false; mouse_pos = new Point(0, 0); stage.addEventListener(MouseEvent.MOUSE_DOWN, MouseDown); stage.addEventListener(MouseEvent.MOUSE_UP, MouseUp); stage.addEventListener(MouseEvent.MOUSE_MOVE, MoveMouse); ChangeState(MAIN_MENU); } public function Update(e:Event):void { current_time = getTimer(); //show the lives on screen if (state == PLAYING || state == PAUSED) { lives_display.graphics.clear(); for (var p:int = 0; p < lives; p++) { lives_display.graphics.lineStyle(.1, 0xFFFFFF); lives_display.graphics.moveTo((p*12)+0, 20); lives_display.graphics.lineTo((p*12)+10 / 2, 0); lives_display.graphics.lineTo((p*12)+10, 20); //draw the line across lives_display.graphics.moveTo((p*12)+(7*20/8 -20)/(-20/(10/2)), 7*20/8 ); lives_display.graphics.lineTo((p*12)+(7 * 20 / 8 -20) / (20 / (10 / 2)) + 10, 7 * 20 / 8); } } if (state == PLAYING) { 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); } } for (var k:int = asteroids.length-1; k > -1;k-- ) asteroids[k].Update(); //removing explosions var explosions_length:int = explosions.length - 1; for (var m:int = explosions_length; m > -1; m-- ) { explosions[m].Update(); if (current_time-explosions[m].life > explosions[m].max_life) { stage.removeChild(explosions[m]); explosions.splice(m, 1); } } //now we'll check collisions var asteroids_length:int = asteroids.length - 1; for (var k1:int = asteroids_length; k1 > -1;k1-- ) { var asteroid_destroyed:Boolean = false;//created so that if an asteroid is destoryed, it won't try to access it afterwards var bullets_length2:int = bullets.length - 1; for(var k2:int = bullets_length2; k2 > -1;k2-- ) if (MovieClip(asteroids[k1]).hitTestObject(bullets[k2])) { stage.removeChild(bullets[k2]); bullets.splice(k2, 1); DestroyAsteroid(k1); asteroid_destroyed = true; break; } if (asteroid_destroyed) continue; if (MovieClip(asteroids[k1]).hitTestObject(ship)&&ship.visible) { //PART 5: Added for having lives lives--; if (lives > 0) ship.ship_death_time = current_time; else { game_over = true; game_over_txt.visible = true; } space_down = false; //to stop firing upon death ship.visible = false; explosions.push(new Explosion( ship.x + ship.width / 2, ship.y + ship.height / 2, 1000, ship.width / 4)); stage.addChild(explosions[explosions.length - 1]); DestroyAsteroid(k1); } } //PART 5: for having the level change and pausing after ship death if (ship.ship_death_time != 0) if (current_time-ship.ship_death_time > death_delay) { ResetShip(); } if (asteroids.length == 0 && !game_over&&level_change_timer==0) level_change_timer = current_time; if(level_change_timer!=0) if (current_time-level_change_timer > level_change_delay) { level++; LoadLevel(level); level_change_timer = 0; trace("loading level " + level); } } else if (state == MAIN_MENU) main_menu_screen.Update(); else if (state == ENTER_HIGH_SCORE) enter_high_score_screen.Update(); else if (state == VIEW_HIGH_SCORES) view_high_score_screen.Update() mouse_click = false; } //PART 5: new functiona public function ShowMainMenu():void { ChangeState(MAIN_MENU); } public function ShowScores():void { ChangeState(VIEW_HIGH_SCORES); } public function StartGame():void { ChangeState(PLAYING); LoadLevel(1); } public function LoadLevel(num:int):void { if (num == 1) { lives = starting_lives; score = 0; score_txt.text = "0"; } for (var i in asteroids) stage.removeChild(asteroids[i]); for (var i in bullets) stage.removeChild(bullets[i]); for (var i in explosions) stage.removeChild(explosions[i]); ResetShip(); asteroids = new Array(); bullets = new Array(); explosions = new Array(); level = num; level_txt.text = "Lvl " + level; game_over_txt.visible = false; //every level the number of asteroids increase until 12 on screen at once, after that the levels just keep resetting //setup four first asteroids asteroids.push(new Asteroid(stageWidth / 5, 2 * stageHeight / 3, 0, 0, 1)); asteroids.push(new Asteroid(stageWidth / 5, stageHeight / 4, 0, 1, 0)); asteroids.push(new Asteroid(2*stageWidth / 3, 1 * stageHeight / 3, 0, 2, 1)); asteroids.push(new Asteroid(2*stageWidth / 3, stageHeight, 0, 3, 0)); if (num > 1)//5 asteroids.push(new Asteroid(stageWidth / 5, 1 * stageHeight / 3, 0, 0, 1)); if (num > 2)//6 asteroids.push(new Asteroid(stageWidth , stageHeight / 5, 0, 0, 1)); if (num > 3)//7 asteroids.push(new Asteroid(stageWidth / 2, stageHeight, 0, 1, 1)); if (num > 4)//8 asteroids.push(new Asteroid(2*stageWidth / 3, 2 * stageHeight / 3, 0, 1, 1)); if (num > 5)//9 asteroids.push(new Asteroid(stageWidth / 7, 3*stageHeight / 4, 0, 2, 1)); if (num > 6)//10 asteroids.push(new Asteroid(stageWidth / 2, 2 * stageHeight / 8, 0, 2, 1)); if (num > 7)//11 asteroids.push(new Asteroid(4*stageWidth / 5, stageHeight, 0, 3, 1)); if (num > 8)//12 asteroids.push(new Asteroid(2 * stageWidth / 3, 2 * stageHeight / 3, 0, 3, 1)); for (var i in asteroids) stage.addChild(asteroids[i]); } public function ChangeState(new_state:int):void { state = new_state; main_menu_screen.visible = false; enter_high_score_screen.visible = false; view_high_score_screen.visible = false; paused_screen.visible = false; game_over_txt.visible = false; ship.visible = false; score_txt.visible = false; level_txt.visible = false; lives_display.visible = false; for (var i in bullets) bullets[i].visible = false; for (var i in asteroids) asteroids[i].visible = false; for (var i in explosions) explosions[i].visible = false; if (state == PLAYING || state == PAUSED) { score_txt.visible = true; level_txt.visible = true; lives_display.visible = true; for (var i in bullets) bullets[i].visible = true; for (var i in asteroids) asteroids[i].visible = true; for (var i in explosions) explosions[i].visible = true; if (state == PAUSED) paused_screen.visible = true; else ship.visible = true; } if (state == MAIN_MENU) main_menu_screen.visible = true; else if (state == ENTER_HIGH_SCORE) enter_high_score_screen.visible = true; else if (state == VIEW_HIGH_SCORES) view_high_score_screen.visible = true; } public function ResetShip():void { ship.visible = true; ship.rotation = 0; ship.rotation_amount = 0; ship.x = ship_start.x; ship.y = ship_start.y; ship.speed.x = 0; ship.speed.y = 0; ship.thrust = 0; ship.ship_death_time = 0; } public function MoveMouse(e:MouseEvent):void { mouse_pos.x = e.stageX; mouse_pos.y = e.stageY; } public function MouseDown(e:MouseEvent):void { mouse_down = true; } public function MouseUp(e:MouseEvent):void { mouse_down = false; mouse_click = true; } //end of new functions public function DestroyAsteroid(asteroid_hit:int):void { //PART 5: added for score text score += Asteroid.Scores[asteroids[asteroid_hit].size]; score_txt.text = String(score); explosions.push(new Explosion( asteroids[asteroid_hit].x + asteroids[asteroid_hit].width / 2, asteroids[asteroid_hit].y + asteroids[asteroid_hit].height / 2, 1000, asteroids[asteroid_hit].width/4)); stage.addChild(explosions[explosions.length - 1]); //now delete the old asteroid, and add 2 new ones in it's place if there are any more sizes left var old_asteroid:Asteroid = asteroids[asteroid_hit]; //if there are more sizes to chose from if (old_asteroid.size != Asteroid.Sizes.length - 1) { var rand_dir:int = Math.floor(Math.random() * (1 + Asteroid.Directions.length - 1 )); var rand_dir2:int = rand_dir - 2; if (rand_dir - 2 < 0) rand_dir2 = rand_dir + 2; var rand_type:int = Math.floor(Math.random() * (2 )); var rand_type2:int = Math.floor(Math.random() * (2 )); //add 2 new asteroids at half the size asteroids.push(new Asteroid( old_asteroid.x, old_asteroid.y, old_asteroid.size + 1, rand_dir, rand_type)); stage.addChild(asteroids[asteroids.length - 1]); asteroids.push(new Asteroid( old_asteroid.x, old_asteroid.y, old_asteroid.size + 1, rand_dir2, rand_type2)); stage.addChild(asteroids[asteroids.length - 1]); trace("creating two new asteroids with dir = " + rand_dir + ", " + rand_dir2); } stage.removeChild(asteroids[asteroid_hit]); asteroids.splice(asteroid_hit, 1); } public function KeyDown(e:KeyboardEvent):void { if (ship.visible == false) return; 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 (state == PAUSED) { if (e.keyCode==P) ChangeState(PLAYING); } else if (state == PLAYING) { //PART 5: New section if (e.keyCode==ESC) ChangeState(MAIN_MENU); if (e.keyCode==P) ChangeState(PAUSED); if (e.keyCode==R) { LoadLevel(1); } } if (ship.visible == false) return; 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; } } } |
We’ll also need to update “Ship.as” to add a variable to store the death time, and make speed public to have access to it:
Ship.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | package Entities { import flash.display.MovieClip; import flash.geom.Point; public class Ship extends MovieClip { //changed to public for chapter 5 public var speed:Point; //added ship death time for part 5 public var ship_death_time:Number; private var speed_multi:Number = .5; private var friction:Number = .95; public var thrust:int; public var rotation_amount:int; public function Ship(x:int, y:int) { super(); this.x = x; this.y = y; var width:int = 10; var height:int = 20; //Create an internal movieclip to change the rotational point from the top left to the center var internal_mc:MovieClip = new MovieClip(); internal_mc.graphics.lineStyle(.1, 0xFFFFFF); internal_mc.graphics.moveTo(0, height); internal_mc.graphics.lineTo(width / 2, 0); internal_mc.graphics.lineTo(width, height); //draw the line across internal_mc.graphics.moveTo((7*height/8 -height)/(-height/(width/2)), 7*height/8 ); internal_mc.graphics.lineTo((7 * height / 8 -height) / (height / (width / 2)) + width, 7 * height / 8); //offset the internal drawing so the center is around the top left corner internal_mc.x = -width / 2; internal_mc.y = -height / 2; this.addChild(internal_mc); speed = new Point(0, 0); thrust = 0; rotation_amount = 0; //added for chapter 5: ship_death_time = 0; } public function Update():void { //Part 5: to not update the ship when not visible if (!visible) return; 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; } public function Thrust():void { var angle_rad:Number = rotation * Math.PI / 180; if (thrust == 1) { speed.x += Math.cos(angle_rad - Math.PI/2) * speed_multi; speed.y += Math.sin(angle_rad - Math.PI/2) * speed_multi; } else { speed.x += Math.cos(angle_rad - Math.PI/2) * (-speed_multi); speed.y += Math.sin(angle_rad - Math.PI/2) * (-speed_multi); } } } } |
And we also need to change Asteroid.as to add an array of scores for each size of the asteroids:
Asteroid.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | package Entities { import flash.display.MovieClip; import flash.geom.Point; /** * ... * @author Chris Moeller */ public class Asteroid extends MovieClip { public static const Directions:Array = [Math.PI / 4, 3 * Math.PI / 4, 5 * Math.PI / 4, 7 * Math.PI / 4]; public static const Sizes:Array = [40, 20, 10]; public static const Speeds:Array = [1, 3, 5]; //PART 5: added for scores public static const Scores:Array = [20, 50, 100]; public var size:int; public var type:int; private var direction:Number; private var speed:Point; public function Asteroid(x:int, y:int, size:Number, direction:Number, type:int) { this.x = x; this.y = y; this.size = size; this.type = type; this.direction = direction = Directions[direction]; this.speed = new Point( -Speeds[size] * Math.cos(direction), -Speeds[size] * Math.sin(direction)); graphics.lineStyle(1, 0xFFFFFF); this.type = type; var ast_width:Number = Sizes[size]; var ast_height:Number = Sizes[size]; if (type == 0) { //first lets draw the asteroid //they are basically circles with some craggly looking edges graphics.moveTo(0, 1 / 3 * ast_height); graphics.lineTo(1 / 3 * ast_width, 0);//top line graphics.lineTo(2 / 3 * ast_width, 0);//top line graphics.lineTo(ast_width, 1 / 3 * ast_height); //right slanted line graphics.lineTo(ast_width, 2 / 3 * ast_height); //right line graphics.lineTo(2 / 3 * ast_width, ast_height); //bottom right slanted line graphics.lineTo(ast_width / 2, ast_height); //bottom line graphics.lineTo(ast_width / 2, 2 / 3 * ast_height); //vertical bottom line graphics.lineTo(1 / 3 * ast_width, ast_height); //slanted bottom left line graphics.lineTo(0, 2 / 3 * ast_height); //slanted bottom left line 2 graphics.lineTo(1 / 3 * ast_width, ast_height / 2); //slanted center left line 1 graphics.lineTo(0, 1 / 3 * ast_height);//back to start } else { graphics.moveTo(0, 1 / 3 * ast_height); graphics.lineTo(1 / 3 * ast_width, 0);//to top left line graphics.lineTo(ast_width / 2, 1 / 3 * ast_height);//to center top graphics.lineTo(2 / 3 * ast_width, 0);//to top right line graphics.lineTo(ast_width, 1 / 3 * ast_height);//to right top line graphics.lineTo(4 / 5 * ast_width, ast_height / 2);//inward right center line graphics.lineTo( ast_width, 2 / 3 * ast_height);//to right bottom line graphics.lineTo( 2 / 3 * ast_width, ast_height);//to bottom right line graphics.lineTo( 1 / 3 * ast_width, ast_height);//to bottom left line graphics.lineTo( 0, 2 / 3 * ast_height);//to left bottom line graphics.lineTo(0, 1 / 3 * ast_height);//back tot he start } //super(); } 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; } } } |
In the next section we’ll add the Kongregate API to the game, and create a way to submit the high scores to their servers when the game is live.
Download the source Code and please leave me any comments or feedback you have.


Wowsers! As a kid I’ve enjoyed a lot of this game. Although didn’t expected that such a small game, need so much code to be functional.
. Thanks for the great tut!
Will experiment now
Also forgot to ask. Is it possible to implement Spawning time which would activate, after the ship is destroyed? So to prevent Asteroid collision (for 3 seconds, for example).
Thanks!
Hey, thanks for checking out the tutorial!
The variable ‘death_delay’ will set the timer for re-spawning after death– right now I have it set at 2 seconds (2000ms), but you could change it to whatever you like best.
Thanks for the feedback!
question for you chris, is there any benefit to checking if the mouse position is within the area of the button instead of just adding event listeners to the buttons?
I would understand the reason to do this without the display list, but having it here confuses me.
No, you can do it either way, and it might be easier just to have the event listener fire and run the related function. (although I just like being able to control where it finds it)
If nothing else, it probably would have been simpler to use the built-in Movieclip function ‘hitTestPoint’ to check for intersection.
I think I didn’t use it because I wrote the non-display list version of the tutorial first before this one, and converted the code.
I’ll have to probably update that to make it simpler/fit in with the display list method, thanks for seeing that!