Game Input: Polling vs. Events

Sep 27 2009 Published by under ActionScript, Flash

Send to Kindle

As mentioned, the single “controversial” part of my talk at Flash on the Beach this year was in questioning polling for input in Flash games. In truth, it was hardly controversial. No death threats. No twitter-based lynch mobs. Just that a couple of guys came up to me and politely expressed disagreement later, and we had a conversation about it. But, as said conversations were done later in the evening at the Old Ship, I thought it might be worth discussing in a clearer state of mind.

So the idea is that I said I thought it was better, i.e. more efficient, to use events for keyboard and mouse input, rather than polling. A few people have made keyboard manager classes which allow you to check which keys are down. You can then poll this class to see if the navigation / action keys you are interested in are currently down, and act accordingly. If you are doing this in the game loop, this is going to happen on every frame or interval, and to me, this does not make sense.

To demonstrate this kind of setup, here is a bare bones game class. It’s using Richard Lord‘s KeyPoll class.

package
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.ui.Keyboard;

	import uk.co.bigroom.input.KeyPoll;

	public class KeyboardGame extends Sprite
	{
		private var keyPoll:KeyPoll;

		// view
		private var character:Sprite;

		// model
		private var xpos:Number = 200;
		private var speed:Number = 0;
		private var direction:Number = 0;

		public function KeyboardGame()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;

			keyPoll = new KeyPoll(stage);

			character = new Sprite();
			character.graphics.lineStyle(0);
			character.graphics.drawCircle(0, 0, 10);
			character.graphics.lineTo(20, 0);
			character.x = xpos;
			character.y = 200;
			addChild(character);

			addEventListener(Event.ENTER_FRAME, gameLoop);
		}

		protected function gameLoop(event:Event):void
		{
			input();
			update();
			render();
		}

		protected function input():void
		{
			if(keyPoll.isDown(Keyboard.LEFT))
			{
				direction = 180;
				speed = -5;
			}
			else if(keyPoll.isDown(Keyboard.RIGHT))
			{
				direction = 0;
				speed = 5;
			}
			else
			{
				speed = 0;
			}
		}

		protected function update():void
		{
			xpos += speed;
		}

		protected function render():void
		{
			character.x = xpos;
			character.rotation = direction;
		}
	}
}

For the sake of simplicity, this is all in one class, with the view being a sprite with some graphics, and the “model” being a few class variables. The game loop runs on every frame and polls for input, updates the model, and renders the view.

The input method polls the keyPoll class, checking to see if the left or right cursor keys are pressed. If so, it adjusts the direction and speed in the “model”. If neither is pressed, direction is unchanged and speed is 0.

The update method simply updates the xpos based on the speed and the render method moves and rotates the character based on the model. Run it, press the left and right keys and the character turns and moves in the right direction. Yay.

So what was I proposing instead? To cut out the polling part. The idea being that the only time you need to handle input is when a key goes down or up. Not on every single frame. So you do something like this:

package
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;

	public class KeyboardGameNoPoll extends Sprite
	{
		// view
		private var character:Sprite;

		// model
		private var xpos:Number = 200;
		private var speed:Number = 0;
		private var direction:Number = 0;

		public function KeyboardGameNoPoll()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;

			character = new Sprite();
			character.graphics.lineStyle(0);
			character.graphics.drawCircle(0, 0, 10);
			character.graphics.lineTo(20, 0);
			character.x = xpos;
			character.y = 200;
			addChild(character);

			addEventListener(Event.ENTER_FRAME, gameLoop);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
			stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
		}

		protected function onKeyDown(event:KeyboardEvent):void
		{
			if(event.keyCode == Keyboard.LEFT)
			{
				direction = 180;
				speed = -5;
			}
			else if(event.keyCode == Keyboard.RIGHT)
			{
				direction = 0;
				speed = 5;
			}
		}

		protected function onKeyUp(event:KeyboardEvent):void
		{
			if(event.keyCode == Keyboard.LEFT || event.keyCode == Keyboard.RIGHT)
			{
				speed = 0;
			}
		}

		protected function gameLoop(event:Event):void
		{
			// input by events
			update();
			render();
		}

		protected function update():void
		{
			xpos += speed;
		}

		protected function render():void
		{
			character.x = xpos;
			character.rotation = direction;
		}
	}
}

Here, we add event listeners for key up and key down. On key down, we check which keys is being pressed, and update the model accordingly. When either of the special keys is released, set speed to 0.

