Tutorials

November 17, 2009

Asteroids Style Movement… Now with Firing Lasers

More articles by »
Written by: Par
Tags:
asteriods2thumb

Share on TwitterShare on TumblrSubmit to StumbleUponSave on DeliciousDigg ThisSubmit to reddit

A few months ago I wrote a series on character movement. As a part of that series we discussed developing the movement for a spaceship shooter like Asteroids. Since then I’ve had multiple requests for how to make the ship fire bullets. No problem! This one is simple, super simple, you may smack yourself to the head repetitively with an iron stick for not thinking of it.  But if you don’t have an iron stick, no problem, we’ll get this all squared away and you’ll be on your way to solving this sort of issue with ease from now on.

Okay so why is this super simple? Well,  in the last tutorial we made our ship move in the direction it’s pointing. So wouldn’t that mean making a bullet move in the direction the ship is pointing would be pretty much the same thing? Guess what… it is.  The awesome thing is that it’s a lot easier than making the ship move because we just create the bullet and let it go.

Since this is a pretty simple project, here’s the final source with the FLA and the new Laser.as class.
Asteroids Style Movement… Now with Firing Lasers source code

1. What’s in the Source Archive? Lasers? Guns? Nuclear Bombs?

Sorry guys, we’ll save nuclear bombs for next time. The FLA is pretty much the same as the end of the previous tutorial. I simply added the laser graphic from a previous tutorial. Otherwise there is a minor adjustment to the ship.as class and a new laser.as class. Alright, quickest step in AsGamer history is complete.

2. Altering the Ship.as file to fire our laser

This may be the second quickest step in history. In order to fire lasers we only need to do one thing. Check when a key is pressed and create our laser. So our alterations to the ship class is literally only two lines :D

Here’s the two lines.

			if (key.isDown(Keyboard.SPACE))
				stage.addChild(new Laser(x, y, rotation));

The breakdown:

  • if (key.isDown(Keyboard.SPACE)). This almost reads like english. “If the keyboard’s space key is down”. See what I mean? Pretty simple.
  • stage.addChild(new Laser(x, y, rotation)); Here we add our new laser to the stage… we pass through the ships x, y, and rotation so we can apply all that to our laser.

So our new completed Ship.as looks as follows:

package com.asgamer.directionalmovement
{
 
	import flash.display.MovieClip;
	import flash.display.Stage;
	import flash.events.Event;
	import com.senocular.utils.KeyObject;
	import flash.ui.Keyboard;
 
	public class Ship extends MovieClip
	{
 
		private var key:KeyObject;
		private var speed:Number = 0.3;
		private var rotateSpeed:Number = 5;
		private var vx:Number = 0;
		private var vy:Number = 0;
		private var friction:Number = 0.95;
 
		public function Ship () : void
		{
			key = new KeyObject(stage);
			addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
		}
 
		public function loop(e:Event) : void
		{
 
			if (key.isDown(Keyboard.UP))
			{
				vy += Math.sin(degreesToRadians(rotation)) * speed;
				vx += Math.cos(degreesToRadians(rotation)) * speed;
			} else {
				vy *= friction;
				vx *= friction;
			}
 
			if (key.isDown(Keyboard.RIGHT))
				rotation += rotateSpeed;
			else if (key.isDown(Keyboard.LEFT))
				rotation -= rotateSpeed;
 
			if (key.isDown(Keyboard.SPACE))
				stage.addChild(new Laser(x, y, rotation));
 
			y += vy;
			x += vx;
 
			if (x > stage.stageWidth)
				x = 0;
			else if (x < 0)
				x = stage.stageWidth;
 
			if (y > stage.stageHeight)
				y = 0;
			else if (y < 0)
				y = stage.stageHeight;
		}
 
		public function degreesToRadians(degrees:Number) : Number
		{
			return degrees * Math.PI / 180;
		}
 
	}
 
}

And now for step 3.

3. Writing our Laser Class so we can fire Blue Awesomeness

Okay, in order to make Blue Awesomeness(our laser) we gotta fire out what class variables we may need. As you can see from the ship class we are passing in the x, y, and the rotation of our ship  Since our laser is established at creation and doesn’t alter it’s direction or speed after creation we don’t need to save these details. The only thing our ship needs to know at every frame is where it is going. So we need to store it’s velocity in x and y. So technically we need two class variables vx and vy. While it’s not needed I’m going to add a speed variable as well so I can change it easily later if needed.

So the start of our laser class looks something like this:

package com.asgamer.directionalmovement
{
 
	import flash.display.MovieClip;
	import flash.display.Stage;
	import flash.events.Event;
 
	public class Laser extends MovieClip
	{
 
		private var vx:Number = 0;
		private var vy:Number = 0;
		private var speed:Number = 12;
 
		public function Laser (x:Number, y:Number, shipRotation:Number) : void
		{
 
		}
 
	}
 
}

