How to Add Multiplayer to An Existing Game In GameMaker


How to Add Multiplayer to An Existing Game In GameMaker

So you’ve made a game in GameMaker, and you want to take it online?

We recommend reading our Create a New Multiplayer Game tutorial first, which gets you started with a super basic game.

After that, follow this tutorial to convert your existing game into multiplayer, and fix common issues as they arise.

Download the 2022.600+ beta version of GameMaker to use multiplayer.

Contents

Steps

  1. How to Start Multiplayer
    1. How to Define Inputs (And Player)
    2. How to Set Player Locations
  2. How to Read Input in Multiplayer
  3. How to Test It Online

Fixes

  1. How to Fix ‘Managed Object’ Error
  2. How to Use Cameras in Multiplayer
  3. How to Interact with Game Objects
  4. How to Optimise a Multiplayer Game

Step 1: How to Start A Multiplayer Game

Our universe started with a big bang. Everything that we know, and everything we are, started from that point.

A multiplayer game in GameMaker also starts with a big bang, which allows players to join and the gameplay to begin.

undefined

It either hosts a new game, or joins the game you were invited to. Once everyone has joined, the game begins.

From this point on, the way your game works changes, which is detailed throughout this tutorial.

The beginning of everything

When your game’s big bang happens, the player gets a link for inviting their friends:

undefined

You would usually place the big bang code in the Create event of a “game manager” object, which exists in every level room:

if (!rollback_game_running && room == rm_level_1)
{
        var _join = rollback_join_game();

        if (!_join) rollback_create_game(2);
}

undefined

❔ What does this do?

This checks if the multiplayer game is running, and if we’re in the first level.

If both conditions are true, it either joins a game, or creates one, with two required players.

You can change this up to four.

If you don’t have a game manager object, just create a new blank object and place it in your first level.

If your game has a menu, multiplayer only begins when the user clicks “Play” and loads a level.

You can put this code in a different place to experiment with a different flow.

How to Check If I Was Invited

In our example, you start the game, hit “Play”, and then the multiplayer game starts.

This is fine for the host, but you want every joining player to jump straight into the game, without having to hit “Play”.

undefined

Let’s skip the menu entirely for every player that joins:

  • When the menu loads, ask: “Was I invited?
  • If the answer is “Yes, jump into the first level.

Add this to the Create event of your “menu manager” object:

If you don’t have a menu manager object, just create a new blank object and place it in your menu room.

if (rollback_join_game(true))
{
        room_goto(rm_level_1);
}

undefined

❔ What does this do?

This runs rollback_join_game() in “dry run” mode, which only checks if the player was invited, without actually joining the game.

If the player was invited, it takes them to the first level.

Step 1.1: How to Define Inputs in Multiplayer Games In GameMaker

This isn’t a major step, but part of the set-up, hence the decimal.

You need to define at least two things for multiplayer:

  1. The player object
  2. Inputs
    1. You don’t need to set your own inputs, but the default multiplayer inputs most likely won’t cover everything your game needs.

You need to define your player and inputs before the big bang. The perfect place for that is a script, which runs as soon as your game opens.

Create a new script, and name it Rollback (or anything you like). Remove the default function, and add this:

rollback_define_player(obj_player, “Instances”);

rollback_define_input({
        left : vk_left, // use ord(“A”) for letter keys
        right : vk_right,
        down : vk_down,
        up : vk_up,
        jump : vk_space
});

undefined

If your player object is named differently, change the object in rollback_define_player(). You can change the layer where the players will be created.

You may also change the list of inputs in rollback_define_input() to fit your game, and assign multiple inputs using arrays. Keyboard and mouse are currently supported.

Now remove any player instances present in your game room, as GameMaker will automatically create your players.

Run the game, and it’ll start in offline test mode, with all players spawning immediately. We’ll take it online soon.

Step 1.2: How to Set Player Locations In GameMaker

GameMaker will, by default, create your players in the top-left corner of your room.

undefined

To fix this, you can either:

  1. Hardcode the position for each player
  2. Use spawner objects

Solution 1: Hardcode Positions

In the Create event of your player object, set a position based on its ID:

switch (player_id)
{
        case 0:
                x = 800;
                y = 1000;
                break;
        
        case 1:
                x = 1100;
                y = 1000;
                break;
}

undefined

This is the simplest way, but it’s bad practice.

You’re hardcoding the positions, and when looking at the code, you can’t tell exactly where the players will spawn.

Solution 2: Create Spawners

Create a spawner object. Give it a “player_id” integer variable:

