It’s been ten years, almost to the day, that I attended my first programming lecture and learned how to code. I was really lucky to be able to spend years of my life learning how to do that and not worry about much else. Most people don’t get that opportunity, and that can make it hard to think about how software gets made, or what programming even is. Lately I’ve seen a few cases where people have made guesses at how hard it is to make games, and while it’s easy to laugh at these claims, it makes a lot of sense to me that people think multiplayer could be added to No Man’s Sky in a week. After all, the main way a lot of people experience games development right now is Early Access, where features are rapidly prototyped and added into games, often at high-speed.
This way of adding features to a game is one particular approach to making games, and it has an impact on your game in a way that’s hard to see if you’re just reading patch notes or occasional developer tweets. So I thought it might be interesting to talk briefly about what it means to write a single line of code, and how Hello Games probably could write multiplayer code in a week, but you might not want them to. If you’re a programmer yourself, this post might not have much to interest you, but if you’ve never seen the insides of a program, it might offer a quick thing to think about.
In Rogue Process you have a keycard level that says which doors you can open. Suppose we want to write a line of code that wipes the player’s keycard level out – maybe a rogue virus is able to reset the player’s level to zero. In Unity we can search for a game object by name, so we might write something like:
Find("Player").keycardLevel = 0
This (simplified) line of code is very easy to write, because I know the player object is called ‘Player’ and I know I want to set the keycard level to zero. Code like this is simple to write, and when you’re prototyping that kind of code is the best code. But we can’t always guarantee the player object will be called ‘Player’, so to be safe we’ll keep a variable called ‘player’ that we set to point the right object (somewhere else in the code).
player.keycard_level = 0;
This works great, but then one day we decide we want to add a second player sometimes, for co-op. Oops. Now we need to change this code, because the virus won’t affect player two. We could just add another line:
player.keycard_level = 0; player2.keycard_level = 0;
But if we decide to add four-player co-op later this won’t work either. We’re very sensible, well-behaved programmers, so let’s do the correct thing and make a list that we can add as many players to as we like:
The ‘[ ]’ symbols mean that I’m making something called an array, which is a kind of list. I can then put my players in this list when I start the game, and just go through the list when I want to affect all the players. I can use a programming tool called a loop, like this:
foreach player in list_of_players: player.keycard_level = 0;
Oh, wait, there’s a problem. You see, arrays are a special kind of list that can’t change size. What if Player 4 wants to drop-in on a game that’s already playing, and we only have an array with three slots in? We’d better make it a proper capital-L List instead of an array, they let us add and remove things.
Phew! Now we can add and remove players whenever we want, and the scripts in our game like the keycard virus can easily look at this list to find all the players. Ah but, dangit, the next day we realise it’d be much more fun if the virus could affect guards as well… so now we need to change the list again so it can have both players and guards in it, not just PlayerObjects.
You get the idea. Each time we rethink a decision, we have to go back and change the way we wrote the code. And not just that one line of code – any code that references the player, or an attribute of the player, might need to be changed or rewritten as we change just that one line of code. That could be hundreds of lines scattered all across the game code, depending on how well we’ve written the rest of our game (and looking at all the changes we made to one line, you can imagine the complications that exist elsewhere). Each change causes a huge time cost to change and update our code with the new and improved structure.
One response to this is to simply ask why we don’t always write the best, most flexible code we can, all the time. In a lot of software development this is exactly what you should do, and a lot of money and time goes into engineering software and planning its design in advance. Game development has a problem that makes this very inefficient, though: it’s hard to imagine how an idea will feel like in-game. You have to code it and play it. But you also know there’s a high likelihood you’ll have to throw it away later. Should you spend the time to write this new feature correctly, knowing that there’s a good chance it won’t be fun and will get deleted? Or should you quickly prototype it, knowing that if it stays in the game it might cause dozens of problems later on as you fix the shortcuts you took?
Writing fast prototype code is definitely an approach I like, and doing it gets you satisfying, short-term gains as you cover ground quickly. If you’re lucky, and you wish really hard on just the right star, even the worst and most rushed code can survive all the way to a game’s release without causing any problems. But get it wrong and you might realise only months later, where fixing a problem suddenly requires hours and hours of code replacement, as Rogue Process did this month. So, could Hello Games add multiplayer to No Man’s Sky in a week? Probably. But you might spend the rest of the year regretting it.
Thanks for reading! I’ve found some time lately to really get to work on Rogue Process, and we’ll be releasing a build for AIIDE’s Playable Experiences showcase really soon. I can’t wait. More info on that and hopefully some November development streams coming soon!