So, no input method, but update and render are exactly the same.

Now, when a key is being pressed, it’s going to get multiple, repeated key down events, so you might say this is basically just polling anyway, even if only when the key is down. Yes, but it’s not like that is just a substitution for the polling in the first version. Actually, if you look in the KeyPoll class, or any other similar keyboard manager class, you are going to find an event listener for key up and key down. And just like this version, those are going to be run repeatedly when a key is held down. So the polling in the first version is ON TOP of that repeated key event stuff, which is going to happen regardless.

Problems, and a Comprimise

Even in this simple example, however, a problem can soon arise: in the non-polling version, you could run into this situation:

1. User presses right key and holds it. Character starts moving right.
2. User presses left key and holds it, while still holding right key. Character starts moving left.
3. User releases either key, while still holding the other. Character stops, even though one of the action keys is still down.

This would not happen in the polling version.

If this kind of problem happens in such a simple example, you are bound to run into it in many other forms in more complex input schemes. So it’s likely that you will have to start adding some additional logic to address it. My initial response was to create a leftKeyDown and rightKeyDown property, set these to true in the key down handler, and false in the key up handler and check the value of both to see if speed should be 0:

protected function onKeyUp(event:KeyboardEvent):void
{
	if(event.keyCode == Keyboard.LEFT)
	{
		leftKeyDown = false;
	}
	else if(event.keyCode == Keyboard.RIGHT)
	{
		rightKeyDown = false;
	}
	if(!leftKeyDown && !rightKeyDown)
	{
		speed = 0;
	}
}

Unfortunately, this still breaks. If the user presses and holds right, and taps and releases left, the character will continue to move left. So you could do something ridiculous like this:

protected function onKeyUp(event:KeyboardEvent):void
{
	if(event.keyCode == Keyboard.LEFT)
	{
		leftKeyDown = false;
	}
	else if(event.keyCode == Keyboard.RIGHT)
	{
		rightKeyDown = false;
	}
	if(leftKeyDown)
	{
		direction = 180;
		speed = -5;
	}
	else if(rightKeyDown)
	{
		direction = 0;
		speed = 5;
	}
	else
	{
		speed = 0;
	}
}

But this is just duplicating what’s going on in the key down handler. So I could extract the duplicated code into another method, but I’m starting to feel like I’m unnecessarily complicating the code for the sole reason of avoiding key polling. I don’t want to be that guy. There’s a beautiful simplicity in the polling version. My main concern is over performance, since the keyboard managers I’ve seen usually involve array lookups. But looking at Richard’s, it’s using byte arrays and liberal bitwise operators (almost to the point of obfuscation). So my initial guess is that’s not too inefficient. Even so, something about it doesn’t sit well with me.

What seems to make sense to me is to create a sort of custom keyboard handler for each game. I’ve done this in other games and it worked out pretty well. The thing about keyboard managers is they are generic and reusable, and thus have to be able to take note of, store, and retrieve the state of any possible key. So some type of an array or collection is always needed. But for your specific game, there probably at the most a half dozen keys you are really interested in. These can be stored as class properties with getters (or public properties if that’s not too taboo for you). These properties can also be named something logical to the game, such as moveLeft, moveRight, jump, shoot, etc. rather than Keyboard.LEFT, Keyboard.SPACE, etc. which makes for more readable code. This also abstracts away lower level stuff into whatever you are interested in. If you wanted to change your keyboard mappings you could do it right there, without changing your external code. Something like this:

package
{
	import flash.display.Stage;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;

	public class InputLayer
	{
		private var stage:Stage;
		public var movingLeft:Boolean = false;
		public var movingRight:Boolean = false;

		public function InputLayer(stage:Stage)
		{
			this.stage = stage;
			stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
			stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
		}

		protected function onKeyDown(event:KeyboardEvent):void
		{
			if(event.keyCode == Keyboard.LEFT)
			{
				movingLeft = true;
			}
			else if(event.keyCode == Keyboard.RIGHT)
			{
				movingRight = true;
			}
		}

		protected function onKeyUp(event:KeyboardEvent):void
		{
			if(event.keyCode == Keyboard.LEFT)
			{
				movingLeft = false;
			}
			else if(event.keyCode == Keyboard.RIGHT)
			{
				movingRight = false;
			}
		}
	}
}

And the final class implementing it:

