Multiplayer tutorial 4: real-time game

Favourite 83 favourites
Tutorial written by AshleyOriginally published on 19th, March 2014 - 4 revisions

Signalling group

This group has many similarities to the previous pong example. However there are some important differences in what is synced, since it's a different kind of game.

First of all, in On start of layout we must set up the objects and data that are going to be transmitted. The first actions set up the inputs that peers send to the host. To prevent cheating, the peers only send their inputs to the host, who then does the actual movement for the peers. Local input prediction covers up the latency from sending the inputs to the host and receiving the new position back. The inputs we add look like this:

The inputs that are sent to the host are called the client input values. For this game we have three: the aim position X and Y co-ordinates, which we call lookx and looky, and the button states the player is pressing, which we call inputs. The look X and Y are int16 values, since they don't need sub-pixel precision and the layout is far smaller than 32767, the maximum value that can be sent with this precision. inputs is an 8-bit value which we set individual bits for each control. This game has five controls: four directions and left mouse button to shoot with three more spare bits, so we don't need to use any higher precision. Later on in the tutorial we'll deal with updating these values so the correct information is sent to the host.

The look X and Y values use Linear interpolation. This means if updates come in infrequently, the multiplayer engine guesses the in-between values by moving them in a straight line between known values. This ensures movement of the look position moves smoothly. However it's very important that no interpolation be used for the inputs, so None is chosen for interpolation. There are no in-between states for these inputs and using interpolation for this value will simply cause incorrect results on the host.

The next part of the event sets up which objects and which of their instance variables are going to be synced. These are the relevant actions:
The first action uses the Multiplayer object's Sync object action. This is the key action to indicate which objects are to be sent over the network. Syncing an object is one-way: it means the host tells peers how many of those objects exist and where they are. Peers only receive this data and any changes that peers make to these objects will be ignored and overridden. The client input values are the only ways the peers have of affecting the game. The host has the authoritative version of the game, and the peers are doing their best to display what the host has got. The host is solely responsible for creating, moving and destroying these objects; as it does so, Sync object will cause objects to be created, moved and destroyed on the connected peers. All of this happens internally in the multiplayer engine as a consequence of this action.

It is important to save bandwidth by only syncing objects that absolutely have to be transmitted. In this case the Peer object represents a player and must be transmitted. It is usually redundant to sync things like terrain, scenery and props - it is a waste of bandwidth to sync them, especially if they never change. Even if they do change, if the results are unimportant to actual gameplay (e.g. cosmetic bullet holes or craters that don't affect collisions) then it's fine for local peers to handle that by themselves. There's also no need to sync the AimSpot, PeerLaser or PeerName objects in the container with the Peer: the container ensures they are created and destroyed at the same time, and later events will position them based on the Peer object only, so there's no need to send any extra data for them.

Sync object can update the object position or angle, or both, or neither. In this case we're only interested in the position, since the peer angle is always set towards the aim position. int16 precision is suitable for positions. (Linear interpolation is automatically used here for the object position, and Angular interpolation used for the angle if it is enabled.) The Bandwidth parameter allows the maximum rate of updates from the host to be reduced. This is not normally necessary - refer to the manual entry to find out more about the option.

The Sync object object by default does not transmit any other data, and instance variables would not be updated. It would be wasteful to automatically update every instance variable, since some of them might only be used locally. After the Sync object action, we can optionally use any number of Sync instance variable actions to add instance variables for the host to also send to peers (and as with Sync object, the instance variables are updated automatically). The variables added in this case are the lookatx and lookaty variables (so we can see where other players are aiming), inputs (so we can tell what buttons they are pressing), and the player stats with the health, kills and deaths variables. Remember that as with Sync object, these variables are only sent from the host to peers to indicate what the state of the game is. Also note that although peers are receiving which direction buttons each player is pressing in the inputs variable, this information is ignored: Sync object is already updating object positions, so it's not important to take in to account their controls. However we are interested in whether or not they are firing, which is just one of the bits in this value, so we sync the whole variable anyway. (Another game might also find the input states useful for setting animations.) The look positions also use linear interpolation, and the others don't use any interpolation since they should simply be updated in steps.

The client value tag is used if the instance variable also corresponds to a client input value. In other words if a peer's client input value is set from that instance variable, setting the corresponding tag here allows the multiplayer engine to know they are linked. The host can use this information to reduce the latency when relaying the inputs to other peers.

Now that all the client inputs and synced objects are set up, the last action in On start of layout connects to the signalling server.

Share and Copy this Tutorial

You are free to copy, distribute, transmit and adapt this work with correct attribution. Click for more info.


Tedg 9,893 rep

Awesome tutorial and awesome tool :)

