« Back to Blog

Phaser Platformer Series: 5 Touch Controls

posted on 30 March 2021

Touch controls and mobile is a bit of a tricky one. Lot’s of people have tried different things. Here are some options:

I find on-screen buttons really clunky. They take up a lot of screen real estate and they don’t feel very good. Auto running works well, but it plays like a very different game. Likewise, auto jumping has the same issues.

That leaves us with something like Leos Fortune. Of all the touch platformers, these controls are miles ahead in terms of feel. Unfortunately, it’s probably the hardest one to implement! You use the left-hand side of the screen to move left and right, and the right hand click to touch to jump. Wherever you place your finger, it makes that the centre point, you move left of that, you go left. You move right, you go right. So the tricky bit with that is that it’s dynamic – you can put your finger anywhere to set this centre point.

Let’s start with a simplified version. We’re going to split the screen in two. Left-hand side is for horizontal movement and right-hand side is for jumping. Instead of a dynamic centre point, we’ll make our centre point the mid point of the first half of the screen i.e. the quarter mark. So if your thumb/finger is to the left of this point you go left, if it’s to the right you go right.

The end result will look like this:

See the Pen
Phaser Platformer: 5 Touch Controls
by Digitherium (@digitherium)
on CodePen.

If you are viewing it on a mobile, you can open a version of it here without all the codepen chrome taking up screen space so you play it in landscape mode.

Touch and mouse input in Phaser is handled by ‘pointers’. There are two pointers activated by default. One for the mouse (this.input.mousePointer), and one touch pointer (this.input.pointer1). If you want multitouch you need to add more pointers (You can have up to 10). We want two touch pointers to work at the same time (one for each hand), so we’ll need to add an extra one. There are a couple of ways of doing this. Firstly in our create function we could add this line:

this.input.addPointer(1);

The number in the brackets is the number of pointers to add. By default, there are two pointers, and we are adding one making three in total.

Alternatively, we can change the number of active pointers by adding an extra line to the config at the very top of our code, like this:


input: {
  activePointers: 3
},

Remember that the first pointer is for the mouse, so this number 3 will give us 2 touch pointers and 1 mouse pointer.

We can start listening for these inputs like this in the update function:

if (this.input.pointer1.isDown || this.input.pointer2.isDown) {

This checks for either pointer (pointer1, or pointer2) being down. Pointer1 is always going to be the first finger or thumb that touches the screen and pointer2 the second. With that in mind, you’d think pointer1 is always going to be our left-hand thumb and control horizontal movement, and that pointer2 is going to be our right-hand thumb controlling jumping. This will be the case most of the time due to the mechanics of our game, but it is completely possible that the right hand is the one that touches the screen first, and is therefore pointer1. Every time you take all fingers off the screen it resets too! To cut a long story short it’s not just as simple as pointer1 does movement and pointer2 does jumping, both pointers need to do both things.

Now that we have some pointers to listen out for, let’s logically split the screen in two – left-hand side for movement, right-hand side for jumping. We can work out this halfway point by getting the width value from our config:

//work out half way point of our game
var leftHalf = config.width / 2;

By doing this, if a pointer is on the left half of the screen we can assume it’s for horizontal movement and code accordingly.

//if thumb is on the left hand side of the screen we are dealing with horizontal movement
if (this.input.pointer1.x < leftHalf || this.input.pointer2.x < leftHalf) {

Once we’ve established we’re looking at horizontal movement we want to grab the pointer that is dealing with it. We create a variable called myMovePointer which we will set to avoid having to type out this huge either pointer1 or pointer2 conditional all the time. Each time I’ll set this back to null, so if a thumb is removed, we’re no longer going to move our character.

//reset pointer variable
var myMovePointer = null;
//here we get the pointer that is being used on the left hand side of screen. Depends which thumb they touched screen with first.
if (this.input.pointer1.x < leftHalf && this.input.pointer1.isDown) {
  myMovePointer = this.input.pointer1;
}
if (this.input.pointer2.x < leftHalf && this.input.pointer2.isDown) {
  myMovePointer = this.input.pointer2;
}

If we have a pointer that is currently down and it’s in the left half of the screen we then need to work out which side it sits in the left or right side of this half of the screen. If left, move player left, if right move player right.

//if we have an active touch pointer on the left hand side of the screen then...
if (myMovePointer) {
  //if thumb is in the left hand quarter of the screen then go left
  if (Math.floor(myMovePointer.x / (leftHalf / 2)) === 0) {
    moveLeft();
  }
  //If touch is to the right of the player move them right
  if (Math.floor(myMovePointer.x / (leftHalf / 2)) === 1) {
    moveRight();
  }
}

We’re still using keyboard controls, which aside from the different type of input are doing exactly the same thing. We don’t want the exact same bit of code in two places, so we can take our movement code and wrap it up in moveLeft and moveRight functions.

function moveLeft() {
  var standing = player.body.blocked.down || player.body.touching.down;
  //if hero is on ground then use full acceleration
  if (standing) {
    player.setAccelerationX(-acceleration);
  }
  //if hero is in the air then accelerate slower
  else {
    player.setAccelerationX(-acceleration / 3);
  }
}

function moveRight() {
  var standing = player.body.blocked.down || player.body.touching.down;
  //if hero is on ground then use full acceleration
  if (standing) {
    player.setAccelerationX(acceleration);
  }
  //if hero is in the air then accelerate slower
  else {
    player.setAccelerationX(acceleration / 3);
  }
}

In our old keyboard code we change it so it also calls these functions

if (cursors.left.isDown) {
  moveLeft();
}
//same deal but for right arrow
else if (cursors.right.isDown) {
  moveRight();
}

Now if we want to change the speed or way our character moves, regardless of whether it’s keyboard, touch or even something new we add later like a gamepad, we only have to change it in one place.

So that handles our players horizontal movement. To handle jumping we use some similar code to determine if a pointer is on the right hand half of the screen.

if (this.input.pointer1.x > leftHalf || this.input.pointer2.x > leftHalf) {

And again similar code to get the current pointer:

//reset pointer variable
var myJumpPointer = null;
//get active touch pointer for this side of the screen
if (this.input.pointer1.x > leftHalf && this.input.pointer1.isDown) {
  myJumpPointer = this.input.pointer1;
}
if (this.input.pointer2.x > leftHalf && this.input.pointer2.isDown) {
  myJumpPointer = this.input.pointer2;
}

To perform a jump, we want the user to swipe up. They should be able to jump really easy with just a tiny swipe up, but not so tiny that it feels buggy and jerky. So in order to achieve this we want to get the y position of when the user touched the screen, and then see if the current y position is higher up the screen. i.e. they have moved their thumb upwards. We need some more variables for this:

prevPos = 0,
yPos = 0,
touchJump = false,
touchJumpThreshold = 5;

prevPos will store old y value, yPos will be the current y value, touchJump is flag that we set if they should be jumping. Finally touchJumpThreshold is the minimum distance someone’s thumb must travel for us to register a jump.

//if we have a touch pointer on right hand side of screen...
if (myJumpPointer) {
  //store last y position of touch pointer
  prevPos = yPos;
  //get new position of touch pointer
  yPos = myJumpPointer.y;
}

When we have these two values, we can check if the new value is less than the old one. The y axis starts from 0 at the top, and numbers get bigger as you go down the screen. So if the user is swiping up, the new yPos value will be less than the old value. e.g your thumb was at a y pos of 20, if you brought it up, it might be at a y pos of 10. So to work out if a user is jumping we can just take the old position and subtract the new position from it. If this is a positive number we have moved up. We also want to avoid false jumps from tiny thumb movements, so we use our touchJumpThreshold which I have set to 5. So someone’s thumb would have had to have moved 5 at least five units up to register a jump. This could be tweaked to make it more or less sensitive.

//if we have moved our thump upwards then we set jump flag to true
if (prevPos - yPos > touchJumpThreshold) {
  touchJump = true;
}

Then we tweak our existing keyboard jump controls to include the touchJump flag in the check. So either the up key is pressed OR a jump swipe has just happened:

//if player is standing, or just fallen off a ledge (within our allowed grace time) and...
  //either up key is press, or touchjump flag is set AND they are not already jumping then jump!
  if ((standing || time <= edgeTimer) && (cursors.up.isDown || touchJump) && !jumping) {
      player.setVelocityY(jumpVelocity);
      jumping = true;
  }

Finally we reset our flags at the end of the update if the player is just standing:

//if not pressing up key...
if (!cursors.up.isDown) {
    //if player is touching ground / platform then reset jump parametrs
    if (player.body.touching.down) {
        jumping = false;
        touchJump = false;
        prevPos = 0;
    }
}

Phew. That’s it, you should have some basic touch controls. Next up we’re going to improve on these basic controls and make them less rigid in improved touch controls.

chromeless mobile version
view all the code on codepen
download the source on github.