Top-Down RPG Shooter — Part 2 — Player Movement

Welcome back to the second part of my “Top-Down RPG Shooter” flash game tutorial. In the last part, we set up a new project and linked it to an external Document Class, and we added the Player to the stage. In this tutorial, we’re going to program keyboard controls to move the player.

Step 1: KeyObject.as

We are going to make use of a really great open-source class called “KeyObject.as“. This class was written by a talented developer named senocular. It provides a really simple but powerful way to check which keyboard keys are pressed.

Copy and paste this class into a new .as actionscript file, and save it as “KeyObject.as” in the same folder as your main project:

package {

	import flash.display.Stage;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	/**
	 * The KeyObject class recreates functionality of
	 * Key.isDown of ActionScript 1 and 2
	 *
	 * Usage:
	 * var key:KeyObject = new KeyObject(stage);
	 * if (key.isDown(key.LEFT)) { ... }
	 */
	dynamic public class KeyObject extends Proxy {

		private static var stage:Stage;
		private static var keysDown:Object;

		public function KeyObject(stage:Stage) {
			construct(stage);
		}

		public function construct(stage:Stage):void {
			KeyObject.stage = stage;
			keysDown = new Object();
			stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
			stage.addEventListener(KeyboardEvent.KEY_UP, keyReleased);
		}

		flash_proxy override function getProperty(name:*):* {
			return (name in Keyboard) ? Keyboard[name] : -1;
		}

		public function isDown(keyCode:uint):Boolean {
			return Boolean(keyCode in keysDown);
		}

		public function deconstruct():void {
			stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
			stage.removeEventListener(KeyboardEvent.KEY_UP, keyReleased);
			keysDown = new Object();
			KeyObject.stage = null;
		}

		private function keyPressed(evt:KeyboardEvent):void {
			keysDown[evt.keyCode] = true;
		}

		private function keyReleased(evt:KeyboardEvent):void {
			delete keysDown[evt.keyCode];
		}
	}
}

How do we use this class? Basically, we’re going to create an instance of it called “key” in our Player class (or wherever we need to access the keyboard controls). Then in that class, we can check the Boolean value of the keyObject’s isDown() function for specific keys. We can refer to keys by their unique keyCode. For example, if key.isDown(65) returns true, it means that the “A” keyboard key is currently being pressed.

Step 2: stageRef

Great! Now we just need to integrate this into our Player class. But as you can see in the constructor in the above code, KeyObject requires a reference to the Stage in order to work. Which means we also need to give our Player class a reference to the stage, so it can pass this reference to its KeyObject.

Modify your Main.as file so that it passes the stage to the player as an argument:

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

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

        public function Main():void
        {
            player = new Player(stage, 320, 240); //pass the stage as the first argument
            stage.addChild(player);
        }
    }
}

Now, change the player so that it assigns this stage parameter to a variable that it can use later:

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

    public class Player extends MovieClip
    {
        public var stageRef:Stage; //create an instance variable for the stage reference

        public function Player(stageRef:Stage, X:int, Y:int):void //modify the constructor
        {
            this.stageRef = stageRef; //assign the parameter to this instance's stageRef variable
            this.x = X;
            this.y = Y;
        }
    }
}

Step 3: KeyObject Instance

Cool. Now, let’s import KeyObject.as into the Player.as file, and create a new instance variable of it, using the stageRef.

package
{
    import flash.display.Stage;
    import flash.display.MovieClip;
    import flash.events.Event;
    import KeyObject; //add this import

    public class Player extends MovieClip
    {
        public var stageRef:Stage;
        public var key:KeyObject; //add this instance variable

        public function Player(stageRef:Stage, X:int, Y:int):void //modify the constructor
        {
            this.stageRef = stageRef;
            this.x = X;
            this.y = Y;

            key = new KeyObject(stageRef); //instantiate "key" by passing it a reference to the stage
        }
    }
}

You might be thinking, “I thought you said KeyObject was simple to use! This is an awful lot of work to set it up.” And it’s true, it does take a bit of work to set up. But now that it’s set up, it’s super simple to use! In the long run, it’s more efficient than using EventListeners for KEY_UP and KEY_DOWN.

Step 4: Loop

In order to give the player movement, we need to give him a loop function. If you’ve read my previous tutorials, you should be familiar with adding an EventListener to do this.

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

    public class Player extends MovieClip
    {
        public var stageRef:Stage;
        public var key:KeyObject;

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

            key = new KeyObject(stageRef);

            addEventListener(Event.ENTER_FRAME, loop, false, 0, true); //add the EventListener
        }

        public function loop(e:Event):void //create this (currently empty) function
        {

        }
    }
}

Step 5: checkKeypresses()

Alright, I’m about to write a new function, checkKeypresses(), which we will call every frame in our loop function. We don’t really need this to be in a separate function, but it’s important to keep your code organized, so I’m putting all of the keypress handlers in a function.