Wednesday, March 19, 2014 at 5:21:07 PM
Jayjay 23.0k rep

Thanks for this tutorial! :D

Wednesday, March 19, 2014 at 5:51:25 PM
superwifibattler 3,332 rep

How do you make this with a drag&drop game or a board game or something?

Wednesday, March 19, 2014 at 7:02:12 PM
exertia 2,630 rep

@ashley - is there a way to restrict # of players to say 2 or a predetermined number, and force additional players into a new room / game.

Wednesday, March 19, 2014 at 8:33:47 PM
SergioRM 6,968 rep

really interesting! thank you!

Wednesday, March 19, 2014 at 9:59:39 PM
rexrainbow 164.6k rep

Like @superwifibattler said, the prediction might not be useful in some cases like turned based game (board game, poker game) , could Ashley provide some hint about that?

Wednesday, March 19, 2014 at 11:15:51 PM
Joskin 6,129 rep

Thanks for this tutorial ! It's not easy !

@rexrainbow, what about using the messages system for cards game ?
I can try to make a simple example.

Thursday, March 20, 2014 at 9:06:27 AM
rexrainbow 164.6k rep


Yes, I know it's possible.
My idea is -
1. [peer] request a command
2. [peer] validate this command (could I run this command?)
3. [peer] if validated pass, execute this command and send this request to host

4. [host] receive command , then validate it
5. [host] if validated pass, execute this command and send this request to other peers
- if validated failed , something with that peer

6. [other peer] get validated command, execute it.

The validation function and execution function will be the same at host and peer. (peer and host will use the same function to validate command)

Thursday, March 20, 2014 at 9:34:26 AM
potpie1010 2,963 rep

Awesome tutorial; great feature.

There's no other game engine that's fun to use, yet is still able to produce actual games.

Multiplayer should be interesting!

Thursday, March 20, 2014 at 1:18:01 PM
bilgekaan 32.3k rep

Great feature and tutorial thank you!

Friday, March 21, 2014 at 11:48:30 PM
dynamiczny 442 rep

Is there any chance of obtaining the code for the signaling server? After all I would not like to infinitely rely on the server which I don't even have access to..

Could you provide the code so that we could set up our own signalling servers?

Tuesday, March 25, 2014 at 2:30:26 PM
qu0y 3,151 rep

Thank you Ashley!

Saturday, March 29, 2014 at 2:09:01 PM
drappdev 2,164 rep

I think i missed the section around the room size.
If i change the "Max Peers" value to 2, and then open 4 separate browser windows to the localhost:50000 address. the first 2 i login are entered into the room, whilst the 3rd & 4th are given the error "signalling error: room full".
If anyone knows how to push the extra players above the max peers into 'new' rooms. I would really appreciate the knowledge share.

Tuesday, April 01, 2014 at 10:08:55 PM
AbelaNET 14.9k rep

Ashley, thanks. Great info.

Wednesday, April 02, 2014 at 8:20:19 AM
Wsoukkachang 1,433 rep

@drappdev The new auto-join feature should help you with your problem.

Thursday, April 10, 2014 at 3:26:15 AM

Leave a comment

Everyone is welcome to leave their thoughts! Register a new account or login.