Not much of a breakdown here… we just created the class variables and gave them a starting value and added the code for our constructor.  Let’s start filling in our constructor now.

So what do we need to do? What needs to be set to start off our laser? Well, let’s see. We need to place it at the correct x and y coordinates, set it’s rotation, find it’s velocity, and setup a listener so we can update the laser on every frame.

Let’s look at some more code… our now filled in constructor. (Plus our degreesToRadians function from the ship class)

		public function Laser (x:Number, y:Number, shipRotation:Number) : void
		{
 
			this.rotation = shipRotation;
			vy += Math.sin(degreesToRadians(shipRotation)) * speed;
			vx += Math.cos(degreesToRadians(shipRotation)) * speed;
 
			this.x = x + vx*2;
			this.y = y + vy*2;
 
			addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
		}
 
		public function degreesToRadians(degrees:Number) : Number
		{
			return degrees * Math.PI / 180;
		}

Alright source code breakdown time.

  • this.rotation = shipRotation; point our laser in the same direction as our ship
  • vy += Math.sin(degreesToRadians(shipRotation)) * speed; vx += Math.cos(degreesToRadians(shipRotation)) * speed; Remember this? You should. It’s the same thing we did to the ship to make it more in the direction it was pointing. I’d explain it with more intelligence and detail but I’m not a mathematician. Let’s just say we are taking the speed of our laser and applying it to the x and y based on the laser’s rotation.
  • this.x = x + vx*2; this.y = y + vy*2; Why +vx*2 you ask? Why not just x? Well, our ship’s x is at our ship’s center. If we add the laser at the same place it will appear in the center of the ship. Not attractive in my opinion. So we know if our laser is shooting in the right direction and continues moving in the right direction when we add vx and vy on each frame then we can just add a little extra vx and vy to the creation and move the laser on away from our ship’s center when it is created. Awesome huh? Simple yet works.
  • addEventListener(Event.ENTER_FRAME, loop, false, 0, true); You know what this is… seriously. Calls loop every on every enter frame.  So what’s that tell you? Next we’re writing a loop function.
  • public function degreesToRadians(degrees:Number) : Number {…} This is the exact same function from the Ship.as class. Simply converts degrees to radians. Now to be honest with you, adding degreesToRadians to every class that needs it is probably a very bad way of programming. I mean, save yourself some time and create a static class and add your common functions to it so you can use them when necessary. Hmm…. static classes, I may have to discuss that in the next tutorial. For now, know that adding degreesToRadians to every class you’ve ever wrote won’t destroy your game or project, it’s just tedious and there is an easier way. But don’t worry about that, we’ll get to it another time.

So what are we doing with each loop? Not much. Just adding our velocity to x and y. Then, of course, making sure our laser is still on screen because if not, we need to remove it. So here’s the last two functions in our laser class.

		public function loop(e:Event) : void
		{
			y += vy;
			x += vx;
 
			if (x > 520 || y > 420 || x < -20 || y < -20)
				destroy();
		}
 
		public function destroy() : void
		{
			if (parent)
				parent.removeChild(this);
 
			removeEventListener(Event.ENTER_FRAME, loop);
		}

Simple huh?

  • y += vy; x += vx; Pretty self explanatory. Add our velocity to x and y and move our ship! Awesome, I love Flash and its simplicity.
  • if (x > 520 || y > 420 || x < -20 || y < -20) destroy(); If our laser runs off the game screen… kill it. Well call our destroy function and it will kill it.
  • if (parent) parent.removeChild(this); If the laser has a parent then remove the laser from the parent. Woah! what is a parent? Well when a MovieClip (or in general any DisplayObject) is inside another MovieClip then that Movieclip is the child and the other is the parent, respectively. So when in our Ship.as we did stage.addChild(new Laser(…)); the parent of our laser became the stage. So all we are doing is saying if the parent isn’t null then it has a parent and we need to remove it from the parent. This way garbage collection can grab it.
  • removeEventListener(Event.ENTER_FRAME, loop); This stops the laser from calling loop every time we enter the frame. Once removed we have no way to access our laser anymore so garbage collection upon it’s next run will grab our laser and get rid of it from memory.

Last here’s the full source code for our laser class.

