Lessons Learned: Multiplayer
No matter if it is a LAN party with your friends or an online multiplayer game, playing together
with other people is fun (at least most of the time ;-). In our first sessions of this project,
we juggled with a lot of ideas before settling on what became Mages: a side-scroll shooter where
you could influence gravity, a mobile zombie apocalypse settlement builder, a strategic micro-management
fantasy fighting game, ... Looking back, I noticed that nearly all of our ideas had one thing in
common: they heavily relayed on multiplayer mechanics. Turns out, these are quite difficult to implement.
Both from a technical and from a gameplay perspective, multiplayer presents a lot of challenges. In
this article I'll write down some thoughts on what worked out and what not in these two areas.
Early in the beginning, Jonas set up a command-based architecture which worked out really well.
If you summon a fire projectile, the physic system decides to destroy a wall after a collision or the
game mechanics notice that the game is over, every change is done by a command. The acting element (user
controller, physics, etc.) will send a corresponding command object to the lobby (see the illustration).
Based on the observer pattern different
handlers listen at the lobby for new commands. Once a new command arrives at the lobby, each listener
decides if it wants to handle this command or just ignore it. If it wants to handle it, it might send
it over the network to the other players' lobbies or just apply it locally. Only when a command object's
apply function is called, the fireballs are created, the walls destroyed or the game ended.
Exemplifying illustration of the command architecture used.
This architecture has the big advantage that game mechanics and network are clearly separated. If I
want to add a new element to the game, let's say a fire-ball throwing minion, I first create the minion
class to represent it. I then create a SummonMinionCommand class that extends from WorldCommands. In there
I define all the game mechanics necessary for summon a minion: removing some of the player's mana, binding
the minion to the corresponding mage, etc. I then only have to make sure that when the player clicks on the
right button a SummonMinionCommand is send to the lobby. Since this is a WorldCommand the correct handler
will pick it up and the minion will magically appear in every players' world without me having to worry
about any network stuff. I'm simplifying a bit, but just a bit.
The commands can also be reused very nicely. If you take a look into our map files (in the directory
res/maps the .map files), you can see that all objects in the environment (barrels, trees, rivers and so on)
are defined as JSON-serialized object creating commands. We do not need to create any object-specific map
loading code. Our map-loader just turns the JSON-commands into Java objects and sends them to the lobby
letting the handlers take care of that. So far, so good. Let's come to the parts that did not work out that well.
We wrote a lot of code for serializing our commands to and deserializing them from JSON. Boring work that
mostly could have been done by libraries like GSON or
Sending some commands through the network is rather easy. But then all the little and not so little details
start haunting you. One major problem is player lag, even in a local network. It can cause synchronization
errors if a player tries to move an object that is already destroyed on the host. It can make objects jump
in the clients' game worlds which looks really bad. Or it can make a client player's action have a totally
different effect just because the fireball was on a slightly different place on the host machine. Especially
for a fast-paced game, fixing these problems is essential. We spent a lot of time on them, adding e.g.
tricks to resynchronize a client without the player noticing, but we have to admit that we are still not
satisfied with it.
Developing a multiplayer game might seem easier than having to develop a complex AI. Just let the players play.
But there are some caveats to this. Gameplay or mechanics are crucial for a game. A player might forgive a small
bug now and then. But if playing the game does not feel right, he or she just won't like the game. And while
fixing a bug in the later stages of your game is usually a minor task, fixing mechanics is much more difficult.
One pain point of multiplayer mechanics is balancing. For a single player game slight (!) balancing is usually
easier: if the level is not hard enough, you add some more enemy minions, the level is too difficult, you lower
the end bosses health. In player versus player fights, you have much more combinations because each party can
adapt their tactics independently. You might think that making the fireball a bit faster is the right thing to
do. But this might change the opponent's strategy, which you can not control since it is another human, completely.
And the more game mechanics you add, the more complicated this gets.
In an intermediate state Mages had much more weapons to offer. Apart from fire and earth projectiles and walls,
you could cast a shield, summon totems that threw fire projectiles and slowed enemy objects. And there was a third element
"Arcane" giving you bouncing projectiles, shooting rays and mana regeneration spots. But balancing did not work
out. The fire projectile generating totem was e.g. either way to strong, basically giving you free fire
projectiles all the time. Or it was much to expensive, causing players to never summon it. We then decided to
remove all these parts and concentrate on a reduced amount of mechanics but getting these right. Still, it hurt
to throw all these half-finished parts away.
Some elements that did not make it into the final prototype.
There is probably a reason why many successful small developers produce single player games even though their
game ideas would have allowed to add multiplayer parts (FTL, World of Goo, Mini Metro, Prison Architect just to
name a few). World of Goo and Prison Architect allow some indirect interactions between players (sharing
highscores and maps) but that's it. Sticking to single player removes a lot of burden on the technical side.
Also, testing your game gets easier because you need to coordinate with less people, in particular when getting
feedback from external people. Although creating games that allow people to interact with each other in manifold
ways is fascinating, if this is your first project, I'd recommend to start with a single player game and focus
on the mechanics that make your game special.
Michael Hedderich, 23.07.2016