I’m also about to define 4 new Boolean variables, to keep track of which keys are currently being pressed.

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

    public class Player extends MovieClip
    {
        public var stageRef:Stage;
        public var key:KeyObject;

        //add these four variables:
        public var leftPressed:Boolean = false; //keeps track of whether the left arrow key is pressed
        public var rightPressed:Boolean = false; //same, but for right key pressed
        public var upPressed:Boolean = false; //...up key pressed
        public var downPressed:Boolean = false; //...down key pressed

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

            key = new KeyObject(stageRef);

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

        public function loop(e:Event):void
        {
            checkKeypresses(); //call "checkKeypresses()" every frame
        }

        public function checkKeypresses():void
        {
            // I used http://www.dakmm.com/?p=272 as a reference to get the keyCode numbers for each key
            if(key.isDown(37) || key.isDown(65)){ // if left arrow or A is pressed
                leftPressed = true;
            } else {
                leftPressed = false;
            }

            if(key.isDown(38) || key.isDown(87)){ // if up arrow or W is pressed
                upPressed = true;
            } else {
                upPressed = false;
            }

            if(key.isDown(39) || key.isDown(68)){ //if right arrow or D is pressed
                rightPressed = true;
            } else {
                rightPressed = false;
            }

            if(key.isDown(40) || key.isDown(83)){ //if down arrow or S is pressed
                downPressed = true;
            } else {
                downPressed = false;
            }
        }
    }
}

That’s all we need to do to keep track of the keyboard! One cool thing you may have noticed, is that we’re allowing the player to either use the arrow keys or the WASD keys. This is very user-friendly, because different people like to play games with different keys, depending on what kind of computer they’re using.

Step 6: Make the Player move!

Finally, we can use the keyPressed variables to adjust the position of the player. Create a Number variable named speed, which you can use to adjust how fast the player moves. I’m setting mine to 5, right now, but I might adjust it later.

All that’s left to do is add a conditional in the loop function, after checkKeypresses(), which will change the x and y coordinates of the player:

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

    public class Player extends MovieClip
    {
        public var stageRef:Stage;
        public var key:KeyObject;

        public var leftPressed:Boolean = false;
        public var rightPressed:Boolean = false;
        public var upPressed:Boolean = false;
        public var downPressed:Boolean = false;

        public var speed:Number = 5; //add this Number variable

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

            key = new KeyObject(stageRef);

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

        public function loop(e:Event):void
        {
            checkKeypresses();

            if(leftPressed){
                x -= speed; // move to the left if leftPressed is true
            } else if(rightPressed){
                x += speed; // move to the right if rightPressed is true
            }

            if(upPressed){
                y -= speed; // move up if upPressed is true
            } else if(downPressed){
                y += speed; // move down if downPressed is true
            }
        }

        public function checkKeypresses():void
        {
            if(key.isDown(37) || key.isDown(65)){
                leftPressed = true;
            } else {
                leftPressed = false;
            }

            if(key.isDown(38) || key.isDown(87)){
                upPressed = true;
            } else {
                upPressed = false;
            }

            if(key.isDown(39) || key.isDown(68)){
                rightPressed = true;
            } else {
                rightPressed = false;
            }

            if(key.isDown(40) || key.isDown(83)){
                downPressed = true;
            } else {
                downPressed = false;
            }
        }
    }
}

That’s it! Your player should now be able to run around the screen, using either the arrow keys or WASD.

Try it out in this demo:

You can grab all of the source files, here: https://www.box.com/s/9v6p3byyio5hbiqmko2o

Next time, we’ll make the player rotate to face the mouse, in preparation to add a shooting system.

Cheers!

Ben

14 comments on “Top-Down RPG Shooter — Part 2 — Player Movement

  1. Bob says:

    Woah, part two came out super close back-to-back. I found your page during the hiatus and basically fell in love with it. I am so excited to see new stuff pouring in, I am seriously bouncing.

  2. Brian Spates says:

    When you move around, tiny parts of the movie clip turn white, as the same with the pong tutorial. Is there a way to fix this?

    • Guddis says:

      Lower the framerate, it’ll still look good at about 30. (I believe this is about 60 pr second)

  3. Bryon says:

    Don’t forget to add this one too your menu.

  4. Ben L. says:

    Excellent tutorial. Waiting for 3rd one!

  5. manvirgr3wal says:

    the wasd keys aren’t working too great, but I even checked your source and mines the same. The problem that I have is that when I press the wasd keys for a bit, the A key stops moving the character left and the character moves up on its own.

  6. Carlos says:

    Hi Ben.

    I ran the code with player movement and i found this error

    Error: Error #2090: The Proxy class does not implement callProperty. It must be overridden by a subclass.
    at Error$/throwError()
    at flash.utils::Proxy/http://www.adobe.com/2006/actionscript/flash/proxy::callProperty()
    at Man/checkKeypressed()
    at Man/loop()

    I called my player man.

    Can you help me with that?
    I have searched in many websites and i couldn’t find the solution.

  7. Loon says:

    Great tutorials! I keep getting this error when I attempt to run the code and i’m not sure why.

    ArgumentError: Error #1063: Argument count mismatch on Player(). Expected 3, got 0.
    at flash.display::Sprite/constructChildren()
    at flash.display::Sprite()
    at flash.display::MovieClip()

  8. daisle says:

    Argh I wish I could understand this..
    As I am trying to move my character for a top down game (not shooting)
    I can’t get the movement to work..
    I keep on getting this error: Desktop\Top Down\KeyObject.as, Line 1 1172: Definition __AS3__.vec:Vector could not be found.
    Line 1 is package?

    Other then that, great tutorial C=

  9. Carl says:

    1046: Type was not found or was not a compile-time constant: Player.

  10. Y8 says:

    Hi, you are awesome. Nice code! You programming skills are great.

  11. Zelos says:

    Very good tutorial!

    In new versions your code of KeyObject.as won’t work because 2 signatures are slightly wrong:

    “dynamic public class KeyObject extends Proxy” should be “public dynamic class KeyObject extends Proxy”
    and
    “flash_proxy override function getProperty(name:*):*” should be “override flash_proxy function getProperty(name:*):*”

    Notice the slight change of the first 2 keywords.

Leave a reply to Zelos Cancel reply