Top-Down RPG Shooter — Part 4 — Shooting

Welcome to Part 4 of the Top-Down RPG Shooter flash game tutorial. So far, we’ve set up the project, and added a player that moves with the arrow keys and rotates to face the mouse. In this part, we’re going to add bullets which the player can shoot by clicking and holding the mouse down.

The very basic concepts relating to shooting have already been covered in-depth in Part 10 and Part 11 of my Sidescrolling Platformer tutorial series, so this tutorial might seem a little faster paced. If you find yourself confused with the basic ideas, it might be a good idea to review those tutorials. (But don’t worry, I’ll still try to explain things clearly in this post, too :-)

Step One: Creating the Bullet MovieClip

Just as we did with the player, before we start programming the bullet with AS3, we need to draw it and link it to a class. I drew mine as a black rectangle, 20px wide by 6px tall — but as always, feel free to customize your own art to fit the mood and theme of your game. Be creative! When the art is finished, select it, Convert to Symbol, and link it to a class named “Bullet”, as outlined in the following screenshot:

as3 flash game tutorial top down rpg shooter bullet class movie clip

Step Two: Create a simple Bullet.as class

Create a blank Actionscript 3.0 file (using File –> New… –> Actionscript File), and save it as “Bullet.as” in the same folder as your other project files. Start out by typing or pasting this block of code into Bullet.as. I included a bunch of comments in there to walk you through it.

package
{
    import flash.display.Stage;
    import flash.display.MovieClip;
    import flash.events.Event;

    public class Bullet extends MovieClip
    {
        private var stageRef:Stage; //we'll use this to check if the bullet leaves the screen borders
        private var speed:Number = 10; //speed that the bullet will travel at
        private var xVel:Number = 0; //current x velocity
        private var yVel:Number = 0; //current y velocity
        private var rotationInRadians = 0; //convenient to store our rotation in radians instead of degrees

        //our constructor requires: the stage, the position of the bullet, and the direction the bullet should be facing
        public function Bullet(stageRef:Stage, X:int, Y:int, rotationInDegrees:Number):void
        {
            this.stageRef = stageRef;
            this.x = X;
            this.y = Y;
            this.rotation = rotationInDegrees;
            this.rotationInRadians = rotationInDegrees * Math.PI / 180; //convert degrees to radians, for trigonometry
        }
    }
}

Basically, all this does is initialize a bullet object in a given position, rotated in a given direction. It’s enough to get started with.

OK, here’s the plan. This tutorial is going to show 2 implementations of the bullet.

  1. The simple method: check for mouse clicks, and add 1 bullet for each click.
  2. A slightly more complicated method: start shooting when the mouse it clicked down, and make the player keep shooting rapid-fire until the mouse is released.

The second method takes a few extra lines of code, but is much more fun ;) I’ll demonstrate both, for clarity. You can also choose which one you’d prefer to use in your game. (Or maybe you can have multiple weapons, each with a different firing mechanism? Be creative!)

Step Three: Bullet Version 1: Add one bullet per click

Back in Main.as, we need to check for mouse clicks on the stage. When the player clicks, we’ll add a new bullet; simple as that. How do we check for clicks? You guessed it! With an EventListener!

//you'll need to import the MouseEvent to use it
import flash.events.MouseEvent;

//add this line to the bottom of the "Main()" constructor function
stage.addEventListener(MouseEvent.CLICK, shootBullet, false, 0, true);

//then add this new function to handle the mouse event
public function shootBullet(e:MouseEvent):void
{
    var bullet:Bullet = new Bullet(stage, player.x, player.y, player.rotation);
    stage.addChild(bullet);
}