package
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.ui.Keyboard;

	public class KeyboardGameCustom extends Sprite
	{
		private var inputLayer:InputLayer;

		// view
		private var character:Sprite;

		// model
		private var xpos:Number = 200;
		private var speed:Number = 0;
		private var direction:Number = 0;

		public function KeyboardGameCustom()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;

			inputLayer = new InputLayer(stage);

			character = new Sprite();
			character.graphics.lineStyle(0);
			character.graphics.drawCircle(0, 0, 10);
			character.graphics.lineTo(20, 0);
			character.x = xpos;
			character.y = 200;
			addChild(character);

			addEventListener(Event.ENTER_FRAME, gameLoop);
		}

		protected function gameLoop(event:Event):void
		{
			input();
			update();
			render();
		}

		protected function input():void
		{
			if(inputLayer.movingLeft)
			{
				direction = 180;
				speed = -5;
			}
			else if(inputLayer.movingRight)
			{
				direction = 0;
				speed = 5;
			}
			else
			{
				speed = 0;
			}
		}

		protected function update():void
		{
			xpos += speed;
		}

		protected function render():void
		{
			character.x = xpos;
			character.rotation = direction;
		}
	}
}

Furthermore, you could perform other basic logic on the input before setting the input layer properties. For example, shift plus left key might set a runLeft property to true. Not the greatest example, but the point being you don’t need a one to one mapping of keys to Boolean values like you do in a generic manager.

So, am I eating crow? Yes, I guess I am. I still think the event method I originally described would be best, but the complexity you’d need to introduce to make it robust enough to handle all the intricacies would soon outweigh the potential performance benefits. And I’m also saving a bit of face by maintaining my opinion that generic keyboard managers are not a good idea, and offering a bit of a better solution.

Let the discussion begin.

Send to Kindle