undefined

Disable the “Visible” option so you don’t see spawners in-game.

Place them in your level. Give each spawner a separate ID, starting at 0.

undefined

I’ve created two spawners, with IDs 0 and 1.

In your player object, add the Rollback Start event, which is found under “Other”.

Find the spawner with the matching ID, and teleport the player there:

with (obj_spawner)
{
        if (player_id == other.player_id)
        {
                other.x = x;
                other.y = y;
                break;
        }
}

undefined

What does this do?

This block of code runs for all spawners.

It checks if the spawner’s ID is the same as the player’s ID.

In that case, it sets the player’s position to the spawner’s position.

It then breaks the loop, so other spawners aren’t checked: we’ve found the one.

That’s it! Your player will find its matching spawner and go to it.

What a love story. You could even say it’s better than a certain vampire movie.

Step 2: How to Read Input in Multiplayer

At this stage, you’re likely able to control all players at once.

Why Is This Happening?

In a single-player game, you read input directly from the keyboard, using either a function or an event.

In multiplayer, GameMaker handles inputs for each player automatically. All you have to do is get the input for the correct player.

For example, if you are Player 0, that player will be controlled by your keyboard. Player 1 will be controlled by input from Player 1’s computer, probably in a land far, far away.

What Inputs Can I Read?

Earlier on this adventure, we defined the following inputs:

rollback_define_input({
        left : vk_left,
        right : vk_right,
        down : vk_down,
        up : vk_up,
        jump : vk_space
});

undefined

The inputs you define here are the ones you can read throughout your game.

Of course, your inputs may be different, so what you read will also be different.

How Can I Read Input?

In the Step event of your player, get the inputs, and check them using conditions:

var _input = rollback_get_input();

if (_input.left)
{
        // Move left
}
if (_input.right)
{
        // Move right
}
// etc.

undefined

You can add up, down, jump, attack, or whatever inputs your game needs.

How Do I Use This?

Scrap your old input system, and transfer controls to this new one.

If your input system uses conditions like above, you can just swap out the conditions.

Or, you may be using Key events, like in my game:

undefined

In this case, you need to move the code from these events, into the conditional blocks previously shown.

For example, the input code for my platformer looks like this:

if (_input.left)
{
        move_x = -move_speed;
}
if (_input.right)
{
        move_x = move_speed;
}
if (_input.jump_pressed)
{
        if (grounded)
        {
                move_y = -jump_speed;
        }
}

undefined

✨ Here’s something new: in the GML Code above, jump is written as jump_pressed.

That’s because each input automatically gets a _pressed and _released variant, and I just wanted to check if jump was pressed, not held.

In GML Visual, we use the “If Key Pressed (Rollback)” action instead.

Make sure to remove your old input code.

You can now control the first player! The second one gets random input while testing offline, which you can disable.

Step 3: How to Test An Online Multiplayer Game

Time to test the game online. There will be bugs, and crashes, and we’ll fix them as we go.

Publishing your game on GX.games is covered in the GX.games publishing tutorial.

First, go to GX.games and sign in.

undefined

Go into your profile settings and change the “Default region” to your closest region. This region will be used for your multiplayer games, reducing lag.

Go to your big bang event, where you call rollback_create_game().

Set the second argument to false. (If you’re using GML Visual, disable Sync Test.)

undefined
GML Code


undefined
GML Visual: Disable “Sync test”

Select the Opera GX target and run the game.

undefined

Start your game, so the multiplayer part begins.

Scroll down below the game window, and click on “Copy Share Url”.

undefined

Open this link in a second Opera GX window.

Make sure both windows are visible at the same time.

undefined

Start the game from the second window. Both players will connect online, and the game will begin!

…or, it’ll crash.

Fix 1: How to Fix ‘Managed Object’ Error In GX.games

Either immediately or after some time, you may get the following error:

undefined

This means you can’t modify a managed object before all players have joined.

What is a managed object?

All objects are "managed" by default. A managed object is part of your game loop and affects players.

undefined

You should disable this for objects that are only visual or auditory, but not for objects that are integral to your gameplay.

So, you can’t change any managed objects until all players have joined.

Until that happens, all managed objects should “sleep”.

Going back to our big bang example, it would look something like this:

undefined

How To Tell If Players Have Joined

When all players have joined, the Rollback Start event will be triggered in all objects. GameMaker will also set the rollback_game_running variable to true.

So, using the Rollback Start event, or the rollback_game_running variable, you can tell if the game has started.

Until that has happened, you want your managed objects to sleep, or technically, to deactivate.

