How to Add Multi-Touch and Virtual Joystick Controls


How to Add Multi-Touch and Virtual Joystick Controls

Touch controls are crucial for mobile games. This tutorial covers detecting simple touches, multi-touch support, and adding a virtual joystick.

Download the project containing all code shown in this tutorial.

Detecting Touch

There are a few common ways of detecting mouse input in GameMaker.

You can use the “Mouse” events in an object to detect clicks and hovers:

undefined

In GML Code, you can use the mouse_check_button() family of functions to detect clicks, and mouse_x / mouse_y to detect the position of the cursor.

All of these methods are compatible with touch screens.

For example, if your game already has a button system that works with a mouse, it’s very likely that it’ll work on a mobile display as well.

Simple Button

Create a button object, and add a Left Pressed event:

undefined

In this event, add code or actions for what you want the button to do.

It will respond to your touch when run on a mobile device.

On GUI

This button will work fine in a simple room, but it won’t work well if you’re using cameras.

Create buttons on the GUI layer to create buttons and menus that are overlaid on your game.

Multi-Touch

Before creating a virtual joystick, let’s look at multi-touch in GameMaker.

Each “touch” is detected as a “device”. You can read such devices from 0 to a maximum number using a loop.

How?

Create an object for managing multi-touches: obj_multitouch_manager. Place it in your room.

Also create an object that serves as a parent for all UI objects: obj_ui_parent.

undefined

In obj_multitouch_manager, add the Begin Step event, with the following contents:

var _max_devices = 4;

for (var i = 0; i < _max_devices; i++)
{
        var _touch_x = device_mouse_x_to_gui(i);
        var _touch_y = device_mouse_y_to_gui(i);
        
        var _ui_at_pos = instance_position(_touch_x, _touch_y, obj_ui_parent);
        var _held = device_mouse_check_button(i, mb_left);
        
        if (_ui_at_pos != noone && _held)
        {
                _ui_at_pos.input(i, _touch_x, _touch_y);
        }
}

undefined

Allowing a maximum of four touches, it loops through all devices and gets their touch coordinates.

It tries to find a UI instance at each touch’s position and checks if the touch is being held.

If a UI instance is found, and the touch is being held, it calls the input() method in the UI instance that was found.

The input() method is how we tell a UI instance that it’s being pressed. We pass in the ID and coordinates of the touch.

Read more: "Device Input" in the GameMaker manual

UI Object

Because our touch manager calls the input() method in a UI object, obj_ui_parent needs to have it initialised in its Create event:

input = function (_touch_id, _touch_x, _touch_y)
{
        
}

undefined

This function is currently empty, and will be implemented in a child object – such as a button or a joystick.

Multi-Touch Button

Earlier in the tutorial, we used the Left Pressed event to detect clicking on a button.

To create a button that uses your shiny new multi-touch system, do this:

  • Create a new object
  • Make it a child of obj_ui_parent
  • Define the input(_touch_id, _touch_x, _touch_y) function
  • Make that function do whatever you want your button to do!

Now, let’s add a joystick using our multi-touch manager.

Virtual Joystick

For the joystick, I’m using two sprites: one for the base, and one for the stick itself.

undefined

Create a new object: obj_joystick. Make this a child of obj_ui_parent.

Assign the spr_joystick_base sprite to it.

undefined

Add the Create event, and add this there:

event_inherited();

joy_x = 0;
joy_y = 0;
touch_id = -1;

radius = sprite_width / 2;

input = function (_touch_id, _touch_x, _touch_y)
{
        touch_id = _touch_id;
}

undefined

This first runs the Create event of the parent.

It then initialises joy_x and joy_y, which store the relative position of the joystick.

It creates touch_id, which is the ID of the touch that is using the joystick, and radius, which is the radius of the joystick derived from its sprite’s size.

It initialises the joystick’s input() function, which simply stores the ID of the touch in a variable.

Joystick Logic

Add the Begin Step event with the following contents:

if (touch_id != -1)
{
        if (device_mouse_check_button(touch_id, mb_left))
        {
                joy_x = device_mouse_x_to_gui(touch_id) - x;
                joy_y = device_mouse_y_to_gui(touch_id) - y;
        
                var _direction = point_direction(0, 0, joy_x, joy_y);
                var _distance = point_distance(0, 0, joy_x, joy_y);
        
                if (_distance > radius)
                {
                        joy_x = lengthdir_x(radius, _direction);
                        joy_y = lengthdir_y(radius, _direction);
                }
        }
        else
        {
                touch_id = -1;
                joy_x = 0;
                joy_y = 0;
        }
}

undefined

undefined

All of this code runs only if the touch ID is not -1, meaning the joystick was pressed.

If that touch is being held, we set joy_x and joy_y to the relative position from the joystick to the touch.

To ensure the joystick doesn’t go beyond its base, the code does the following:

  1. It gets the direction and distance to the joystick.
  2. If the distance is greater than the radius,
  1. It sets the joystick position so it caps at a distance of radius

The else block runs if the touch is not being held, in which case it resets the joystick and touch ID.

Drawing the Joystick

Add the Draw GUI event:

draw_self();

draw_sprite(spr_joystick_stick, 0, x + joy_x, y + joy_y);

undefined

This will draw the joystick base, and then the stick itself, using joy_x and joy_y.

Now, place your joystick in a room – you can even place multiple joysticks to test multi-touch:

In the video above, the game is running on Opera GX Mobile, with the system detecting all 4 touches, and each joystick operating separately.

Using the Joystick

To use the joy_x and joy_y values in your game, you can divide them by the radius to get normalised 0-1 values.

As an example, I’ll add this to the Step event of my player object:

var _move_x = 0;
var _move_y = 0;

with (obj_joystick)
{
        _move_x = joy_x / radius;
        _move_y = joy_y / radius;
}

x += _move_x * move_speed;
y += _move_y * move_speed;

undefined

move_speed needs to be initialised in the Create event. Mine is set to 4.

The player can now be moved using the joystick:

It also moves slower when you push the joystick just a bit!

Download the GML Code project to test it yourself.

More

Follow the GUI Buttons tutorial and incorporate it with this tutorial to add more multi-touch GUI to your game.

The Windy Woods template in GameMaker has multi-touch controls implemented, which appear if you run the game via the Opera GX target.

You can take a peek at its code and learn how it’s done.

Happy GameMaking!