« Back to Blog

Platformer Series: 6 Improved Touch Controls

posted on 3 April 2021

In the previous tutorial, we mentioned Leos Fortune (Here’s a recap of the Leos Fortune touch indicator). The controls we created are similar to this, but the really nice thing about Leos Fortune is this dynamic centre point. You move left of that initial centre point and you go left, you move right you go right.

It still makes sense that the left-hand side of the screen controls movement, but if we borrow heavily from the Leos Fortune mechanic then we’ll have a much nicer experience.

Let’s make the left and right movement based upon the initial spot where you place your thumb (within the left half of the screen) and let’s add a nice visual indicator AND also some different speeds depending on how far you move your thumb.

The end result will look like this:

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

If you are viewing 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.

First up let’s add some graphics for our touch slider.


this.load.image('touch-slider', 'assets/touch-slider.png');
this.load.image('touch-knob', 'assets/touch-knob.png');

We have a rectangular slider, and a knob to show the direction. We create a new function to build this:


function buildTouchSlider(game) {
  sliderBar = game.add.sprite(0, 0, 'touch-slider');
  sliderKnob = game.add.sprite(0, 0, 'touch-knob');

  touchSlider = game.add.container(100, 450);
  touchSlider.add(sliderBar);
  touchSlider.add(sliderKnob);
  touchSlider.alpha = 0;
}

We create our two sprites, and add them to a container called touchSlider. We also make it transparent so it’s invisible for now.

In our create function we call this new buildTouchSlider function. We pass in this so we’re keeping the phaser scene scope and we can call things like add sprite.

buildTouchSlider(this);

We need some new global variables for the slider indicator graphics (sliderBar and sliderknob). We’re going to add some variable speeds for touch, so we’ll need some extra variables for that too. This is going to work in a very similar way to the jumping. A flag to say whether or not we are moving (touchMoving), a variable to store the original x position (startX), a threshold to make sure we have moved our thumb enough to trigger the action (touchMoveThreshold) and an offset for our slider graphic so it doesn’t get hidden behind the users the thumb, but rather appears above it (thumbSizeOffset):

touchSlider,
sliderBar,
sliderKnob,
touchMoving = false,
touchMoveThreshold = 3,
largeThumbMoveAcross = 25,
thumbSizeOffset = 60,
startX;

I’ve chosen a thumbSizeOffset of 60. A touch area of at least 40px is always recommended, but this can be tweaked for people with larger or smaller thumb if you like.

Now we’ll change our touch movement code in the update function. We can keep the code that gets the correct pointer, then instead of just working out if we are left or right of the centre point of the left half of the screen we now want to do a couple of things:

  1. set a new dynamic centre point which is where we first touched our thumb. If we go left of this, we go left, if we go right of this we go right.
  2. display the slider indicator above our thumb so it’s easy to see