package com.asgamer.directionalmovement
{
 
	import flash.display.MovieClip;
	import flash.display.Stage;
	import flash.events.Event;
 
	public class Laser extends MovieClip
	{
 
		private var vx:Number = 0;
		private var vy:Number = 0;
		private var speed:Number = 12;
 
		public function Laser (x:Number, y:Number, shipRotation:Number) : void
		{
 
			this.rotation = shipRotation;
			vy += Math.sin(degreesToRadians(shipRotation)) * speed;
			vx += Math.cos(degreesToRadians(shipRotation)) * speed;
 
			this.x = x + vx*2;
			this.y = y + vy*2;
 
			addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
		}
 
		public function loop(e:Event) : void
		{
			y += vy;
			x += vx;
 
			if (x > 520 || y > 420 || x < -20 || y < -20)
				destroy();
		}
 
		public function destroy() : void
		{
			if (parent)
				parent.removeChild(this);
 
			removeEventListener(Event.ENTER_FRAME, loop);
		}
 
		public function degreesToRadians(degrees:Number) : Number
		{
			return degrees * Math.PI / 180;
		}
 
	}
 
 
}

Alright guys and gals, we’re finished!!! You should have something that looks like the below. :D

Similar Posts:


Share on TwitterShare on TumblrSubmit to StumbleUponSave on DeliciousDigg ThisSubmit to reddit


About the Author

Par
Hey! Don't be surprised, I'm a flash developer. While Flash is definitely one of my favorite languages to develop in, most of all I just like making games. If you want to see the games I've developed so far head over to my website, DigitallyBold, in the link below. If you want to know more about what I'm working on now and in the future be sure to follow me on twitter.