How to Deactivate Objects

Luckily, GameMaker allows you to deactivate instances, and activate them when needed.

Go to your multiplayer big bang code, and before running the “join” or “create” function, deactivate all instances:

undefined
GML Code


undefined
GML Visual

This deactivates all instances except the one running the code.

❔If you need an object to remain activated, use instance_activate_object(<your_object>) to re-activate it immediately after deactivating all objects.

How to Re-Activate Objects

We want to re-activate everything once the multiplayer game has started.

Add the Rollback Start event into the same object, and activate all instances:

instance_activate_all();

undefined

Run the game in online mode.

All your objects will sleep until you join with the remaining players. This prevents your game from modifying its state before the game has begun.

How to manually freeze parts of your code

If the above method is not suitable for your game, you need to manually freeze parts of your code until the game begins.

Create

If you’re setting things off in a Create event, like creating instances and triggering alarms, move them over to the Rollback Start event instead.

undefined

This doesn’t mean you need to completely scrap the Create event – you can keep it for initialising variables.

Other Events

You might have Step, Draw or other events that may run at any time, but you don’t want them to run until all players have joined.

At the top of such an event, add this:

if (!rollback_game_running) exit;

undefined

This will exit the event if the game hasn’t started yet.

At this point, your crashes are hopefully fixed. Let’s fix other issues now.

Fix 2: How to Use Cameras in Multiplayer

👇 Skip to Fix 3: Interactions if your game doesn’t use a camera.

When you run your game in online mode, all games will be centred on the same player, no matter which one you’re controlling:

undefined

The camera just doesn’t know which player is local, so it follows the first player by default.

Thankfully, the fix is simple.

How to Follow the Local Player

In your player’s Create event, add this:

if (player_local)
{
        camera_set_view_target(view_camera[0], id);
}

undefined

Run the game, and your camera will now follow the local player. This means each window centres on a different player:

undefined

❕The player_local variable tells if the player is local. You can use it in the Draw event to draw an icon above the local player only, to make it easier for the player to find where they are.

Fix 3: How to Interact With Game Objects

Interacting with game objects becomes less simple in multiplayer.

In a single-player game, there’s only one player, so “where is the player” is a simple question.

You can’t ask the same in multiplayer. If you do, only one player will be able to interact with other objects.

Let Me Tell You A Story

My game has a coin block. That block looks for obj_player every step, and reads variables from it:

if (place_meeting(x, y + 1, obj_player))
{
        if (obj_player.yprevious > obj_player.y)
        {

undefined

In multiplayer, this results in only one player being able to hit the block.

The first condition isn’t the problem: it’s the second one, which reads variables from obj_player directly: ‘obj_player.yprevious’.

This is the problem, as it means the block may collide with one player, and then ask for information from a different player.

How to Detect The Correct Player

Instead of assuming there’s just one player, the code should only ask for information from the player it collides with.

Here’s the fixed code:

var _inst = instance_place(x, y + 1, obj_player); // Find the colliding player

if (_inst != noone) // If a colliding player was found,
{
        if (_inst.yprevious > _inst.y) // Read data only from that player
        {

undefined

This allows any player to hit the block, as it looks for “a player”, and then reads info from that player instance.

You’ll likely find similar issues throughout your project. Find a similar solution using the “a player” concept to fix them.

Fix 4: How to Optimise a Multiplayer Game In GameMaker

The “state” of your game comprises all your managed objects.

The more managed objects you have, the more there is for GameMaker to keep track of, which may result in worse performance.

To improve network performance, disable “Managed” for objects that don’t need it.

undefined

Only those objects should be managed which interact with players or other gameplay elements, or have any code at all that affects the game’s outcome.

Objects that only appear as visuals, particles, or that never move or change, can be unmanaged.

GameMaker does not automatically synchronise the state among all players, it only synchronises input.

I recommend reading Rollback System in the GameMaker manual to further understand this.

Tip 1: Player Instances

GameMaker handles the creation and destruction of your players automatically, so make sure to not destroy player instances at any time during the game.

If you need to show a player as defeated, change its sprite, make it invisible, or use some other technique (like state machines) -- but don't destroy it.

Conclusion

Congratulations, your game is now multiplayer! There is much more you can do – find out in the manual.

Publish your game to GX.games for millions of potential players.

If you’re facing issues, experiment. Try different things to narrow down your problem.

When in doubt, just create a new multiplayer game. Add gameplay mechanics to it. There’s no better way to learn than practice.

Happy GameMaking!