Alright, that’s a great start… but the bullets won’t actually do anything yet… they’ll just sit wherever you spawn them, which is a pretty sad fate for a bullet :(

Not to worry! As I’m sure you’re an expert with by now, we’ll just create a loop for the bullets.

There are actually two ways we can go about doing this:

  1. We can add an EventListener to the Bullet.as class, so each Bullet automatically loops. (Like we did in Part 10 of the Platformer)
  2. Another method is to just have one single EventListener in the Main class, which calls the loop function in all of the bullets.

It’s possible that using one or the other could make your code run faster. Or maybe one of them fits better with the way you are structuring your code. In any case, it doesn’t really matter too much for this game, but I’ll show method 2, just so you know how it works.

It’s not too complicated; we just need to create an array of all the bullets that’ll be on the screen. Then each frame, we can call the loop function on every object in the array.

Add these new lines of code to Main.as. I’ve highlighted and commented the new lines:

package
{
    import flash.display.Stage;
    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.events.MouseEvent;

    public class Main extends MovieClip
    {
        public var player:Player;
        public var bulletList:Array = []; //new array for the bullets

        public function Main():void
        {
            player = new Player(stage, 320, 240);
            stage.addChild(player);

            stage.addEventListener(MouseEvent.CLICK, shootBullet, false, 0, true);
            stage.addEventListener(Event.ENTER_FRAME, loop, false, 0, true); //add an EventListener for the loop
        }

        public function loop(e:Event):void //create the loop function
        {
            if(bulletList.length > 0) //if there are any bullets in the bullet list
            {
                for(var i:int = bulletList.length-1; i >= 0; i--) //for each one
                {
                    bulletList[i].loop(); //call its loop() function
                }
            }
        }

        public function shootBullet(e:MouseEvent):void
        {
            var bullet:Bullet = new Bullet(stage, player.x, player.y, player.rotation);
            bullet.addEventListener(Event.REMOVED_FROM_STAGE, bulletRemoved, false, 0, true); //triggers the "bulletRemoved()" function whenever this bullet is removed from the stage
            bulletList.push(bullet); //add this bullet to the bulletList array
            stage.addChild(bullet);
        }

        public function bulletRemoved(e:Event):void
        {
            e.currentTarget.removeEventListener(Event.REMOVED_FROM_STAGE, bulletRemoved); //remove the event listener so we don't get any errors
            bulletList.splice(bulletList.indexOf(e.currentTarget),1); //remove this bullet from the bulletList array
        }
    }
}

Great! If anything is confusing, let me know in the comments, and I can expand. If not… let’s move on to implementing the loop() function inside Bullet.as.

What does the loop need to do? We want the bullet to move forward every frame. But how do we know which direction forward is? The answer: trigonometry. Gotta love trigonometry. In the last tutorial, we used the inverse tangent of the mouse’s x and y position to determine what angle to rotate to. In this tutorial, we will use the sine and cosine of the bullet’s angle to calculate its velocity.

We want to move the bullet forward with a speed of 10 pixels per frame, in any possible direction. But we can only update its x and position every frame… there is no magic built-in concept of moving “forward”. We solve this by converting our speed into x and y velocities, depending on the bullet’s rotation, and using those velocities to update the x and y positions of the bullet.

Here’s the math. It’s basic trig:

  • The xVelocity is just the speed, multiplied by the cosine of the bullet’s rotation.
  • Likewise, the yVelocity is the speed, multiplied by the sine of the bullet’s rotation.

as3gametuts trigonometry math tutorial game programming sin cos velocity

With that in mind, our code is really easy to implement. The only thing you need to remember is that trigonometric functions require the angle to be in radians, not degrees. But don’t worry, remember we already stored that value in the “rotationInRadians” variable.

Here’s the full code for the loop in Bullet.as. Notice that there’s one more chunk of code at the end of the loop() function that we didn’t discuss: a conditional that checks if the bullet is outside the edges of the stage. If it is, the bullet removes itself.

public function loop():void //we don't need to include the "e:Event" because we aren't using an EventListener
{
    xVel = Math.cos(rotationInRadians) * speed; //uses the cosine to get the xVel from the speed and rotation
    yVel = Math.sin(rotationInRadians) * speed; //uses the sine to get the yVel

    x += xVel; //updates the position
    y += yVel;

    //if the bullet goes off the edge of the screen...
    if(x > stageRef.stageWidth || x < 0 || y > stageRef.stageHeight || y < 0)
    {
        this.parent.removeChild(this); //remove the bullet
    }
}

Awesome! That will move the bullet perfectly. And it will remove the bullets that go too far off the screen!

Here’s a demo of what we have so far. Move with the arrow keys or WASD. Aim and fire with the mouse, by clicking. Pretty cool, eh?

Step Four: Bullet Version 2: Rapid Fire!!

When you’re playing a game, what’s cooler than a weapon that shoots one round at a time? A rapid fire weapon! This isn’t too much extra effort to add, so I’m going to show you how to convert your game so that bullets are continuously added until you release the mouse click.

Essentially, we replace the MouseEvent.CLICK event listener with 2 new ones: MouseEvent.MOUSE_DOWN and MouseEvent.MOUSE_UP. These trigger functions which set a new Boolean, “mousePressed“, to be either true or false. Then in the loop function, if mousePressed is true, we shoot a new bullet! The one complication is that we don’t want to add a new bullet every single frame. That would be too many bullets! So we add a delay between shooting; we only add a new bullet once every 8 frames (in this example — definitely try experimenting). It actually looks pretty awesome if you remove the delay altogether (by setting delayMax = 1). Try it, if you dare.

This is the entire code for the new version of Main.as. I’ve highlighted the changes that need to be made.

package
{
	import flash.display.Stage;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.MouseEvent;

	public class Main extends MovieClip
	{
		public var player:Player;

		public var bulletList:Array = [];

		public var mousePressed:Boolean = false; //keeps track of whether the mouse is currently pressed down
		public var delayCounter:int = 0; //we use this to add delay between the shots
		public var delayMax:int = 8; //try changing this number to shoot more or less rapidly

		public function Main():void
		{
			player = new Player(stage, 320, 240);
			stage.addChild(player);

			//stage.addEventListener(MouseEvent.CLICK, shootBullet, false, 0, true); //remove this
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, false, 0, true);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false, 0, true);

			stage.addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
		}

		public function loop(e:Event):void
		{
			if(mousePressed) // as long as the mouse is pressed...
			{
				delayCounter++; //increase the delayCounter by 1
				if(delayCounter == delayMax) //if it reaches the max...
				{
					shootBullet(); //shoot a bullet
					delayCounter = 0; //reset the delay counter so there is a pause between bullets
				}
			}

			if(bulletList.length > 0)
			{
				for(var i:int = bulletList.length-1; i >= 0; i--)
				{
					bulletList[i].loop();
				}
			}
		}

		public function mouseDownHandler(e:MouseEvent):void //add this function
		{
			mousePressed = true; //set mousePressed to true
		}

		public function mouseUpHandler(e:MouseEvent):void //add this function
		{
			mousePressed = false; //reset this to false
		}

		public function shootBullet():void //delete the "e:MouseEvent" parameter
		{
			var bullet:Bullet = new Bullet(stage, player.x, player.y, player.rotation);
			bullet.addEventListener(Event.REMOVED_FROM_STAGE, bulletRemoved, false, 0, true);
			bulletList.push(bullet);
			stage.addChild(bullet);
		}

		public function bulletRemoved(e:Event):void
		{
			e.currentTarget.removeEventListener(Event.REMOVED_FROM_STAGE, bulletRemoved);
			bulletList.splice(bulletList.indexOf(e.currentTarget),1);
		}
	}
}