21 Comments


  1. Aro

    Great tutorial! You could make a “Build an asteroid style game” tutorial series if you wanted :) . I would add two variables in the lasers class that would be equal to the ships vx and vy and change the lasers vx/vy in the loop function to something like “x += vx + xVar” so that the laser would always go faster then the ship and not fall behind.


  2. Par

    Yes, but a more realistic solution would be to limit the speed of your ship and increase the speed of your laser to something faster than the ship’s max speed.


  3. Aro

    True. The laser will still warp out away from the ship when shooting sideways even if you create a max speed for the ship that is far more then the laser speed. I guess if the laser was fast enough you might not notice it so much.


  4. Aro

    Oops. I mean it will still warp even if the lasers speed is much higher then the ships. “… ship that is far more then…” should be “… ship that is far LESS then…”


  5. Par

    If you are referring to when the laser is moving at a high speed perpendicular to its firing direction that it looks slightly offset and not centered, the fix is actually rather simple. The reason this happens is because when we create a laser we pass it the current x and y of the ship. In the next section of code we adjust the ship’s x and y. So the ships x and y on this frame is different than the lasers. Simply add the “If key is down” code after we adjust the x and y of the ship and this will no longer occur.


  6. Aro

    I’m not sure if you understand what I mean. If you throw a tennis ball out a car window driving down a road it wouldn’t just move in the direction you threw it in but it would also move with the car at the same speed (with no air resistance of course). The same should happen with the ships lasers not only would they have their speed but they should get the ships added onto it.


  7. Mc

    YOU ROCK MY WORLD. THANK YOU SO MUCH!! THANK U THANK U!!!!!!!!


  8. Aro

    Lol! Par I would be on the lookout for any stalkers if I were you :D .


  9. Par

    :D , yes, I think I understand now. I agree with what you said earlier except I’d take care of all the updating in the constructor instead of the loop.

    For everyone else wanting this effect:

    Just add the vx and vy of the ship to the constructor of the laser. Then when you setup the laser’s vx and vy in the constructor just add the vx and vy from the ship to it. This will screw up the rotation a bit so you’d need to adjust it but past that it will work as intended.


  10. Nice! I was going to say you just need to add the ships initial velocity to the lasers when they’re created, but looks like Par answered it already.

    I just finished a version of the original asteroids…
    http://www.blurredistinction.com/test/roids.htm


  11. ant

    thank you very much!

    I am a super newbie with OOP of AS and followed through your tutorial and very found it very informative.. however, now I at a brick war as I am trying to add rocks and hitTest… can anyone help?

    Other than a new class called Rock.as and added a destroy function in ship as well.. the code for the rest is more of less the same..

    Has no clue how to identify which rock to test hit

    Rock.as
    ==============================================================
    package {

    import flash.display.*;
    import flash.events.*;

    public class Rock extends MovieClip{
    public var radius:Number;
    public var cnum:Number;
    var radians:Number=0;
    var speed:Number=0;

    public function Rock(inputRadius:Number):void {
    radius=inputRadius;
    speed=0.1+0.5*Math.random();
    this.addEventListener (Event.ENTER_FRAME, moveCircle);
    }

    public function moveCircle (e:Event) {
    radians += speed;

    x+=Math.round(radius*Math.cos(radians))+1;
    y+=Math.round(radius*Math.sin(radians))+1;

    if (x > stage.stageWidth)
    x = 0;
    else if (x stage.stageHeight)
    y = 0;
    else if (y < 0)
    y = stage.stageHeight;
    }
    }
    }
    ============================================================
    ship.as (addition)
    public function destroy():void {
    parent.removeChild(this);
    removeEventListener(Event.ENTER_FRAME, loop);
    }
    =========================================================
    main.fla

    import flash.utils.Timer;

    var level:int=1;

    genRocks();

    function genRocks() {
    for (var count:int=0; count<level+2; count++) {
    var randomSize=Math.random()*2;
    var newRock:Rock=new Rock(randomSize);
    addChild(newRock);

    newRock.x=Math.random()*550;
    newRock.y=Math.random()*400;

    newRock.scaleX=Math.random()*2+1.5;
    newRock.scaleY=Math.random()*2+1.5;
    }
    }
    var gameTimer:Timer=new Timer(30);
    gameTimer.addEventListener(TimerEvent.TIMER,gameTimerFunc);
    gameTimer.start();

    function gameTimerFunc(t:TimerEvent):void{
    if(ship.hitTestObject(newRock)){
    trace (“hit”);
    ship.destroy();
    gameTimer.stop();
    }
    }
    =================================================

    I get undefined property newRock.. please help! thanks!


  12. I have been reading and following along with your tutorials and have found them perhaps the most intelligible yet. :) so many thanks for that.

    Been working on using them to build a car game. So far, so good. have the engine.as and car.as built and running. Essentially using your ship.as class.

    Now here is the thing I have movieclip built for the car. within it it has movieclip headlights to turn on and off. with frame labels = “headlightson” and “headlightsoff” similar to what you have done with the laser graphics here but included as movieclip inside the car movieclip.

    Can I add keyboard event listener to Car.as so that when ctrl key is pressed the label is sent to headlightson (frame2)? and if pressed a second time to play headlightsoff (frame1)?

    Sorry for the long post but movieclips within movieclips cause me no amount of troubles..


  13. pabloR

    Hi. I was looking here and there for something like scenarios to flash games. I would like to for example to set how many enemies are to be displayed or their behavior (not only from Math functions) what time they should appear on stage, what they do etc. What i need is just a concept. I came to do it by arrays and inside of them objects with their specifications, but after all every period of time i check in the loop of all array (which can be quite huge), which object carries out the limitations. I’d like to find out how it is done in games. Maybe there is a better way to set up scenario of the game, and not only do the random function to put objects to the stage.
    Regarding to your shooter game tutorials, there could be objects set up behind the scene and going upward by the space ship means the stage goes downward and objects with their x,y positions set up at the beggining of level for example goes down. If they meet y=0 pos. they could do their job. But i think thats not clever (and killing performance probably too) to set up all the objects in the movie even behind the scene…
    So that is what im lookin for?
    Just glimpse here in couple days to check for some idea.
    Thanks for so great job. I have never seen better tuts for flash programmers.


  14. I don’t mean to be a hater but you really need a max speed limit for this to be usable as a game. It’s very easy:

    // enforce speed limit – find length of vector
    // and scale down to maxSpeed if necessary
    var currentSpeed:Number = Math.sqrt((vx * vx) + (vy * vy));
    if(currentSpeed > maxSpeed){
    vx *= maxSpeed / currentSpeed;
    vy *= maxSpeed / currentSpeed;
    }


  15. Quintus Kilsdonk

    Its just so awsome that you do everything in .as files and not just code it in frames just like 90% of the as3 tutorial writers do.


  16. Kalle

    I don’t mean to be a hater but you really need a max speed limit for this to be usable as a game. It’s very easy:

    // enforce speed limit – find length of vector
    // and scale down to maxSpeed if necessary
    var currentSpeed:Number = Math.sqrt((vx * vx) + (vy * vy));
    if(currentSpeed > maxSpeed){
    vx *= maxSpeed / currentSpeed;
    vy *= maxSpeed / currentSpeed;
    }
    ___________________________________________________________

    Where?
    More exakt plz? =)


  17. “Where?
    More exakt plz? =)”

    Hi Kalle, put that code I posted in the Ship class in the loop() method right before the “y += vy;” line. That should do it!


  18. Is there a way to pause the game and show a menu?…


  19. gotoAndStop

    I’m trying to add flames while the ship is flying. When forward button is pressed the flame appears from the center of the ship and when the right or left button is pressed the flame appears from the right or left side of the ship, I’m using gotoAndStop, but it only shows frame by the last pressed button, if I press two keys at same time I see onnly one flame. What should I do to see two frames at the same time?


  20. shah

    Hi i was wondering how i could add objects flying towards it to be shot at?


  21. Cooldev

    thanz.. Do you a turned based game..?



Leave a Reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">


Advertisement