Jun
25

Easy Keyboard Controls and Game Design

by Paul


In this tutorial I will be going over some of the basic elements to making a game in Flash. This being the first in a short series of tutorials on the subject, I will cover the main game loop and keyboard controls.

First off we need to set up some simple graphics. I did some REALLY simple graphics as you can see. The ship_mc being just a triangle and on the 3rd frame of the ship movie clip it get’s a little fire tail. Since I am just imitating Atari Interactive, Inc.’s Asteroids, we don’t really need anything more then that for now.
Now, to get our initial code setup:

package {
        import flash.display.*;
        import flash.events.*;
        import flash.text.*;
        import flash.utils.Timer;

        public class Asteroids extends MovieClip {
                protected var mainTimer:Timer;

                public function Asteroids() {
                        mainTimer = new Timer(20);
                        mainTimer.addEventListener(TimerEvent.TIMER, MainLoop)
                        ship_mc.stop();

                        this.addEventListener(Event.DEACTIVATE, deactive);
                        this.addEventListener(Event.ACTIVATE, active);
                        stage.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
                        stage.addEventListener(KeyboardEvent.KEY_UP, kUp);
                }

                protected function MainLoop(e:TimerEvent):void {
                }

                protected function deactive(e:Event):void {
                        mainTimer.stop();
                }
                protected function active(e:Event):void {
                        mainTimer.start();
                }
                protected function kDown(e:KeyboardEvent):void {
                }
                protected function kUp(e:KeyboardEvent):void {
                }
        }
}

So far this is just a skeleton of our game. We have our KEY_DOWN and KEY_UP events which will handle all of our keyboard controls later. But, more importantly, we have our mainTimer. By setting up our game engine to run based on a timer I can later change the frame rate without changing the game dynamics (I left the frame rate at 12/sec for the example above). This is also useful if your game has too many graphical elements and the frame rate drops on some machines, this will preserve the speed of the game itself.
So, please notice that I have the Timer set to “tick” every 20 milliseconds (1000 milliseconds to 1 seconds) which is about 50 ticks per second. Because of that, we are going to have some small values to work with when playing with flight speed and rotation in our ship.
But, first let us think about keyboard controls:

protected var thrust:Boolean;
protected var rpm:Number;
protected function kDown(e:KeyboardEvent):void {
//      trace(e.keyCode);
        if(e.keyCode == 37 || e.keyCode == 65 || e.keyCode == 100){
                rpm = -4;
//              trace("LEFT");
        }
        if(e.keyCode == 38 || e.keyCode == 87 || e.keyCode == 104){
                thrust = true;
//              trace("UP");
        }
        if(e.keyCode == 39 || e.keyCode == 68 || e.keyCode == 102){
                rpm = 4;
//              trace("RIGHT");
        }
}
protected function kUp(e:KeyboardEvent):void {
        if(e.keyCode == 37 || e.keyCode == 65 || e.keyCode == 100){
                rpm = 0;
//              trace("LEFT");
        }
        if(e.keyCode == 38 || e.keyCode == 87 || e.keyCode == 104){
                thrust = false;
//              trace("UP");
        }
        if(e.keyCode == 39 || e.keyCode == 68 || e.keyCode == 102){
                rpm = 0;
//              trace("RIGHT");
        }
}

rpm and thrust are going to control our ships flight in the MainLoop later. The keyCode is a number which represents the key being pressed or released on the keyboard. I prefer to set a value on down because the KEY_DOWN event will be fired off numerous times very quickly if the key is held too long. I have the controls set up to work with WASD, the arrow keys, or the num pad.
To find the key values I just traced e.keyCode in the kUp() function and tried pressing the keys in question. This got me the numbers you see in the code above. The commented out traces are there to make sure we have the correct keys, since we don’t have any other indicator yet.

The next step is a simple one (except for some of the math):

protected const TOP_SPEED:Number = 10;
protected const ACCELERATION:Number = .1;
protected const FRICTION:Number = .995;

protected var mainTimer:Timer;
protected var speedX:Number;
protected var speedY:Number;

public function Asteroids() {
        rpm = 0;
        speedX = 0;
        speedY = 0;
        …
}

protected function MainLoop(e:TimerEvent):void {
        ship_mc.rotation += rpm;

        var dx:Number = ACCELERATION * Math.cos((ship_mc.rotation*Math.PI)/180);
        var dy:Number = ACCELERATION * Math.sin((ship_mc.rotation*Math.PI)/180);

        if(thrust){
                speedX += dx;
                speedY += dy;
//We want to have a max speed in any direction of the compass
                var speed = speedX*speedX + speedY*speedY;
                if(speed >= TOP_SPEED*TOP_SPEED) {
                        speedX *= (TOP_SPEED*TOP_SPEED)/speed;
                        speedY *= (TOP_SPEED*TOP_SPEED)/speed;
                }
                ship_mc.gotoAndStop(3);
        }else {
                ship_mc.gotoAndStop(1);
        }
//I know there is no friction in space, but I can’t remember if Asteroids had it or not.
        speedX *= FRICTION;
        speedY *= FRICTION;

        ship_mc.x += speedX;
        ship_mc.y += speedY;

        if(ship_mc.x > 560) ship_mc.x = -10;
        if(ship_mc.x < -10) ship_mc.x = 560;
        if(ship_mc.y > 410) ship_mc.y = -10;
        if(ship_mc.y < -10) ship_mc.y = 410;
}

Here we have initialized some variables in the constructor first. Then we filled in our MainLoop to give our spaceship some life. I would think the rotation is self evident… it’s the first line of the method. I used some constants in my math (ACCELERATION, TOP_SPEED, and FRICTION) so that I can easily adjust the game settings all in one place, without searching for the parts of the code that I need changed.
The last group of if statements above are checking to see in our ship has flown off the screen and if it has it will be moved to the other side.

That’s all for now. Next time I will probably add some asteroids and talk a little about collision detection.

You can download the source for this example here: AsteroidsSource.rar

Tags: , ,

Related Posts

3 Responses to “Easy Keyboard Controls and Game Design” RSS

  1. taRs Says:

    Very interesting article!

    I’ve got one question: there is any special purpose in writing MainLoop method with capital M (like a class name)?

  2. Fernanda Gomez Says:

    Great tutorial, Thanks

  3. Paul Says:

    taRs,
    Using capitalization or not is completely up to the preference of the programmer. The only important thing about it is consistency, since ActionScript is case sensitive in almost all cases.
    Personally I like to name my classes and important methods with capitalization. I also (usually) make my constants all CAPS. But, there are exceptions all over my code.
    Maybe I should work with more strict naming standard, but it’s just not something that I’ve ever put too much effort into.

    Right, so to answer your question again… No, there isn’t any special reason why I used a capital M. It just seemed fitting at the time.

    ClickPopMedia