// 2012-12-17 16:40:53 // Start to implement the Asteroids flash edition // just translate the python code into AS3.0, I // will optimize it later // First I am going to learn how to load image and // sound into this mini-project. // 2012-12-19 20:49:02 Using global variables is not a good idea. // 2012-12-20 15:38:26 I can't stop thinking about implementing this little game. // What about my CET6!!?? It's really the end of the world!!! // Once I started, I just can't stop doing this. // 2012-12-25 12:54:25 finally I think that I have figured out how to deal with the keyboard event. // using an array which stores all the keyboard event. package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.display.LoaderInfo; import flash.display.Sprite; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.events.TimerEvent; import flash.geom.Matrix; import flash.geom.Rectangle; import flash.net.URLRequest; import flash.ui.Keyboard; import flash.utils.Timer; // import flash.media.Sound; /** * * @author Sean */ [SWF(width="800", height="600", frameRate="60")] public class Asteroids extends Sprite { public static var image_assets:Array; protected var score:int = 0; protected var lives:int = 3; protected var time:int = 0; public static const WIDTH:int = 800; public static const HEIGHT:int = 600; protected var my_ship:Ship; protected var rock_group:Array; protected var missile_group:Array; protected var explosion_group:Array; protected var loader:Loader; protected var canvas:BitmapData; protected var image_url:Array; protected var counter:int = 0; protected var frame:int = 0; protected const SIZE:Rectangle = new Rectangle(0, 0, 128, 128); protected var keydown:Object; protected var timer:Timer; protected static const TIMER_DELAY:Number = 1 * 1000; public function Asteroids() { stage.scaleMode = StageScaleMode.NO_SCALE; loadingGame(); } protected function loadingGame():void { canvas = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0xFF000000); image_assets = new Array(); loader = new Loader(); image_url = new Array("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/nebula_brown.png", "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_blue.png", "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/splash.png", "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/double_ship.png", "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/shot2.png", "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blend.png", "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/explosion_orange.png"); loadImage(); } protected function loadImage():void { loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded); loader.load(new URLRequest(image_url[counter++])); } // protected function loadSound():void { // // } protected function loaded(event:Event):void { image_assets.push(Bitmap(LoaderInfo(event.target).content).bitmapData); trace(image_assets.length); if (counter == image_url.length) { trace("successfully loaded all images!"); initGame(); } else loadImage(); } public function initGame():void { keydown = new Array(); var bmp:Bitmap = new Bitmap(canvas); addChild(bmp); my_ship = new Ship(Vector.<Number>([WIDTH / 2, HEIGHT / 2]), Vector.<Number>([0, 0]), 0, 0, image_assets[3]); addChild(my_ship); // my_ship.focusRect = false; stage.focus = stage; rock_group = new Array(); missile_group = new Array(); explosion_group = new Array(); stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeydown, false); stage.addEventListener(KeyboardEvent.KEY_UP, onKeyup, false); timer = new Timer(TIMER_DELAY); timer.addEventListener(TimerEvent.TIMER, rock_spawner); timer.start(); this.addEventListener(MouseEvent.CLICK, onClick); this.addEventListener(Event.ENTER_FRAME, draw); } protected function onClick(evt:MouseEvent):void { stage.focus = my_ship; } protected function draw(evt:Event = null):void { // animated background, need to be implemented... canvas.draw(image_assets[0]); var transform:Matrix = new Matrix(); transform.translate(10, 0); transform.scale(WIDTH / image_assets[1].width, HEIGHT / image_assets[1].height); canvas.draw(image_assets[1], transform, null, null, null, true); // draw ship and sprites my_ship.draw(); // process group of rocks and missiles process_sprite_group(rock_group); process_sprite_group(missile_group); process_sprite_group(explosion_group); // update ship and sprites my_ship.update(); // detect collisions if (group_collide(rock_group, my_ship)) lives -= 1; // trace(lives); score += (group_group_collide(missile_group, rock_group)) * 10; } protected function process_sprite_group(sprite_group:Array):void { // this function is going to draw each of the sprite on the canvas var alive:Boolean = true; if (sprite_group.length) { for each (var sprite:my_sprite in Vector.<my_sprite>(sprite_group)) { stage.addChild(sprite); sprite.draw(); alive = sprite.update(); if (!alive) { stage.removeChild(sprite); sprite_group.shift(); } } } } protected function group_collide(sprite_group:Array, other_object:Sprite):int { var counter:int = 0; for each (var sprite:my_sprite in Vector.<my_sprite>(sprite_group)) { if (sprite.collide(other_object)) { explosion_group.push(new my_sprite(sprite.get_position(), Vector.<Number>([0, 0]), 0, 0, image_assets[6], 24, 0, true)); stage.removeChild(sprite); sprite_group.splice(sprite_group.indexOf(sprite), 1); counter += 1 } } return counter; } protected function group_group_collide(sprite_group1:Array, sprite_group2:Array):int { var counter:int = 0; for each (var sprite:my_sprite in Vector.<my_sprite>(sprite_group1)) { // check if the missile collides with the any of the rocks if (group_collide(sprite_group2, sprite)) { stage.removeChild(sprite); sprite_group1.splice(sprite_group1.indexOf(sprite), 1); counter += 1; } } return counter; } protected function rock_spawner(evt:TimerEvent = null):void { if (rock_group.length < 12) { // I still need to make sure that rock won't spawn in the place where the ship are var rock_pos:Vector.<Number> = new Vector.<Number>(); rock_pos = Vector.<Number>([Math.random() * WIDTH, Math.random() * HEIGHT]); var rock_vel:Vector.<Number> = new Vector.<Number>(); rock_vel = Vector.<Number>([Math.random() * 1.6 - .8, Math.random() * 1.6 - .8]); var rock_avel:Number = Math.random() * 4 - 2; var a_rock:my_sprite = new my_sprite(rock_pos, rock_vel, 0, rock_avel, image_assets[5]); rock_group.push(a_rock); } } // help function protected function is_in_keydown(key:uint):Boolean { for (var key_code in keydown) { if (key == key_code) return true; } return false; } protected function onKeydown(evt:KeyboardEvent):void { if (evt.keyCode != Keyboard.LEFT && evt.keyCode != Keyboard.RIGHT && evt.keyCode != Keyboard.UP && evt.keyCode != Keyboard.SPACE) return; if (!is_in_keydown(evt.keyCode)) keydown[evt.keyCode] = true; if (keydown[evt.keyCode]) { switch (evt.keyCode) { case Keyboard.LEFT: my_ship.decrement_angle_vel(); break; case Keyboard.RIGHT: my_ship.increment_angle_vel(); break; case Keyboard.UP: my_ship.set_thrust(true); break; case Keyboard.SPACE: my_ship.shoot(missile_group); } keydown[evt.keyCode] = false; } } protected function onKeyup(evt:KeyboardEvent):void { if (is_in_keydown(evt.keyCode)) delete keydown[evt.keyCode]; switch (evt.keyCode) { case Keyboard.LEFT: my_ship.increment_angle_vel(); break; case Keyboard.RIGHT: my_ship.decrement_angle_vel(); break; case Keyboard.UP: my_ship.set_thrust(false); break; } } } } import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.display.Loader; import flash.display.Sprite; import flash.geom.Matrix; import flash.geom.Rectangle; import flash.media.SoundChannel; import flash.net.URLRequest; class Ship extends Sprite { protected static const COF:Number = 0.03; protected static const ACCELERATION:Number = .6; protected var scale:int = 1; protected var vel:Vector.<Number>; protected var thrust:Boolean; protected var angle_vel:Number; protected var image:BitmapData; protected var image_data:BitmapData; protected var SIZE:Rectangle; protected var my_ship_image:Bitmap; protected var sound_channel:SoundChannel; public function Ship(pos:Vector.<Number>, vel:Vector.<Number>, angle:Number, angle_vel:Number, image:BitmapData) { SIZE = new Rectangle(0, 0, image.height, image.height); this.x = pos[0]; this.y = pos[1]; this.vel = new Vector.<Number>(); this.vel = vel; this.rotation = angle; this.angle_vel = angle_vel; this.image_data = image; this.image = new BitmapData(SIZE.width, SIZE.height); my_ship_image = new Bitmap(this.image); my_ship_image.x = -SIZE.width / 2; my_ship_image.y = -SIZE.height / 2; addChild(my_ship_image); } public function increment_angle_vel():void { this.angle_vel += 3; } public function decrement_angle_vel():void { this.angle_vel -= 3; } public function shoot(missile_group:Array):void { var forward:Vector.<Number> = Vector.<Number>(angle_to_vector(this.rotation * Math.PI / 180)); var vel:Number = 5; var missile_pos:Vector.<Number> = new Vector.<Number>(); missile_pos = Vector.<Number>([this.x + (SIZE.width / 2) * forward[0], this.y + (SIZE.width / 2) * forward[1]]); var missile_vel:Vector.<Number> = new Vector.<Number>(); missile_vel = Vector.<Number>([this.vel[0] + vel * forward[0], this.vel[1] + vel * forward[1]]); var a_missile:my_sprite = new my_sprite(missile_pos, missile_vel, 0, 0, Asteroids.image_assets[4], 50); missile_group.push(a_missile); } public function set_thrust(on:Boolean):void { this.thrust = on; if (on) { sound_channel = sound.ship_thrust_sound.play(0); } else sound_channel.stop(); } public function get_position():Vector.<Number> { return Vector.<Number>([this.x, this.y]); } public function draw():void { image.fillRect(SIZE, 0); var transform:Matrix = new Matrix(); transform.createBox(scale, scale, 0, -int(this.thrust) * SIZE.width, 0); //use SIZE to clip the shifted graphic to the correct position and size image.draw(image_data, transform, null, null, SIZE); } public function update():void { // position update this.x = this.x < 0 ? Asteroids.WIDTH : this.x; this.y = this.y < 0 ? Asteroids.HEIGHT : this.y; this.x = (this.x + this.vel[0]) % Asteroids.WIDTH; this.y = (this.y + this.vel[1]) % Asteroids.HEIGHT; // friction update this.vel[0] *= (1 - COF); this.vel[1] *= (1 - COF); // accelerate if (this.thrust) { var forward:Vector.<Number> = Vector.<Number>(angle_to_vector(this.rotation * Math.PI / 180)); this.vel[0] += forward[0] * ACCELERATION; this.vel[1] += forward[1] * ACCELERATION; } // angle update this.rotation += this.angle_vel; } protected function angle_to_vector(ang:Number):Vector.<Number> { return Vector.<Number>([Math.cos(ang), Math.sin(ang)]); } } class my_sprite extends Sprite { protected var vel:Vector.<Number>; protected var angle_vel:Number; protected var image:BitmapData; protected var image_data:BitmapData; protected var my_sprite_image:Bitmap; protected var SIZE:Rectangle; protected var animated:Boolean; protected var radius:int; protected var lifespan:Number; protected var age:int; public function my_sprite(pos:Vector.<Number>, vel:Vector.<Number>, angle:Number, angle_vel:Number, image:BitmapData, lifespan:Number = Number.MAX_VALUE, age:int = 0, animated:Boolean = false) { SIZE = new Rectangle(0, 0, image.height, image.height); this.x = pos[0]; this.y = pos[1]; this.vel = new Vector.<Number>(); this.vel.push(vel[0]); this.vel.push(vel[1]); this.rotation = angle; this.angle_vel = angle_vel; this.image_data = image; this.image = new BitmapData(image.height, image.height); this.my_sprite_image = new Bitmap(this.image); my_sprite_image.x = -SIZE.width / 2; my_sprite_image.y = -SIZE.height / 2; this.lifespan = lifespan; this.animated = animated; addChild(my_sprite_image); } public function get_position():Vector.<Number> { return Vector.<Number>([this.x, this.y]); } public function collide(other_object:DisplayObject):Boolean { return this.hitTestObject(other_object); } public function draw():void { if (!this.animated) { image.fillRect(image.rect, 0); image.draw(image_data); } else { image.fillRect(SIZE, 0); var transform:Matrix = new Matrix(); transform.createBox(1, 1, 0, -age * SIZE.width, 0); //use SIZE to clip the shifted graphic to the correct position and size image.draw(image_data, transform, null, null, SIZE); } } public function update():Boolean { // position update this.x = this.x < 0 ? Asteroids.WIDTH : this.x; this.y = this.y < 0 ? Asteroids.HEIGHT : this.y; this.x = (this.x + this.vel[0]) % Asteroids.WIDTH; this.y = (this.y + this.vel[1]) % Asteroids.HEIGHT; // angle update this.rotation += this.angle_vel; // aging this.age += 1 // if true, he/she is still alive, or otherwise if (this.age <= this.lifespan) return true; else return false; } } import flash.media.Sound; class sound { public static var soundtrack:Sound = new Sound(new URLRequest("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/soundtrack.mp3")); public static var missile_sound:Sound = new Sound(new URLRequest("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/missile.mp3")); public static var ship_thrust_sound:Sound = new Sound(new URLRequest("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/thrust.mp3")); public static var explosion_sound:Sound = new Sound(new URLRequest("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/explosion.mp3")); } // //import flash.events.Event; //import flash.display.Loader; //import flash.display.LoaderInfo; //class image { // public static var debris_image:BitmapData; // public static var nebula_image:BitmapData; // public static var splash_image:BitmapData; // public static var ship_image:BitmapData; // public static var missile_image:BitmapData; // public static var asteroid_image:BitmapData; // public static var explosion_image:BitmapData; // // protected var counter:int = 0; // protected var image_loader:Loader; // protected var image_url:Array; // // public function image() { // image_loader = new Loader(); // image_url = new Array("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/nebula_brown.png", // "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_blue.png", // "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/splash.png", // "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/double_ship.png", // "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/shot2.png", // "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blend.png", // "http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/explosion_alpha.png"); // image_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded); // loadImage(); // } // // protected function loadImage():void { // image_loader.load(new URLRequest(image_url[counter++])); // } // // protected function loaded(evt:Event):void { // var temp_data:BitmapData = Bitmap(LoaderInfo(evt.target).content).bitmapData; // if (counter < 8) // switch (counter - 1) { // case 0: // debris_image = temp_data; // case 1: // nebula_image = temp_data; // case 2: // splash_image = temp_data; // case 3: // ship_image = temp_data; // case 4: // missile_image = temp_data; // case 5: // asteroid_image = temp_data; // case 6: // explosion_image = temp_data; // } // else // loadImage(); // } //}