MANUAL for GMnet ENGINE and GMnet CORE
Okay, okay, I know we already added a player in Step 5.
What this title means, is simply that we are now adding the player to the engine. After this step the player will be synchronized between all players you connect to your server.
So we are basicly done after this step. The rest of the tutorial is some more advanced stuff.
To set the player up, we only have to make minimal adjustments. Create a new code block in the create event, and insert it before the other block.
Start the code block with
mp_sync();
This will tell the engine to sync this object. Once this was created on one client, this instance wil also be sent to all other players.
If you add one player instance to your room and connect 4 players togther, each player will have four player instances.
Now we need to tell the engine what variables to sync and how:
mp_addPosition("Pos",5*room_speed);
This will tell the engine to sync the position variables x
and y
every 5 seconds. 5*room_speed
means 5 seconds. The first argument of mp_addPosition
is the name of group. You can choose that however you want.
What we just added is a so called variable group. The group is called "Pos", get's synced every 5 seconds and sync the variables x
and y
.
Let's set how the position should be synced:
mp_setType("Pos",mp_type.SMART);
This changes the sync type to the variable group "Pos". The default sync type is FAST. We are changing it to SMART. Here is a list of what every sync type does:
mp_setType
if you want to use that):
In our case, if we had chosen FAST, the engine would send the position of our player every 5 seoncds once. Since we are using UDP for networking, there is no assurance, that it will actually arrive. That means we don't know if the other players actually get our position every 5 seoncds. The packet could get lost. However, using FAST is very... FAST. You will see when we want to use FAST later in this section.We are using SMART here and relatively large intervals, because we only sync the position as a backup. As you will see later, we are going to sync the button inputs every single step. We only sync the position to make sure, the players don't get desynchronized.
The button input later will be synced FAST. That means some packets could be lost, and after some time the player could be on two completly different parts of the level, depending on the complexity of your platformer. To make sure that doesn't happen, we reset the position every 5 seconds.
Now, the thing is, if we reset it every 5 seconds, it might happen that the players are just some pixels off. In that case our players might slightly "flicker" whenever the position resets. That doesn't look nice.
Let's add a little tolerance range. If the position at is less than 20 pixels away from where it should be, the other players should not apply this change locally, so it doesn't flicker that much:
mp_tolerance("Pos",20);
Great. Position is set up. Now, just to make sure, let's also sync the basic Game Maker physics and drawing variables:
/**
* Tell the engine to add the basic drawing variables:
* image_alpha,image_angle,image_blend,image_index,image_speed,image_xscale
* image_yscale,visible
*/
mp_addBuiltinBasic("basicDrawing",15*room_speed);
mp_setType("basicDrawing",mp_type.SMART);
/**
* Tell the engine to add the builtin GameMaker variables:
* direction,gravity,gravity_direction,friction,hspeed,vspeed
*/
mp_addBuiltinPhysics("basicPhysics",15*room_speed);
mp_setType("basicPhysics",mp_type.SMART);
We don't need to sync them that frequently. If we sync physics to frequently that might look weird and we are only syncing the basic Drawing stuff for the player color (image_blend
) which doesn't change anyway, and the image_xscale
and iamge_angle
which controls how the player faces, which isn't that critical.
Now we also want to sync the name:
mp_add("playerName","name",buffer_string,60*room_speed);
mp_setType("playerName",mp_type.SMART);
This is using mp_add
to sync our own variables. The syntax is slightly more compelx and we need to do some things to make this work later, but let's just see what we got here:
buffer_string
simply means that the variable "name" stores a string.We decide for a 60 seconds interval, because the name will never change. We could have also used 20 years, that wouldn't make a difference. We only need to make sure the engine syncs it on login and some other critical events, and it does that automatically, no matter what interval we choose. We still need to make it SMART because we need to make sure it REALLY arrived at those events.
Next up are the controls. Remember how we stored the button input in seperate variables? Well now you might know why:
mp_add("controls","pressed_jump,pressed_left,pressed_right",buffer_bool,1);
The "buffer type" is buffer_bool
, because our "pressed_" variables are booleans. 1/0, true/false.
This will sync the button input to all players every single frame no matter what. We don't want to have it SMART or IMPORTANT. FAST is the way to go, since there is no point in checking if the data arrives, because we are syncing the button input every step anyway.
mp_add
Since we are using mp_add
to sync our own variables, we need to make some code changes. We need to send the variables to the engine at the beginning of the step, and retrieve the data at the end.
The reason fot that is, that in Game Maker there is no way of getting a variable's content by accessing it via a string. We need to store the values to a map first before the engine can read them. This is not needed for mp_addPosition
, mp_addBuiltinBasic
and mp_addBuiltinPhysics
, because we hardcoded them.
If you don't know what any of that means what I just said, don't worry. It's complicated.
The only things you need to know, we will explain them now:
For every object where you use mp_add
add the following to the begin step event:
mp_map_syncIn("name",self.name);
mp_map_syncIn("pressed_jump",self.pressed_jump);
mp_map_syncIn("pressed_left",self.pressed_left);
mp_map_syncIn("pressed_right",self.pressed_right);
Replace the names with the names of your synced variables. These are the variables that we created above for our tutorial player.
All changes to these variables need to be made BEFORE using these functions. That means you either have to change them in begin step, or call mp_map_syncIn
again after you changed variables. We recommend the first. And that's also what we are going to do in a minute.
In the end step event add the following code to retrieve the variables again:
self.name = mp_map_syncOut("name", self.name);
self.pressed_jump = mp_map_syncOut("pressed_jump", self.pressed_jump);
self.pressed_left = mp_map_syncOut("pressed_left", self.pressed_left);
self.pressed_right = mp_map_syncOut("pressed_right", self.pressed_right);
Last thing we need to do, is to move this code out of the step event we created earlier:
self.pressed_jump = keyboard_check(vk_space);
self.pressed_left = keyboard_check(vk_left);
self.pressed_right = keyboard_check(vk_right);
Simply remove it. In begin step, add the following code before the other code:
if (htme_isLocal()) {
self.pressed_jump = keyboard_check(vk_space);
self.pressed_left = keyboard_check(vk_left);
self.pressed_right = keyboard_check(vk_right);
}
It should now look like this:
if (htme_isLocal()) {
self.pressed_jump = keyboard_check(vk_space);
self.pressed_left = keyboard_check(vk_left);
self.pressed_right = keyboard_check(vk_right);
}
mp_map_syncIn("name",self.name);
mp_map_syncIn("pressed_jump",self.pressed_jump);
mp_map_syncIn("pressed_left",self.pressed_left);
mp_map_syncIn("pressed_right",self.pressed_right);
All the code we just pasted in begin step does, is check if this is the instance that was locally created and then writes the button input of the players into the variables.
This way when we have 4 players, we only move the instance we control, the instance we locally created. The self.pressed_jump...
variables will be changed by the other 3 players for the rest of the three instances.
Look at the following table from the view of player 1:
Instance/Player | Controlled via buttons | Controlled via engine |
---|---|---|
Ours / Player 1 | Yes | No |
Player 2' s | No | Yes, buttons from Player 2 |
Player 3' s | No | Yes, buttons from Player 3 |
Player 4' s | No | Yes, buttons from Player 4 |
Fire up two games and create a server / connect to 127.0.0.1.
You should now see both players, and should see that we now have a multiplayer platformer.
» Next topic: A second room and doors
« Previous topic: The network controller
All pages in this manual are licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Manuals / GMnet ENGINE Manual
GameMaker: Studio is owned by YoYoGames. GMnet is not affiliated with YoYoGames.
The GMnet logos use icons from Entypo (http://entypo.com/) and Open Iconic (https://useiconic.com/open/). They are licensed under CC BY-SA 4.0.