Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Include Touch, Cursor, and Gesture Events in a Phaser Game

TwitterFacebookRedditLinkedInHacker News

So you’re at a point in your game where you need to allow the player to interact with the game. While keyboard events are common in a lot of games, they aren’t the only way to interact, and they may not even be the best way to interact if you’re planning on taking your game into a mobile setting.

An alternative to using keystrokes to interact with your game is to use pointer events. Events that involve a pointer could include the clicking or movement of a mouse, a tapping of a finger, or a swiping gesture event with either.

In this tutorial, we’re going to see how to interact with a Phaser 3.x game, not with a keyboard, but with your finger on the screen or with your mouse.

To get an idea of what we plan to accomplish, take a look at the following animated image:

Phaser Input Event Example

In the above example we have a simple sprite image. Clicking or tapping will result in the sprite animating up or down on the screen until it reaches wherever the input event occurred. We’re going to see a few options to accomplish these results, something that could be valuable depending on the game or publish platform.

Before starting this tutorial, you should have an image file that you plan to use. It doesn’t need to be extravagant, but it should be easily visible.

Creating a New Phaser 3.x Project for Development

The emphasis of this tutorial is around game input, so we’re not going to spend too much time on the why and how of Phaser 3.x for game development. Instead, create a new directory on your computer, and within that new directory add an index.html file with the following markup:

<!DOCTYPE html>
<html>
    <head>
        <script src="//cdn.jsdelivr.net/npm/phaser@3.24.1/dist/phaser.min.js"></script>
    </head>
    <body>
        <div id="game"></div>
        <script>

            const phaserConfig = {
                type: Phaser.AUTO,
                parent: "game",
                width: 1280,
                height: 720,
                backgroundColor: "#5DACD8",
                scene: {
                    init: initScene,
                    preload: preloadScene,
                    create: createScene,
                    update: updateScene
                }
            };

            const game = new Phaser.Game(phaserConfig);

            var isClicking = false;
            var swipeDirection;

            function initScene() { }

            function preloadScene() {
                this.load.image("plane", "plane.png");
            }

            function createScene() {

                plane = this.add.sprite(640, 360, "plane");
                plane.setScale(0.4);

            }

            function updateScene() { }

        </script>
    </body>
</html>

Remember, the expectation is that you have your own image file to represent our sprite.

In the above markup, we’ve initialized our Phaser 3.x game, loaded our image asset, and created a sprite from it. The sprite sits at the center of the screen, and in this example is scaled to 40% of the original size. If you’re using a smaller image file, you may not need to scale the sprite.

Beyond the basic boilerplate code, take a look at the following two lines:

var isClicking = false;
var swipeDirection;

Eventually we’re going to be tracking our clicks and the direction of our swipe events.

With the foundation of our project in place, now we can work towards moving our sprite on the screen. We’re going to explore a few options increasing in the level of difficulty.

Moving a Sprite to the Current Pointer Position on Click

The first thing we’ll explore is moving the sprite to exactly where the user has clicked or tapped without any kind of animation event. In other words, the sprite will immediately end up at the clicked position.

Within the updateScene function of our index.html file, add the following code:

function updateScene() {

    if(!this.input.activePointer.isDown && isClicking == true) {
        plane.y = this.input.activePointer.position.y;
        isClicking = false;
    } else if(this.input.activePointer.isDown && isClicking == false) {
        isClicking = true;
    }

}

There is no concept of a click or a tap in Phaser 3.x. We can track when the pointer is pressed and when it is up, but not when a click or double-click event occurs. However, we can track these kind of events manually with a little bit of code.

In the above code, we are first checking to make sure the pointer is not currently down and that we’re currently in the process of a click event. To make it more clear, the isClicking variable that we created was designed to track our clicks. If this value is true, it means the cursor is currently compressed. When the pointer is not down, we need to check if it was previously down, hence the boolean.

If we’re ending our click, we update our sprite y-axis position to the current pointer position and end the click.

If the pointer is down and we aren’t currently clicking, it means we have just started a click. Remember, the isDown property is always doing something, so if we aren’t tracking the click with a boolean variable, the pointer is going to always be down or always be up.

When we talk about activePointer, this refers to mouse cursor events as well as touch events.

Animating a Sprite to the Current Pointer Position on Click

Moving the sprite instantly is great, but it won’t give you a great gameplay experience. Instead, you probably want to animate the movement of your sprite to get to the pointer position over time.

Let’s update our updateScene function to the following:

function updateScene() {

    if(!this.input.activePointer.isDown && isClicking == true) {
        plane.setData("positionY", this.input.activePointer.position.y);
        isClicking = false;
    } else if(this.input.activePointer.isDown && isClicking == false) {
        isClicking = true;
    }

    if(Math.abs(plane.y - plane.getData("positionY")) <= 10) {
        plane.y = plane.getData("positionY");
    } else if(plane.y < plane.getData("positionY")) {
        plane.y += 5;
    } else if(plane.y > plane.getData("positionY")) {
        plane.y -= 5;
    }

}

In the above code, we are still using the click or tap logic that we defined in the previous section. However, instead of changing the position of the sprite then and there, we are setting the location that the sprite should be so that we can animate over time.

When it comes to sprites, you can set key-value pairs to the sprite. In this example, we are setting what the position should be on the sprite, which doesn’t hold any meaning until we do our checks like below:

if(Math.abs(plane.y - plane.getData("positionY")) <= 10) {
    plane.y = plane.getData("positionY");
} else if(plane.y < plane.getData("positionY")) {
    plane.y += 5;
} else if(plane.y > plane.getData("positionY")) {
    plane.y -= 5;
}

We’re doing three checks in the above code:

  • Checking to see if the current position of the sprite is within a threshold.
  • Checking to see if the sprite is less than where it should be.
  • Checking to see if the sprite is greater than where it should be.

We need to move the sprite until it reaches the set position defined by the click event. However, the movement amount may not be evenly divisible, so it may never reach the intended position. If it never reaches, then the sprite will bounce back and forth forever. To avoid this, we set a threshold. If the threshold is met, then we just force set the position of the sprite.

There’s nothing wrong with this method of handling player movement, but we’re going to look at another option.

Moving a Sprite with a Swipe Gesture Event

Rather than responding to clicks and taps, you might need to respond to swipes. I’ve done this in one of my games just to make it more challenging on mobile. The good news is that the strategy is similar to what we’ve already seen.

Within the updateScene function of the index.html file, let’s change it to the following:

function updateScene() {

    if(!this.input.activePointer.isDown && isClicking == true) {
        if(Math.abs(this.input.activePointer.upY - this.input.activePointer.downY) >= 50) {
            if(this.input.activePointer.upY < this.input.activePointer.downY) {
                swipeDirection = "up";
            } else if(this.input.activePointer.upY > this.input.activePointer.downY) {
                swipeDirection = "down";
            }
        }
        isClicking = false;
    } else if(this.input.activePointer.isDown && isClicking == false) {
        isClicking = true;
    }

    if(swipeDirection == "down" && plane.y < 500) {
        if(Math.abs(plane.y - 500) <= 10) {
            plane.y = 500;
        } else {
            plane.y += 8;
        }
    } else if(swipeDirection == "up" && plane.y > 150) {
        if(Math.abs(plane.y - 150) <= 10) {
            plane.y = 150;
        } else {
            plane.y -= 8;
        }
    }

}

So let’s break down what’s happening in the above code.

First we’re checking for those click events. However, in this example we’re changing it up to make it behave more like a drag event. While we are using isClicking, it might make sense to call it something like isSwiping instead.

When the click or swipe event has ended, we check to see if the distance between the starting position and the ending position is within a particular threshold. Notice that we’re using upY which represents the value when we stopped touching or pressing, and downY which is the value when we started touching or pressing.

If the distance is greater than our threshold, we check to see if we’re swiping up or down. This can be determined by simple subtraction math.

When we have the swipe direction, then we can start moving the sprite:

if(swipeDirection == "down" && plane.y < 500) {
    if(Math.abs(plane.y - 500) <= 10) {
        plane.y = 500;
    } else {
        plane.y += 8;
    }
} else if(swipeDirection == "up" && plane.y > 150) {
    if(Math.abs(plane.y - 150) <= 10) {
        plane.y = 150;
    } else {
        plane.y -= 8;
    }
}

Because we are working with directions rather than exact positions, we need to define where the sprite should end up. In this example, we defined a top and a bottom position. Since we don’t know that the movement value will for sure be divisible by the top and bottom values, we set a threshold once again. This means we’re either moving to the appropriate position based on the swipe, or we are firmly setting the position if we’re within the threshold.

Swiping could be a tricky subject to grasp, but it has its use-cases.

Conclusion

You just saw how to handle user input events in a Phaser 3.x game. While the input events could expand beyond the examples shown, we saw how to move on click, animate on click, and animate on swipe. These examples work regardless if you’re on a computer or a mobile device.

In this example the plane movement was animated, but not the plane itself. If you want to see how to animate the plane or any sprite, take a look at my tutorial titled, Animate a Compressed Sprite Atlas in a Phaser Game.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in C#, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Unity. Nic writes about his development experiences related to making web and mobile development easier to understand.