How NOT to make a real-time multiplayer game in 30 days

Or how to get more done at your next hackathon

Some time ago I was attending the annual gaming event Game Off 2020, whose members made a game on a specific topic in November. This year’s theme, Moonshot, has led to over 500 ambitious, mostly space-based games that you can see. here

After hearing about the event in October, I told my brother and his wife about it, and we decided to team up. We are by no means game developers, but our personal skills complemented each other well. My brother’s wife, an artist, would focus on creating resources for the game. In the meantime, my brother, an animator-turned-programmer, and I, a programmer, concentrated on making the game.

As soon as the topic became known, we immediately began brainstorming to come up with an idea that, in the spirit of the theme of the gaming competition, would be fun, ambitious and preferably cosmic.
We had a few ideas, but one got us nodding excitedly in agreement, and that was the idea of ​​playing Bad Asstronauts. The message of the game was as follows:

Bad Asstronauts is a fast-paced multiplayer mobile game in which players roam the space in armed spaceships in search of valuable resources that ships accumulate on their home planets. Players must always protect their cache of resources from being destroyed by opponents.

The title originally chosen for the article was “Build a Real-Time Multiplayer Game in 30 Days.” Seeing how we develop the game, I wanted to share with you some of the experience we have learned over these 30 days creating the game in a limited time.

Understanding project requirements

Having decided what kind of game we want to create, we asked ourselves what technology to use. Since we were making a multiplayer game, this decision had to be made with respect to the frontend and with respect to the server side – both decisions once and for all.

For no particular reason, we decided to try our hand at creating an interface with Flutter using Flame, a minimalistic 2D game engine. For our game, this choice seemed reasonable. The server side, however, will be more complex. Massively multiplayer online games are notoriously difficult to develop, and the real-time component we wanted to bring to life would only add to that complexity.

Our first approach was to use Firebase, given its popularity in the Flutter community, its seeming vitality in games and recommendation from a friend. So we decided to do a little proof of concept to test the idea.

Data Synchronization Test with WebSocket

Don’t lose sight of the main task

Unfortunately, what should have ended in a few days lasted until mid-November. While Firebase wasn’t easy to get up and running, the biggest time waster was something else: distraction. It took us several days to implement what you see in the gif above. A lesson learned from this: Firebase serverless approach with Cloud functions and Firestore (one of the two cloud database options) while useful in many situations, it will not be able to meet the requirements of the real-time game we intended to create.

It’s a shame that instead of immediately creating what you see above, we got carried away trying to create a mini-game in which two players compete to see who gets the most clicks, not forgetting about the storage before the timer expires. This involved introducing completely unnecessary features such as timer, counting, and colors, all of which made no sense when it came to figuring out if Firebase was viable.
We switched to WebSockets shortly thereafter, but the time spent working on all these irrelevant features was gone. Our mistake here was that we lost sight of the main goal and had not previously asked the question of how all this helps us to achieve it. So, try to be as single-minded as possible, keep one thought in your head. Whistles and twists can be added later.

Don’t skimp on research

This is not to say that everything went smoothly with the front-end. Just like Firebase does most of the hard work for you on the back end, using a game engine lets you get started by providing a lot of tools out of the box: game loop, object rendering, physics – the list goes on. If, of course, you use these tools.

Although we used many of the tools that Flame had to offer, we ended up implementing a lot ourselves. Not because the implementation in Flame was inadequate, but simply because we did not know about its existence! In an effort to get started, we quickly went through the most important phase – the research phase. As a result, we created many components from scratch, not knowing that they were added by an import statement.

A prime example is the joystick for which Flame had a great module… Instead, we made our own, which (don’t get me wrong) was great practice, but perhaps not the best use of time in a busy schedule. We did the same with other components and it soon became clear that by reinventing the wheel, you often get square

The hasty solutions we came up with made the project very difficult to work with and expand (that really says something if you’ve only been on it for a couple of weeks!). This prompted us to take a step back and redo much of what we already had, but this time using as many of the built-in implementations that the engine offered.

The rebuild certainly took some time before the introduction of new features began (about a couple of days), but by the end it could already be said that we were moving much faster. The point to remember here is that even a few minutes of research can save you hours of unnecessary work down the road.

Know your tool

As such, finding an efficient workflow is critical to rapidly developing features and prototypes. One of the main obstacles that hindered our progress was the need to test the code on multiple clients to make sure the networking functionality was working as expected.

This required launching several Android emulators, which, as everyone who worked with them knows, are memory-hungry animals that, when working with them, will not have enough resources even on decent machines. However, due to the lack of a plan, we tolerated better – even when characters appeared in the code editor as quickly as if we were typing them from another planet. But there was a better way.

Flutter, being a cross-platform toolkit, allows create web applications from the same codebase used for mobile devices. Basically, this meant to us that we could bypass emulators entirely and instead run multiple clients directly from the browser with a significant reduction in memory costs. We only got to make this big discovery much later, and for the most part, when it came time to run tests on multiple clients, we had to put up with a stream-killing state of affairs.

I’m not saying you need to familiarize yourself with every nook and cranny of every tool you use, but this may be one of the few times when dissatisfaction is the virtue rather than patience. The moment you start to wonder if there is a better way to do something, do yourself a favor and check if it exists. It usually exists and can be beneficial.

Consider a layering approach

One of the things we did at the very beginning was we took a layered approach when it came to adding new features to the game. In this sense, a layer is nothing more than a collection of features that, combined with features from the underlying layers, make up a fully functional version of what you do.

The advantage of this approach is that if the team at some point notices that they cannot complete the functions of the current layer before the deadline, they can return to the previous layer, while still being able to complete a working, completed project. For example, here are the layers defined for our game at the start:

High-level development plan for Bad Asstronauts with 7 difficulty levels

The reason for what happened is simple. As the number of features in the application increases, so does the level of complexity. Left unchecked, the increasing complexity can lead to project development spiraling out of control, resulting in significant delays or even project cancellation. This phenomenon is known as “hidden function“.

This is what we hoped to get from the layered approach anyway. However, for various reasons already mentioned, lifting even the first layer from the ground, that is, the layer with basic functions, took more work than could be done in the stated 30-day period.

However, this should not diminish its effectiveness when used correctly and in combination with the other points mentioned here. This brings me to my last point.

Don’t bite off more than you can chew

None of the points mentioned so far would be of much use if the goal were too ambitious from the start. We all strive to surpass ourselves, and this is a wonderful trait that cannot be ignored. And while our ambitions can be endless, the time and energy, the resources that drive our pursuit of these ambitions cannot be endless.

For a team of three with limited game making experience, the decision to build a game of the intended scale, with real-time multiplayer capabilities in just 30 days, may have been a bit overkill. For example, the strategy would be wiser to compromise and choose either a relatively complex single-player game or a simpler multiplayer game.

So, before starting, evaluate the effort to implement each proposed idea – this is the key to ensuring a smooth development process. Evaluating your efforts will increase your chances of getting things done on time. Keep developing! Remember, finished is better than perfect.


Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *