2D Tile Based Movement: Key Priority (with Stack)

If you ever are in a situation like me where you are trying to develop your own 2D tile based movement system where the characters walk full tiles at a time (not pixel based movement) you will probably run into a problem that RpgLegend has faced. This tutorial will explain why it is a problem, and reveal my solution I have came up to address it!

Posted by on - Intermediate Client Side Coding

First, if you look at any tutorial series online to make your own 2D top-down game. You will find code similar to this:

``````function onFrame() {
if (Key.isDown(38)) {
//move up
player.moveUp();
}
if (Key.isDown(39)) {
//move right
player.moveRight();
}

if (Key.isDown(37)) {
//move left
player.moveLeft();
}
if (Key.isDown(40)) {
//move down
player.moveDown();
}
}``````

In this example, on each frame update the code checks to see if the arrow keys (represented by those numbers) are being held down. And if it any are, then it instructs the player object to move in the corresponding direction. This works, and gets movement in the game. But there are problems.

First, if you hold in Up, and then decide to hit any other key, Up will always get processed first. And with tile based movement, you won't process the next movement until the current one is complete. Which basically makes it very unresponsive when you are changing directions, or are trying to walk around a wall or corner.

How do we address this? The problem is, we shouldn't be checking the directions with a series of if statements. Instead, we need to think about how the human mind works and what our hands are actually doing.

So let's break it down briefly: as you approach an obstacle you are holding in, let's say the up key. When you hit the obstacle, you probably wouldn't let go of the up key, instead you would hit either the left or right key to slide around it. In the example above, this isn't possible. Since Up always takes precedent.

The solution is: our brains expect the last key we hit to work and the one we have held in to be ignored temporarily. Which means, we need to stop processing the Up key and let the Right key process then. Because the Right key is the last key that was hit. But when the Right key is let up, the Up key, if is still being pressed, should resume. This is it! But what does this look like in code? That sounds like a lot of if statements...

Well, from knowing some common data structures it does sound kind of like a Stack (last in, first out). Basically, imagine a stack of books. You place a book down, then others on top. You can't get to the bottom book, until you take the books off on top, starting from the top, the last book placed down. That is kind of what we are doing with the keys. We want to process the most recent key, and work our way down to the bottom (first key) pressed. And if the stack is empty, then no keys are pressed, and you stand still.

The only thing left was that we need to be able to remove keys from any part of the stack once the key is let go, even if it was an earlier key. And we also need to make sure we don't add the same key twice.

Here are the two functions that I created to modify the Stack to these requirements:

``````private var fStack:Array = [];

//rpglegend.com
if (fStack.length==0) {
fStack.push(dir);
} else {
var len:int = fStack.length;
for (var i=0; i<len; i++) {
if (fStack[i]== dir) return;
}
fStack.push(dir);
}
}
private function stackRemove(dir:int):void {
//rpglegend.com
var len:int = fStack.length;
for (var i=0; i<len; i++) {
if (fStack[i]== dir) {
fStack.splice(i);
return;
}
}
}``````

And with that this is how the key detection (similar to above) uses the modified Stack approach!

``````function onFrame() {
if (Key.isDown(38)) {
//move up
} else {
stackRemove(1);
}
if (Key.isDown(39)) {
//move right
} else {
stackRemove(2);
}

if (Key.isDown(37)) {
//move left
} else {
stackRemove(4);
}
if (Key.isDown(40)) {
//move down
} else {
stackRemove(3);
}

var fFace:int = 0;

//read the top most key to process
if (fStack.length>=0) {
fFace = fStack[fStack.length-1];
}

if (fFace==1) {
//move up
player.moveUp();
}
if (fFace==2) {
//move right
player.moveRight();
}

if (fFace==4) {
//move left
player.moveLeft();
}
if (fFace==3) {
//move down
player.moveDown();
}

}``````

In the end, this approach feels perfect. And I am pretty sure this is the strategy done by all your favorite console 2D, tile-based games!

Hope this helps any of you with the similar issue! And don't forget to please check out RpgLegend my Online RPG that is currently in development! Indiedb.com