We only want to see this centre pointer the first time we touch the screen i.e. we don’t want it to keep moving. It gets reset when we lift our thumb up and put it back down again. We need a way to keep track of this, and as we are using a visual indicator in the touch slider, we can just use that:


 if (myMovePointer) {
    //if touchSlide is not already showing then
    if (!touchSlider.alpha) {
        //make it visible
        touchSlider.alpha = 1;
        //position touchSlider to be where the users thumb or finger is
        touchSlider.x = myMovePointer.x;
        //with the Y pos we add a thumbSizeOffset so it's above the users thumb not hidden under it
        touchSlider.y = myMovePointer.y - thumbSizeOffset;
        //set our start point and reset slider display
        startX = myMovePointer.x;
        sliderKnob.x = 0;
    }
    ...

If our touch slider is hidden (its alpha is 0) then we know it’s not showing and that we don’t already have a centre point. When we touch our thumb, if the touch slider is currently hidden then we show it and set our new centre point. We also reset the position of the slider knob so it’s in the centre.

We then check if the movement is to the left or right. Left would be less than startX and right would be greater than. The basic idea is this:


 //if thumb has moved left or right of where we started then move
 if (myMovePointer.x < startX || myMovePointer.x > startX) {
    //work out how far thumb has moved. Is this a big enough movement?
    var movement = 0;
    //left movement
    if (myMovePointer.x < startX) movement = startX - myMovePointer.x; 
    //right movement 
    if (myMovePointer.x > startX) movement = myMovePointer.x - startX;
    //If move is significant enough then move our character
    if (movement > touchMoveThreshold) {
        //set flag as we are definitely moving
        touchMoving = true;

        /* 
        1. work out if we are moving a little bit or a lot
        2. animate the slider to show this
        3. move the character 
        */
    }
    //If move is really, really small then we don't count it. Stop moving
    else {
        //set moving flag to false
        touchMoving = false;
        //animate the slider back to center
    }
}

Just like jumping we are checking previous positions and working out whether the user has moved enough in either direction to register a move. If this movement is smaller than our touchMoveThreshold then we ignore it. We have chosen the value 3 for this but you could easily change it to make it more or less sensitive.

If the movement passes our threshold we will allow a small movement at a slow speed like this:


//set slider knob position to be half way to edge
var sliderPos = 0
//left
if (myMovePointer.x < startX) sliderPos = -(sliderBar.width / 4); 
//right 
if (myMovePointer.x > startX) sliderPos = (sliderBar.width / 4);

//set acceleration to be an 8th of normal
var tmpAcceleration = acceleration / 8;

This will set the slider knob in our touch graphic to 1/4 of the way across left or right by altering the sliderPos variable. This will be a nice visual indication that the player is moving slowly. We set the acceleration to be 1/8th of what it normally so that physics wise our player moves slower than normal. We haven’t actually animated this change to the slider yet or applied the acceleration to our hero, we are just setting vars which will be used shortly to make these changes.

Before those changes to the visuals and our characters speed are made, we check if we should be showing the character moving at full speed instead. We’ve got another threshold here which we’ve set to 25 for now and is stored in the ‘largeThumbMoveAcross’ variable. Any movement left or right of 25 or over we will consider to be full speed, and we’ll make the slider knob animate to either edge of the slider:


//if thumb has moved quite a lot, then go faster
if (movement > largeThumbMoveAcross) {
    //the knob position should be at the edge as we're at full tilt
    if (myMovePointer.x < startX) sliderPos = -(sliderBar.width / 2); 
    if (myMovePointer.x > startX) sliderPos = (sliderBar.width / 2);
    //acceleration is normal
    tmpAcceleration = acceleration;
}

At this point, we have our stored variables for character acceleration (tmpAcceleration) and where the slider knob graphic position should be (sliderPos). First we apply the visual change to the slider knob using a tween so it animates smoothly. We’re using a power1 ease and it will take 300ms. All these things can be tweaked to your personal preference.


//tween slider knob to the position we just worked out
var tween = this.tweens.add({
    targets: sliderKnob,
    x: sliderPos,
    ease: 'Power1',
    duration: 300
});

We then call our move (left or right) function, but this time we are passing in the acceleration value. This allows our character to move at different speeds. We’re passing in our tmpAcceleration we just worked out:


if (myMovePointer.x < startX) moveLeft(tmpAcceleration); if (myMovePointer.x > startX) moveRight(tmpAcceleration);

The moveLeft and moveRight functions have been tweaked to accept the acceleration as a parameter:


function moveLeft(acceleration) {
...
}

function moveRight(acceleration) {
...
}

If neither touch pointer is down we reset all our touch movement variables, including making the touchSlider invisible again.


	//if either touch pointer is down. Two thumbs, two pointers
 	if (this.input.pointer1.isDown || this.input.pointer2.isDown) {
 		...
 	}
	else {
    touchSlider.alpha = 0;
    startX = 0;
    touchMoving = false;
  }

Finally, we have some code to decelerate the character. If they are close to no movement, we stop them dead to avoid any jankiness. This code was already firing if no keys were pressed, we just need to add our touchMoving flag to it so it also works if the user is not currently touching the screen:


//if not moving left or right via keys or touch device...
if (!cursors.right.isDown && !cursors.left.isDown && !touchMoving) {
...
}

Nice. Now we have some half-decent controls on all devices, we start adding more features and making the game more fun. There are some simple thing we can do to make the game more exciting, and we’ll look at that next in Animating Coins.

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