22 responses so far. Comments will be closed after post is one year old.

  • Iain says:

    Yummy crow :) I like your new approach much better, but swap movingRight for an x value between 0 and 1, and you can kill the if statement entirely:

    character.x += input.x * 5;
    character.y += input.y * 5;
    character.rotation = input.angle * (180 / Math.PI);

    I’m going to open source my gamepad class very soon so hopefully nobody will have to think about this stuff again!

  • Iain says:

    sorry x value should be between -1 and 1 to handle both directions.

  • kp says:

    Yeah, Iain, that’s essentially what I meant by saying the properties didn’t have to be key mapped Boolean properties.

    I used something very similar to this on an iPhone game but incorporated touch and accel. So rather than getting a raw touch location, I was able to read the touch as an angle offset from a specific point. Very useful.

  • Andreas says:

    Disappointing lack of crow to be eaten actually ;) You make a fair point, and i get it now, but i can’t help but feel that the polling overhead is something worth learning to live with. From my (admittedly simple) performance tests, you’ll need more than a thousand dictionary lookups per frame before you even come close to noticing any actual impact, and dictionary lookups can obviously be replaced for more optimized methods.

    I suppose i want to separate input management from directly altering the model for the sake of order if nothing else, but also for the sake of flexibility. One thing is directional input, where setting properties on a velocity vector on keydown/up is totally doable, but storing things like input buffers for fighting game type applications practically demands a manager of some sort. Esoteric example, but can you handle the Konami code with a purely event based system?

    I suppose it feels as though this event based approach would be something you’d implement as an optimization at the far end of development if you desperately needed those cycles, rather than something you base your entire workflow on.

    Overall, for me at least, a keyboard manager with a fair suite of methods, such as getting a unit vector for the arrow keys and an input buffer of a specified range, as well as aliases for common button functions (jumpButton, fireButton, actionButton, whatever), simply feels more natural to work with during game development. If at any point during the game loop i want to see if conditions for an action are met, i’m free to do so. With your event-driven approach, i get the feeling i’ll constantly be rewriting similar code to solve slightly dissimilar problems.

    So, er, a generic manager i feel is good for general development; a more specialized performance-focused implementation if that level of optimization is needed.

  • [...] Peters put up a comprehensive blog post on why he disagrees with generic keyboard managers. It’s well worth reading. I have to admit, i couldn’t even conceive of input events [...]

  • Al says:

    I’ve always used something like this, which, when both keys are pressed results in no input:

    function gameLoop(event:Event):void {
    var rotationInput:Number = Number(movingRight) – Number(movingLeft);
    trace(rotationInput);
    }

    Perhaps a bit dirty with the casting (not required in AS2).

  • Nice post – I’ve always tended towards an approach like you developed with your InputLayer for any kind of action game where you are expecting simultaneous, and possibly conflicting, input. For simpler stuff, like puzzle or word games, I stick with events – it’s way cleaner and easier to disable keyboard input without tampering with any game loops.

  • Richard Lord says:

    An independent input layer is definitely the way to go. It’s not just easy for the developer to change the key inputs, but also easier to allow the user to do so.

    However, I would still use the KeyPoll class in my input layer -

    public function get moveLeft():Boolean {
    return keyPoll.isDown(Keyboard.LEFT);
    }

    public function get moveRight():Boolean {
    return keyPoll.isDown(Keyboard.RIGHT);
    }

    It’s just a stylistic preference – the interface to the input layer class is the same so the principle doesn’t change at all.

  • Devon O. says:

    Funny – I thought the most controversial part of the presentation was suggesting that model and view should be one and the same for Flash games (at least when dealing with Sprites/MovieClips). Maybe another post…
    Personally speaking, I find polling to be more responsive and use it for more time critical games and save event listening for the less “gotta move now to get outta the way” moments. I wonder though, if you go for the InputLayer approach (as would probably be best), is it really more CPU efficient? Either way, you’re testing for a Boolean during the game loop (inputLayer.movingRight OR keyPoll.isDown(Keyboard.RIGHT)).

  • Owen says:

    My keyboard class uses a third approach, in which you register each key and give them callback methods to run when the key is first pressed, when it is held down (the class can be set to handle its own timing or use the game loop) and when it is released. Keys can be grouped in order to allow mutually exclusive inputs, eg left and right. Keys can be disabled individually, by groups or all at once and re-registered easily at runtime. Of course it still uses the keyboard events to detect when keys are pressed and released, but there’s really no way round that!

    I kind of made it as an experiment and it seems to work pretty nicely, but if you can think of any problems with this approach I’d love to hear them :)

  • Keith Peters says:

    Devon, yeah, I thought the view/model combo would be more controversial as well, but everyone I talked to was glad I brought it up as a possibility.

    As far as the polling vs. events stuff, I knew I was on shaky ground there, but felt it was worth bringing up. If I had had more time to spend on it, I’m sure I would have presented what I did in this post. Not even sure why I went down that path, as I’m always one to fight back against premature optimization. We all have our fixed ideas I guess.

    But overall, I’m glad it worked out as it did. I have more certainty, a clean architecture, and lots of agreement now. :)

  • Keith Peters says:

    Also, if you didn’t see the presentation, just the slides, they may be a bit misleading.

    I’m not out and out advocating collapsing the view and model in games. Just pointing out that it might not be such a sin in many cases. Flash’s rendering system is quite different from something like OpenGL or DirectX. If you are using Sprites or Movie Clips, you just put some content in them and position them and leave them there, move them as needed, etc. So in this case you can wind up with a hierarchy of model objects that has to remain in sync with a nearly identical hierarchy of display objects. Adding new objects or removing them from the model creates complexity in updating the display side of things, and if you are using any kind of display object or bitmap hit testing, then the model has to know about the view, anyway. Thus, having the Sprites and MCs BE the model objects can significantly simplify your architecture. It was mentioned as an idea to consider and not to shy away from it because it’s “wrong” to do that.

  • Itai says:

    Why not combine the best of both worlds? you could use the KEY_DOWN event and KEY_UP events to create a count — if that count is greater than 0, run the poll; otherwise, ignore it completely. This way, you don’t need to create a custom keyboard handler for each game like you mentioned above… something like this:

    public class KeyboardGameNoPoll extends Sprite
    {
    // view&model code hidden
    keyPoll = new KeyPoll(stage);
    addEventListener(Event.ENTER_FRAME, gameLoop);
    stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
    stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
    }

    protected function onKeyDown(event:KeyboardEvent):void
    {
    keyCount++;
    }

    protected function onKeyUp(event:KeyboardEvent):void
    {
    keyCount–;
    }

    protected function gameLoop(event:Event):void
    {
    if (keyCount>0) input();
    update();
    render();
    }

    protected function input():void
    {
    //calculate speed for each direction, and combine to get
    //sum of directional speed
    }

  • stevenvh says:

    Wouldn’t it be an option to combine polling and events?
    As soon as the user hits the keyboard you catch the event and set the key as pressed. Your controller can catch the same event (or one dispatched by the keyboardmanager). There you can poll what keys are down and update the model/view.

    1. User presses right key and holds it.
    ==> Character starts moving right.
    2. User presses left key and holds it, while still holding right key.
    ==> Left and right are down so character stops.
    3. User releases either key, while still holding the other.
    ==> Character will continue the opposite way

  • Grimnick says:

    I usually use something like this:

    if(int(movingLeft) ^ int(movingRight)){
    if(movingLeft) {
    // move left
    } else {
    // move right
    }
    }

    Instead of your suggestion:

    if(movingLeft) {
    // move left
    } else if(movingRight) {
    // move right
    } else {
    // dont move
    }

    I don’t know, maybe your approach has better performance because of my int-casts… I just think it’s cleaner that way :)

  • Grimnick says:

    By the way, in your example the left-button has priority over the right-button.
    If you hold down both buttons the player will walk left and skip through the rest of your if-statement.
    This is not an issue in my approach, if you hold down both buttons the player wont move at all (which seems pretty logical to me).

    Oh and sorry for the double-post!

  • ben says:

    have you ever programmed games before? it doesn’t look like it. in my experience, polling and events are generally used for different things, becase they each have strengths and weaknesses and neither implementation can solve all possible problems/uses for detecting input. for example in an rpg game i’m making with a friend (in java btw, so sorry, no flash/actionscript here, but the idea is the same) and we use both polling and events for detecting keystate and mousestate changes. without going into to much detail, in java the only way to get input it by events, so we use keylisteners for all keyboard input. then for example toggle buttons, like in our editor, pressing the tilde key toggles rendering the entire map or the current layer we are on. if we used polling here, the toggle button would (and it does, ive tried it) switch back and forth on every call to update(), resulting in flickering as the map changes between rendering modes, and never knowing what state you end up in, as being able to watch and release your finger on the exact 1/60th second interval is rather hard, whereas the key events wait a certain time before repeating the keystroke.
    on the contrary, for moving the map, and by extension, moving ANY player controlled object in ANY game, using events is a poor choice because as we all know (using my rpg as an example, we will assume we are controlled the movement of the characte), if i pushed the up button, the character would jump up the specified player move distance in one frame, which is rather unimpressive and unimmersive. if using an animation between squares, then we are limited to moving to discrete tile square, such as in pokemon and many other games. however, using polling, and checking on every frame which key is pressed down, means we can move the character a little each frame, thus resulting in smooth motion, and all the while we can still play our walking animation if we are so inclined.
    so as you can see, polling and events are used for 2 similar but different things.

    also, the code you posted here (shown below, sorry i dunno how to use code tags here) would never happen, even if you used polling and events simultaneously, like we do in our rpg and other programs. no single key (or any other input event for that matter) should simultaneously use events and be polled. it just makes no sense. even if you needed to use polling and events for the same key, it wouldn’t be in the same part of the program, and so you would write the polling and events parts seperately and not have to worry about clashes.

    protected function onKeyUp(event:KeyboardEvent):void
    {
    if(event.keyCode == Keyboard.LEFT)
    {
    leftKeyDown = false;
    }
    else if(event.keyCode == Keyboard.RIGHT)
    {
    rightKeyDown = false;
    }
    if(leftKeyDown)
    {
    direction = 180;
    speed = -5;
    }
    else if(rightKeyDown)
    {
    direction = 0;
    speed = 5;
    }
    else
    {
    speed = 0;
    }
    }

    nonetheless i program mainly in java and so my comments may not apply to flash/actionscript but looking at your code it is very similar to both the syntax and OO paradigm of java.

  • [...] I ran into this issue I couldn’t find any information about it, but is partly why Keith is eating crow ;) However in my simple implementation it was easy enough to removeEventListener on KEY_DOWN and [...]

  • zzzzBov says:

    Your InputLayer class performs the same functionality as a polling class (“a rose by any other name…”). Your post is a good argument for using a polling class. The idea is to make an easily reusable class once, and then use it all over the place.

    You cited performance as an issue, but a good input-polling class wont be the issue. It’s better to simplify your UPDATE and RENDER methods, as they’re going to have a heavier processing load.

  • Joe Hocking says:

    No offense man, but this really smacks of premature optimization. I do like your approach best, because I agree that a keyboard manager class is an unnecessary layer of abstraction on top of the keyboard events Flash already provides. However, if the keyboard manager class is easiest for you then just use that and don’t stress out about it’s performance impact.

  • Louis says:

    To summarize, for performance you’d prefer your initial idea, but for general development you’re okay with polling but prefer a more domain specific interface to the keyboard input management. Right?