Also, for your convenience, here is the full code of the Bullet.as class. (Note: I didn’t make any changes to this code since the single-fire method)

package
{
    import flash.display.Stage;
    import flash.display.MovieClip;
    import flash.events.Event;

    public class Bullet extends MovieClip
    {
        private var stageRef:Stage;
        private var speed:Number = 10;
		private var xVel:Number = 0;
		private var yVel:Number = 0;
		private var rotationInRadians = 0;

        public function Bullet(stageRef:Stage, X:int, Y:int, rotationInDegrees:Number):void
        {
            this.stageRef = stageRef;
            this.x = X;
            this.y = Y;

			this.rotation = rotationInDegrees;
			this.rotationInRadians = rotationInDegrees * Math.PI / 180;
        }

        public function loop():void
        {
			xVel = Math.cos(rotationInRadians) * speed;
			yVel = Math.sin(rotationInRadians) * speed;

			x += xVel;
			y += yVel;

			if(x > stageRef.stageWidth || x < 0 || y > stageRef.stageHeight || y < 0)
			{
				this.parent.removeChild(this);
			}
        }
    }
}

And here’s the result. Have fun!

That’s it for today’s tutorial! We have successfully programmed a player that can move, rotate, and shoot. Up next, we will begin to add more elements to the game for the player to interact with. You can get the full source files for this tutorial here.

Anything you would like clarified? Suggestions for future tutorial parts? Let me know in the comments. Thanks for reading! :-)

About these ads

14 comments on “Top-Down RPG Shooter — Part 4 — Shooting

  1. Ben L. says:

    Excellent tutorial as always. One problem that I and someone else have from the last tutorial is that the WASD controls do not work properly. Pressing A and releasing, for example, will sometimes cause the player to move left even after the button is released. Do you have any suggestions on how to fix this?

    • James A says:

      Maybe it’s because the WASD keys are also linked to Flash, try clicking Control inside the flash player and ticking “Disable Keyboard Shortcuts”

    • walker says:

      I need a enemy ai code to add to my top down shooter as3 flash professional cs5.5

  2. Nick W. says:

    This is an awesome tutorial. I am having trouble adding my enemies and I am trying to add things like tables and opening doors, I am mainly having trouble with the class to class collisions. Thank you for reading and I love this site.

  3. Gman says:

    Wll yeah, I think you should go into sprite pooling so you aren’t instantiating a bullet with the new keyword etc… It’s more advanced but not that difficult to implement. ie: People get taught properly from the beginning. I DO like your tutorials a lot and I think they’re among the best I have seen,

  4. Medo says:

    I am waiting for Part 5 Common :D

    What takes you so long ^^

  5. Daniel says:

    This is a really great tutorial so far and I look forward to the next stages. I’ve only just begun coding with Flash and I must say that due to tutorials that are as descriptive and in depth as this, I’m learning pretty quickly and for that, I thank-you.

    Keep up the great work.

  6. Martynas says:

    Are you still planning on making the 5th part? i’ve just found these tutorials, but i’m not sure if i should use them, if you’re not planning on finishing it.

  7. Loving this so far, I am excited to see more posts.

  8. steve says:

    just sat and typed all 4 of your tuts in word for word and works great thx very much only problem I had was the bullet graphic named its class name with small b instead of big B
    looking forward to next instalment,maybe something like some baddies on screen

  9. CMS says:

    Thanks a lot!

  10. kimster says:

    can i download this game,. or pls send me a file,. i like the game but a little dizzy about the instructions

  11. William says:

    I tried putting this all in and im left with a lot of errors needing identifires before Rightparen and
    before lessthan. Also expecting semicolons before class, any help with these errors? They are found
    in bullet, main, and player

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s