Defining the MVP
I've decided that I want to build a video game, but that's still very vague. I know which genres to avoid, but which genre should I commit to? I know that I want to focus on a single platform, but which platform should I choose? Do I go with an out-of-the-box game engine, or do I attempt to build my own again?
These are all questions that need to be answered for the MVP - the "Minimum Viable Product". Most people attribute the phrase to the book The Lean Startup by Eric Ries, but a quick Google search indicates that the phrase was coined by Frank Robinson back in 2001. I have read The Lean Startup and would definitely recommend it, but I didn't learn the phrase from the book. I first encountered the phrase during my first job out of college. We had a decent product with a decent customerbase and decent revenue, but it seemed that we couldn't grow the product or our customerbase much higher. Leadership pitched a new vision for the company, which excited a lot of people, myself included. During a meeting with my manager, he asked me how I felt about the new direction the company was taking, and I gave him my thoughts. He told me to start thinking about what an MVP would look like for my team - a statement which profoundly confused me. Up to that point, I knew "MVP" as two things: the "most valuable player" of a team, or the "model-view-presenter" architectural pattern of frontend software. Our existing product used a model-view-presenter architecture for our frontend, so the term "MVP" was commonly used in reference to frontend development among the software engineers at the company. I expressed my confusion to my manager, and he was quick to explain what a "minimum viable product" was.
The phrase is somewhat self-explanatory, but for those of you still confused, a minimum viable product is the minimum set of features that are required to release a new product to early customers. Those customers give feedback, which is used to further shape the product into something that the developer knows that customers want. Shipping an MVP minimizes the amount of wasted work by preventing the developer from building features that nobody actually wanted to begin with.
MVPs are tricky in the video game industry, because customers expect a fully fleshed out product on release day. Video game companies tend to have periodic internal playtests so that anyone within the company can give feedback on the game - even those that aren't part of the team building the game. This allows companies to treat their own employees as if they were those early customers who can give feedback, which can then be used to refine the game. Once the game has been refined to the liking of the internal playtesters, video game companies often invite friends and family of the employees to participate in the playtests as well. This base of early "customers" continues to grow until eventually the general public is allowed to give their feedback in the form of an alpha or beta test.
Playtesting is incredibly important to the product development lifecycle, but is actually pretty hard for solo developers. Sure, you can invite your own friends and family to help you test your game, but much of the time, the feedback isn't as genuine as you might expect. Some people may not want to hurt your feelings, and others are really just proud of you for having come so far, even if the game isn't fun. Indie game developers and smaller studios have had to find new ways to expand their access to playtesters. Many of those developers have started using an "early access" model, in which customers can pay full price for a game, with the understanding that it is an unfinished product. This model allows the developers to pay their bills while receiving the valuable feedback that would normally come from playtesting. It's not a perfect model (I've seen "early access" titles stop receiving updates long before the game was "finished"), but it is far better than expecting developers to work with no income during the entire development process.
Game development is highly iterative. The game that was originally conceptualized is rarely the same game that ships. It's hard for even a highly experienced game designer to determine if the idea in his/her head is actually "fun". The only way to find out is to build out a barebones version of that idea and let other people play it. Taking their feedback seriously and iterating on the original idea is the key to developing a good game. Reducing the amount of time between iteration cycles is the key to minimizing wasted effort.
Before I decide on what I need to build for the MVP, I have another story for you. I worked closely on a project with a few friends. We set out to build and release a mobile game with a hyper-aggressive 6-week time limit. Our idea was that we could ship an MVP, get feedback from real users, and iterate on our design to gain popularity over time. We began with a very narrow scope: allow users to place pixels on a giant virtual canvas. We wanted to focus on the underlying technology, and worry about game mechanics later, after we could observe what users did when presented with a giant MMO canvas. I was in charge of developing the networking stack, and I was incredibly proud of my work. Within a week, we could watch each other draw on this giant virtual canvas from our smartphones over the internet. We spent every weekend and every free evening working on the app - we were still working our full-time jobs during this venture. We developed a unique art style for the pixels so that we didn't simply display colored boxes to the user. We built out a deployment pipeline for the server architecture so that we could release updates more rapidly. We worked very hard, and had a long list of items that we wanted to chip away at before releasing the app publicly, but before we knew it, week 6 was among us, and we decided to publish the app as it was. We succeeded in our original goal.
We had our friends and families test the app for us. Naturally, our immature friends drew inappropriate and otherwise offensive shapes across the giant canvas. Most people opened the app and scribbled around for a minute or so, maybe drew their name in block letters, and then closed the app and never opened it again. Seemingly everyone had one behavior in common: they all zoomed as far out as we would allow them to - they wanted to view the entire canvas, which was not what we had expected. We assumed people would tend to some area of the canvas over which they felt some ownership, and they would come back to make sure no one had taken over. Instead, they were just curious about what other people had drawn, and mostly disinterested beyond that.
We pondered what we could do to the app to make it more interesting - what could we do with this information? We pitched ideas about "owning" pixels like a territorial battle, or constraining the zooming capabilities so that users were forced to work with only a few pixels at a time. I remember telling my friend, "If this fizzles out, it's our own fault." I was convinced that we had all the information we needed to iterate and make the app more appealing. Ultimately, progress on the app died out. We couldn't come up with any ideas that anyone could get excited about. Other life events and responsibilities drew our focus away from the app. To be frank, the game lacked any mechanics to even iterate upon, so I'm not sure what we expected.
I had a few takeaways from that failure: * Our MVP was too small. It did not contain the bare minimum amount of features for us to even begin the iteration process. * While we did successfully release the app, no one outside of our friends and family even downloaded it. We failed to account for marketing the product as part of the MVP. * YAGNI ("You ain't gonna need it") - we spent much of our 6-weeks building out deployment pipelines to enable our rapid releasability, which we never used.
Let's talk about game genres.
It's really hard not to be overly ambitious when choosing a genre. Every nerdy kid has had daydreams of games that they would make, perhaps consisting of characters from multiple different IPs. Every World of Warcraft player has a long list of all the things that Blizzard Entertainment did wrong in the latest expansion pack, and all the things that would have made the game better. Don't get me wrong, mashup games have done very well (Super Smash Bros. remains one of the most popular fighting games to date), and games are always better when the developers are receptive to player feedback. But the very idea that any single person could develop a game of the quality and scale of World of Warcraft is utterly absurd. The unfortunate reality is that a kid with dreams of building a game of that scale doesn't actually know how many people it would take - or how many years!
One of my childhood best friends wrote a letter to Nintendo when we were young - maybe 9 or 10 years old. We loved The Legend of Zelda - Ocarina of Time to a fault, and we would stay up all night hunting for new secrets. Unfortunately, it was a single-player game, so we couldn't play together. While one of us played, the other sat and watched. We liked it that way, but we dreamed of more. My friend's letter contained a pitch for the idea of a companion character (similar to Navi) who could be controlled by an optional second player. He named the companion character "Blade" - his logic was simply that "Link" was a noun, and so was "Blade", and it sounded cool. His brilliant idea was actually a complete ripoff of a Jet Force Gemini character named Floyd, a flying robot with which a second player could control and use to shoot enemies. I didn't realize that until years later, but I'm sure Nintendo did. To their credit, they did respond to my friend, explaining that video game development was an incredibly time-consuming and resource-intensive process, but they would consider his idea. We interpreted "time-consuming" as "months" rather than "years" (how hard could it be?), and we never saw Blade make an appearance in future The Legend of Zelda titles, though there was a way for a second player to participate in The Legend of Zelda - The Wind Waker by connecting a Game Boy Advance to the Gamecube console - I'll give my friend credit for that.
I'm not a kid anymore, unfortunately, but I still find it difficult to fight off the urge to build my very own role-playing game. I've made several attempts - some in the form of action-RPGs with puzzle elements (like The Legend of Zelda), and others in the form of turn-based combat with a tile-based overworld (like Pokemon). They all end the same way: I get overwhelmed and bored with the sheer amount of content required to fill a game like that. The battle system of Pokemon isn't particularly difficult to program, and the movement system is even easier. No, the difficulty comes with conjuring up with all of the cities, all of the characters, all of the side quests, all of the monsters, all of the abilities, all of the items, and much much more. Like I said before, I'm fine at building the underlying systems, it's the content that I struggle with.
Any games with a lot of playable characters or a lot of equippable items are tough to balance. These include shooters, MOBAs, MMOs, and even sports and racing games. Balancing is an art, and I must admit, I've never even attempted it, but I have a deep appreciation for the game designers that handle such things. It is fascinating to watch which characters are commonly chosen in DOTA 2 competitions, and how one character can disappear from the preferred lineup as a result of a relatively minute balancing patch. I consider the act of balancing the game another form of content creation, and as such, I'd like to avoid it as much as possible.
I have a soft spot for co-op games. There's something particularly exciting about failing together and watching each other learn over time. It can definitely be frustrating at times - just ask my family about our time playing Overcooked!. Sometimes you just need some time to cool down before getting back into it, but I love the feeling of overcoming the obstacles by learning to trust each other. Co-op gameplay comes in a variety of different forms - Overcooked! is remarkably simple in its design compared to, say, a raid in World of Warcraft:
Overcooked!
- Single-screen multiplayer (not split-screen) "couch co-op", which means...
- No networked multiplayer
- Up to 4 players
- The team's performance is rated in terms of 0-3 stars. Stars are used to unlock new levels
- All players have the same capabilities
- There is no progression of player power
World of Warcraft raids
- Each player uses a separate client, which means...
- Fully networked multiplayer
- Up to 40 players (more likely 10-30 in the modern era of the game)
- Do or die - bosses must be defeated before progressing to new bosses
- Players choose from dozens of class and specialization combinations, each with their own strengths and weaknesses
- Players receive new items by successfully defeating bosses. Items enhance the performance of the player, making future encounters easier
That's not to say they don't have some things in common. Both games feature 3d graphics, unique art styles, fantastic music, and intuitive controls. Still though, Overcooked! is a much less complex game with very focused gameplay, enabling only 2 developers to develop the game in about 18 months (see Road to the IGF: Ghost Town Games' Overcooked). Success stories like that give me hope, but even a game like Overcooked! has a more ambitious scope than I'm comfortable committing to: a handful of playable characters, more than 36 levels, and a storyline.
Another genre that I love is puzzle games. I'm a programmer, so it might be obvious that I like problem-solving and brain teasers. Unlike programming, puzzle games aren't normally completely open-ended, rather, the levels are designed to help you arrive at the solution. Portal and Portal 2 are centered around the concept of just getting to the next door, and your ability to create portals to traverse the level is your means to do so. Every wall, every cliff, and every obstacle were intentionally placed to convey a specific solution to that room. However, think of it this way: while coding, a developer generally has a "big picture" in mind - the product. There are a lot of small steps that it takes to achieve the larger goal, but the problem-solving process is rarely about just getting to the next function. In Portal, the player never actually had the option to achieve the end-goal of the game in any different way. Puzzle games are more laser-focused: you must solve each individual bite-sized puzzle in a certain order.
Except that's not quite how those games are designed. The levels of puzzle games are curated and organized such that the concepts of earlier levels remain relevant in later levels, often overlapping into a larger problem that is more difficult to solve. In that way, puzzle games are very much like programming: the smaller concepts can be combined in interesting ways in order to solve larger problems, as long as you understand how those concepts interact with one another.
Jonathan Blow - noteworthy for his inclusion in Indie Game: The Movie, but even more impressively, the creator of Braid and The Witness - is an advocate for a particular style of puzzle design. He starts with a very specific concept, and explores every possible avenue of that concept to create puzzles that make logical sense to the player. In Braid, the concept he chose was the ability to rewind time. The levels of the game augment that core mechanic in interesting ways, such as certain entities becoming immune to time manipulation. Once an augmentation is introduced, the players must discover for themselves the implications of its existence. One of the most fascinating parts of his level design is that some of the levels are identical to one another, except for the addition of a particular augmentation. The mere existence of the augmentation results in an entirely different solution to the level. My puny paragraph cannot possibly do justice to his design philosophy, so I encourage you to look him up - he has a massive catalog of conference presentations, live streams, and interviews in which he goes into more detail about his design process. Indie Game: The Movie did wonders for getting people into video game development (myself included), but the content is focused more on the drama of game development rather than the design and development process itself.
I could go on for ages about different game genres, comparing different games within a given genre, or drawing Venn diagrams of which games fit within which genres. Luckily, in the process of writing out my thought here, I've come up with an idea.
It would be disingenuous to say that I just came up with this idea. I've had the idea and played around with it a couple of times in the past, but just never really followed through with it. Truth be told, it's one of the only ideas that I successfully implemented to the point of letting others playtest it. The idea is based on the healing mechanics used in many MMORPGs.
In those games, as with most RPGs, each player has some amount of maximum health points (HP). As the player takes damage, they lose health points. If their current health reaches 0, that player dies. The user interfaces of those games display each player's health as a progress bar, filled according to the percentage of their current health relative to their maximum health. Players can recover health through a variety of methods, including items, abilities, and external actors. Some players specialize in healing other players, and those players often focus their attention to the health bars of other players to make sure no one dies.
In the past, I whipped up a simple user interface with a few health bars. The progress of those bars would slowly drop, and the user could touch one of the health bars (it was an Android app) in order to recover some of that bar's progress. I let a few people try it out, most of which were not MMORPG players. The feedback was mostly consistent:
- "This is anxiety-inducing!"
- "Why would anyone want to do this to themselves?"
- "It's like the opposite of a zen garden."
At the time, I took this as negative feedback, and abandoned the concept. I have come to realize over time that the anxiety is exactly what healers in MMORPGs experience, and part of the challenge is overcoming that anxiety. It occurs to me now that I was requesting feedback from the wrong people, or that I simply didn't have a large enough sample size of users.
That feedback wasn't the only reason I shelved the idea. I felt that the concept was missing a couple of fundamental mechanics that healers in MMORPGs must handle:
1) Movement - Healers must avoid taking damage from environmental factors while making sure they are in range of the players that they are responsible for keeping alive. 2) Encounter variance - Healers are generally not responsible for dealing damage to the monsters that their group is fighting. The length of the encounter can vary widely based on the effectiveness of the group as a whole.
Introducing movement into a game with such limited screen real estate (remember, this was on a mobile device) was something that I couldn't wrap my head around at the time. Furthermore, I couldn't think of any way to introduce variance without being purely RNG ("random number generator", a term used in nerd culture to convey randomness and a general lack of control), rather than the skill level of the group increasing over time, as you would expect to happen with "real" players.
I wasn't the only person to have this idea. There are a handful of "healing simulators" available. None of them account for either of those problems, and some are way too similar to the artwork found within World of Warcraft for my comfort level (check out Little Healer on Android or iOS).
A thought occurred to me while writing this section though: I don't have to completely rip off the concept of "healing", per se. The entire concept is a test of optimization - keep the bars above 0%, but be careful not to waste resources on bars that are already at 100%. The concept of a bar going over 100% got me thinking about a bathtub overflowing, which made me realize that I can frame the underlying mechanic using any coat of paint that I want. Perhaps it is a bathtub that needs to be drained, rather than filled, due to a leaky pipe. The entire game could be a series of levels from the perspective of a plumber with several tools at his disposal. The game's theme suddenly has a goofy feeling rather than dire. The anxiety is still there, but no one is dying. Maybe we can reclaim the element of movement - the plumber needs to run around and gather up his tools and parts so that he can "install" his machines onto the bathtub! Maybe we can add in multiplayer to introduce the variance that I find so important.
I'm not sure what genre a game about progress bars could possibly be, but I like this idea. I still might change the overall theme later, but as far as the core mechanic goes, I'm satisfied.
With a hint of direction now in my head, I can start to think about which platform I want to target. The core mechanic is inspired by the MMORPG genre, which is prominent on PC, but has a smaller presence on consoles and mobile devices. But we're not talking about an MMORPG at all, we're just talking about a thin slice, and we're certainly not trying to frame it in the same light, if we're taking the "plumber in a bad situation" approach.
I initially chose Android for my healing concept for a few reasons:
- The limited screen real estate was perfect for a game that was mostly just progress bars aligned in a grid
- I wanted to experience the healing mechanics while I wasn't already at home playing video games
- I specifically chose Android over iOS because I was more familiar with Android development
Mobile devices still have pretty small screens, though they are pretty large these days. In any case, the UI and input has to be carefully crafted to fit the form factor. Fingers are fat compared to mouse cursors, which means any "clickable" objects have to be large enough for the user to easily touch. Furthermore, mobile devices come in a variety of sizes and aspect ratios, and the UI needs to scale to them all appropriately. Android has the concept of "density-independent pixels" to help scale app UIs appropriately, but games typically don't use Android's standard SDK for their user interfaces, so a lot of work is involved in making sure the underlying engine supports a variety of possible hardware.
The limited amount of screen real estate, in combination with the fact that clickable elements must be larger than you might expect (to support touch-detection), results in a situation in which it is difficult to show enough information on the screen at the same time. I might be able to get away with showing 40 progress bars on the screen, but making sure all of those individual bars are large enough for the user to comfortably touch could prove to be difficult.
Another problem to be mindful of when designing mobile UIs is that the user's finger and hand will physically obstruct the screen from the user's vision while they are pressing a button. It seems almost counter-intuitive for the display device to be the same as the input device, but it turns out that it's one of the easiest things for humans to learn - haven't you ever seen a baby use a tablet?
To compensate for these input limitations, a lot of mobile games opt to forego the inclusion of touchable UI elements altogether, instead turning the entire screen into a touchpad for gesture inputs. Infinite runners detect left or right "swipes" on the screen to move the character in the corresponding direction, while the character never stops moving through the world.
The release process for mobile games isn't particularly difficult. The game must be packaged into a specific format, and the game must abide by the rules of whichever platform you are releasing on (typically either Google Play or Apple's App Store, but other distribution services exist).
Consoles have a different set of problems entirely. The UI elements don't have to be large enough to touch, but they do have to be large enough to view on a television screen from 10 feet away. It is also relatively difficult to "click" on a specific location on the screen using a controller, which is quite the opposite of a mobile device. However, consoles have controllers with lots of buttons - something that typical mobile devices do not have at all. While mobile devices tend to make heavy use of on-screen buttons, consoles tend to assign button inputs to certain actions, and must display those inputs to the users so that they know which button to press.
Consoles are also much harder to release a game onto. Getting a development kit for a console is a fairly difficult process that involves proving to the console manufacturer that you are capable of finishing the game that you are developing, and that the likelihood of doing so is high. Even if you succeed in developing the game, consoles have a strict certification process that your game must pass before being allowed on the platform.
PC is, by far, the easiest platform to develop for and release on. Unlike mobile devices, computer screens have pretty much standardized on either 16:9 or 16:10 aspect ratios (there are some exceptions, but it's not a strict requirement to support them). Unlike consoles, it is generally safe to assume that the user will not be sitting 10 feet away from their computer screen.
PCs also have the most dynamic standard input system: a mouse and keyboard. I admit that it is possible to connect a mouse and keyboard to modern consoles, but it is not a strict requirement for games to support them as viable input methods. Similarly, it is possible to connect a controller to a PC, but it is up to a game's developer whether or not to support controller inputs.
PCs have one big disadvantage compared to consoles: the range of possible hardware configurations. When developing for a game console, you know exactly how much processing power and memory you can expect to have at your disposal. This is not the case on PC, where games must either detect hardware capabilities and dynamically change performance and quality settings accordingly, or expose those settings directly to the users and trust that they know best which settings their computers should be able to handle. Most larger games take the latter approach. Small games can have a big advantage here: if your game isn't very resource-intensive, then you can forego the inclusion of those settings altogether and assume that your game will run fine on all hardware.
The term "PC" sort of implies the use of Microsoft's Windows operating system. There was an entire marketing campaign in the 2000's that made sure to differentiate a Mac from a PC, even though technically, they both fall into the "personal computer" category. How is this relevant to the current topic? Well, there are various pieces of game engines that are different based on the target operating system. For example, the preferred graphics API in Windows is DirectX, which is pretty different from macOS's Metal API. OpenGL is available on both, as well as on Linux operating systems, but has been deprecated in recent versions of macOS, which means it won't be available forever. The Vulkan graphics API is available on Windows and Linux (depending on your graphics driver), but MoltenVk must be used on macOS to translate Vulkan calls into Metal calls on-the-fly (and it's not 100% perfect, in my experience). Also noteworthy: Vulkan is not supported on Sony's Playstation or Microsoft's Xbox consoles, but is supported on the Nintendo Switch.
This leads to an interesting conundrum. The preferred graphics API is different for each operating system, yet the time to implement each one into a game engine is substantial. Readily available game engines, such as Unity, Unreal Engine, and Godot, handle interactions with the graphics APIs for you. Furthermore, we've already established that we will only be targeting a single platform for the initial release, so even if we build a custom engine, the effort isn't nearly as painstaking. Designing the render system to be compatible with a variety of graphics API backends can be pretty difficult though, if we so choose to keep those options open for the future.
There is one last "platform" that I want to mention before making a decision: web browsers. Modern browsers are interesting beasts that are capable of more than most people might think. WebGL is a web-specific graphics API that can be used to render hardware-accelerated graphical scenes to a canvas element in the DOM using JavaScript. All major desktop web browsers support WebGL these days, but only a handful of mobile browsers support it.
WebAssembly enables compiling native binaries for the web. That means that browsers which support WebAssembly can actually execute programs written in a variety of programming languages, as long as the compiler of the language supports WebAssembly as a target. In theory, an entire game engine could be written in a language like C++, compiled into a WebAssembly binary, and then executed in a browser on any platform with incredible performance. WebAssembly is supported by all major desktop and mobile web browsers.
Developing for a web browser does come with a strange design conflict: how do you develop a single application that is presentable and usable on both desktop and mobile devices? There are many guidelines out there for how to make webapps that are "responsive" or "reactive", but it is easy to get it wrong.
It is worth mentioning that web browsers are not first-class citizens on consoles, so even though you could reach a wide audience by developing for browsers, it's not quite the silver bullet of cross-platform development that one might hope.
Knowing that I can only start with one platform makes choosing all the more difficult, but I have to remind myself that there is always opportunity for expansion later. I can start by easily ruling out any game consoles. Unfortunately, I just lack the track record and experience to convince anyone to send me a development kit.
I'd also like to go ahead and rule out web browsers. I have a few other (more ambitious) ideas for browser-based worlds, but I can't shake the feeling that a browser just isn't the right platform for this type of game. I also don't like the idea of developing the game for both desktop and mobile audiences at the same time. The entire point of focusing on a single platform is to free me from worrying about multi-platform complications!
I'd like to say that we're down to two options (desktop vs. mobile), but in actuality, we still have five options:
- Windows
- macOS
- Linux
- iOS
- Android
Interestingly, macOS computers with Apple's new custom processors are capable of running iOS apps - similarly, Windows 11 supports installing Android apps. Honestly though, I'd like to ignore those possibilities for now because - again - I don't want to design the game for both desktop and mobile platforms.
I'm going to rule out macOS. It's just not a gaming platform like its nemesis - it's not even a casual gaming platform like its younger sibling. At best, it's a good game development platform, but that's purely subjective.
Linux has come a long way in the gaming world in recent years, thanks in no small part to Valve, who incentivized game developers to support Linux by creating SteamOS, a Debian-based Linux operating system focused on gaming. Unfortunately, I just don't think the Linux gaming ecosystem is quite up to snuff yet, so focusing on Linux as my only platform would simply limit my potential audience.
There are a lot of great reasons to target Windows. I haven't always been the biggest fan of the OS, but Windows 11 is actually quite pleasant to work in (especially with WSL2, check it out if you're curious). Windows has the gaming market share, the distribution systems, the tooling... but there's one major problem with targeting Windows. I'm currently writing this book on my MacBook Pro, with Apple's new M1 Max processor, which uses an ARM64 architecture. There is currently no good way for me to run Windows applications on my computer in a stable and supported way, which is incredibly important in a development environment, where I need to be sure that any problems that occur are my own. If I choose not to build my own engine, then this might not be too big of an issue, so I won't completely rule out Windows just yet.
The Android vs. iOS debate is undying. Without taking sides, let's take a look at market shares:
- Android maintains ~70% of the market world-wide
- iOS holds ~60% of the market in the US
- A staggering 87% of US teens have iPhones
That actually didn't help at all. Android is wildly popular across the world, but within the US, the iPhone dominates.
I bought the first generation iPhone when I was in high school, in 2007. I switched to Android a few years later when I was in college, around 2009. I was a Google fanboy for much of my college career and beyond, upgrading my perfectly functional devices to whatever the latest Google had to offer with their Nexus or Pixel series devices. My fanboyism started to fade pretty quickly once I actually got a job at Google in 2016 - I guess the magic just died for me once I saw the machine from the inside - but I continued to use Android devices. I switched to an iPhone in 2021. I can't say it's life-changing, but I can't say I regret it either. It truly is a matter of opinion, but the experience of iOS just suits me better at this stage in my life.
With an iPhone at my disposal, but no Android device, it would be much easier for me to test my game on iOS. Furthermore, the vast availability of different Android devices means that it is nearly impossible to test an application on all of them - a problem that I experienced while trying to debug issues with Ventriloid many years ago. Conversely, there are only a handful of different iPhones, and they are all pretty tightly controlled by Apple. It is unlikely that I will need to test my game on any other devices, but even if I do, it is much more feasible to acquire a handful of devices than literally hundreds of them. For those reasons, I am ruling out Android.
So the decision comes down to iOS or Windows. I mentioned that I can't run a Windows application on my MacBook Pro, but the same is true the other way around: I also can't run an iOS application on my PC. Both platforms have insanely saturated markets, but I would argue that the quality of the market is better on iOS, due to Apple's vetting process. On the other hand, Steam is incredibly easy to integrate with if I wanted to support the multiplayer idea.
The game itself is more casual, probably better suited for a mobile device. Remember how I said that I originally wanted to experience the healing mechanics while I wasn't already at home playing the games that inspired them? That certainly hasn't changed. While Windows is a great option overall, I'm going to have to rule it out.
My target platform will be iOS.
Now for the part that scares me: choosing a game engine. There are a variety of options available that are more than capable of building this particular style of game on my chosen platform.
I have the most experience with Unity. I'd say at least half of my video game side projects have used Unity. Like most new game developers, I had way bigger aspirations than reasonably attainable, but no small part of that has to do with how ridiculously easy Unity makes it to build your game without worrying about the underlying technical amalgamation that is the engine. Unity has pre-built components for nearly everything: graphics, sound effects, animations, physics - you name it. In addition, they have the "Asset Store" - a digital marketplace full of components or assets built by the community. If something didn't come with Unity, then you're bound to find it on the Asset Store.
One gripe I have with Unity as of late is that they keep deprecating their older systems before even releasing the new systems which are intended to replace them. I wrote earlier about my MMO-canvas project, in which I was responsible for the networking stack. The networking code was built from scratch because Unity deprecated UNET, and discouraged new projects from utilizing it, yet they had not yet released any replacement. It looks like they have finally released their new Netcode library within the last month of this writing - I hope it was worth the wait.
Ultimately, I don't actually enjoy the process of placing objects in a virtual world, or dragging-and-dropping components to different places in the editor UI. I don't enjoy the process of creating art, models, animations, or sound effects. I don't enjoy the process of importing any of that stuff into the editor and tediously clicking around to add those assets into my game world. The part I enjoy is the part that Unity tries very hard to minimize: programming.
Unreal Engine is very similar. The engine comes with very high quality components, and if something is missing, you can probably find it on the "UE Marketplace". The graphical quality of UE's default settings is generally higher than Unity's (something Unity has been trying to combat recently), though higher graphical fidelity comes at a cost.
I've only used UE for a couple of projects. I don't necessarily hate it, but I don't love it either. When given the choice, I tend to opt for Unity just because I have more experience with it at this point, despite its flaws.
Unreal was considered to be "harder" than Unity for a very long time, simply because its supported language was C++ - more difficult to grasp than Unity's language choice of C#. While Unity has focused a lot of their recent efforts in trying to match Unreal's graphical fidelity, Unreal has put a lot of effort into simplifying the learning process by introducing "Blueprints" - a visual coding tool for game logic. This is quite unfortunate for someone like me, who really just wants to get deep into the code. Unreal continues to support C++ game code though, so not all hope is lost.
While Unreal and Unity are the big players in the game engine market, there are plenty of other options available that might fit my use case, such as Godot or GameMaker Studio.
The first "engine" I used on my own was libGDX, which advertises itself as more of a library than a full engine. Its language of choice is Java, and it supports many platforms (including iOS, thanks to J2ObjC), but to be honest, it doesn't provide much that you couldn't get by using LWJGL (Lightweight Java Game Library - a library which provides Java bindings to a lot of C/C++ libraries, including graphics APIs like OpenGL) directly. I don't think LWJGL has bindings for the Metal graphics API though, so maybe iOS support is no longer a thing.
Godot has a lot of promise, but is still in the earlier stages of development, and I have absolutely no experience with it whatsoever. I certainly don't mind learning new things, but I don't think this is a good time for me to learn a new engine.
Last but not least, there is always the option of building my own engine. I hate to admit that it's one of the most appealing options to me. I love the control, I love the structure, and, really, I just love the code. The term "game engine" is strange because our minds immediately go toward the monstrous one-size-fits-all solutions like the ones I've mentioned above. I've tried my hand at developing engines of that scale - similar to a new game developer aspiring to build a huge Zelda-like RPG, I was an ambitious programmer that wanted to build the next Unity. And just like my overly ambitious game projects, so too did my brilliant game engine die off.
The reality is that, given a finite scope, a game engine can only be what you need it to be, and nothing more. If my game doesn't require ragdoll physics, then why would I spend any amount of time working on that? If my game is made up of pixel art, then why would I build a complex physically based rendering system? To that end, a game engine which is made up of only the minimum set of features to support the game itself is barely an engine at all - it's just the game.
This ain't my first rodeo. I've built my own game engines to support several of my side projects. I've built a clever Entity-Component-System architecture, which I'd like to reuse at some point. I've created job systems capable of utilizing modern multi-core CPUs while respecting that some work has to occur in a strict order. I've integrated several different graphics APIs, including the notoriously verbose Vulkan API. Most importantly, I will know the technical requirements of my game before I start to build the engine.
I will be building my own engine.
Programming languages are an interesting topic to me. As with any technology, each option comes with its own list of pros and cons. Some languages are a joy to read, but with that simplicity comes a lack of flexibility. Other languages are bulky and overloaded, but are incredibly powerful in the options that they provide. However, the syntax of a language is only a sliver of the overall story. Some languages are compiled down to native CPU-specific instructions, while others are interpreted by an entirely separate process which runs on the target machine. Still others provide a compromise of the two styles, compiling down to bytecode that is then interpreted by a virtual machine, which runs on the target machine. The tooling available for each language varies widely, with some languages having massive community support and huge integrated development environments (IDEs), while others are simply written in a basic text editor with no syntax highlighting or auto-correction. Perhaps one of the most important aspects, from a developer's perspective, is the amount of time it takes to test the functionality between changes - how long it takes to understand the existing code, modify it in the appropriate way, re-compile the code into a new binary, and run the specific feature that needs to be tested.
Choosing the right language is incredibly important to the success of the project. Most experienced developers can evaluate the needs of a use case fairly quickly in order to identity an appropriate choice, but in the spirit of this book, I've decided to slow down and write out all of my thoughts on this matter.
Let's start with C, the structure of which is the basis of most popular programming languages to this day. Conditional statements, loops, functions, a "main" method - nearly all programmers recognize these features and know how to utilize them. There is one massive feature that C does not have, however: classes. C is not object-oriented, which is the basis of nearly all modern enterprise-level software development. Instead, C allows the programmer to organize data into a struct
, which can then be passed from function to function. There is elegance in C's simplicity which I did not appreciate in my early career: every entity in the language is either data or logic, but never both. Because C is not object-oriented, it stands to reason that functions are global (unless limited in visibility using the static
modifier, which is different from how other languages define staticness). Since functions are global, there is minimal friction in using a method: just import the header where it was defined and use it in your code.
The lack of classes is strange, having developed my entire career using object-oriented languages. I find that a lot of C code "fakes" object-orientation by declaring struct
s with function pointers inside of them. The final result ends up looking just like the invocation of a method on an object:
struct some_struct {
void (*someMethod)(void);
} myStruct;
void someMethod(void) {
// Add some logic here
}
int main(void) {
// Assign the function pointer to the struct
myStruct.someMethod = someMethod;
// Invoke the "method" of the struct, as if it were an object
myStruct.someMethod();
return 0;
}
The obvious downside to this style is that the someMethod(void)
function doesn't actually have any way to modify the struct
that it lives inside (similar to the this
pointer in C++) without directly referring to the struct
by name: myStruct
, which means the code cannot be reused by creating different struct
s with different values. The way to resolve this is to pass in the pointer to the struct
as a function parameter (like void someMethod(some_struct* this)
), which makes the invocation look awkward: myStruct.someMethod(&myStruct);
. This leads to a lot of mental gymnastics, so I prefer to just embrace the separation of data and logic.
The reason most new programmers (and some seasoned ones) are afraid of C is because of pointers. They can certainly be daunting if you don't have a reasonably deep understanding of how a computer stores data in memory. They are also the source of a lot of bugs, which has motivated new programming languages to forego their inclusion altogether. NASA has a strict style guide for C, which includes a lot of requirements about how to use pointers effectively in order to avoid that class of bugs.
In terms of video game development, C is a perfectly viable programming language, although the lack of classes can become tedious. Because C is compiled to native code, it is capable of integrating with native system libraries, including graphics and audio, with minimal effort. Writing the actual code to use those libraries properly is another story, but linking to those libraries is simple.
C++ was literally built on top of C, and added classes. Well actually, "C with classes" added classes to C, but C++ evolved from that. The history is interesting, but not what I'm writing about, so if you're interested, I encourage you to continue researching it. The addition of classes literally translates to a slew of new features in C++:
- Polymorphism
- Inheritance
- Encapsulation
- Virtual functions,
- More complex access modifiers
In addition, C++ also introduced function and operator overloading, exception handling, templates, and more. In any case, C++ is largely compatible with C code, so a lot of the concepts are literally the same.
Templates are actually the part of C++ that I dislike the most. The motivation behind templates is pure: write a bunch of code that should be able to work on a variety of different classes, and the compiler will generate all of the duplicate code for you. Effectively, templates are a way to get the compiler to generate code that can be utilized at runtime. This has sparked an entire community of template metaprogramming, in which the intent is to execute code at compile-time rather than runtime. Let me be clear: I don't mind if a language allows you to specify code that you want to execute at compile-time. There are plenty of use cases, particularly around file parsing or code generation, in which that functionality is hugely beneficial. My problem with C++ templates is that the templating syntax completely diverges from the rest of C++, which is mentally taxing, to say the least.
Every few years, C++ releases a new version of the language, which introduces all sorts of new bells and whistles. I love cutting-edge new features as much as the next tech enthusiast, but C++ has unfortunately grown to a point where there are many different ways to accomplish the same thing. It's hard to keep up with the "right" way to do things. For example, the use of raw pointers rather than "smart" pointers is highly discouraged in modern C++, and for good reason. However, smart pointers didn't exist before C++11, and it's not always easy to upgrade the language version of your entire codebase, so many developers were left doing something "wrong" for a very long time. I certainly don't think that C++ should stop introducing new features, I just think the language has become bloated in the name of backward compatibility.
Java is one of those "compromise" languages, which compiles down to bytecode, but then that bytecode is interpreted by a virtual machine on the target machine. At least, that's what it used to be. There have been several instances of the Java syntax being separated from its runtime environment entirely. Most notably, the Android development team chose Java as the language of choice for applications running on the operating system, but decided to forego the use of the Java Virtual Machine (JVM) altogether. The Dalvik virtual machine was built instead, which used "just-in-time" (JIT) compilation, and was highly optimized for their target platform (mobile devices). Modern versions of Android use the Android Runtime (ART) instead, which opts to use "ahead-of-time" (AOT) compilation rather than JIT compilation in order to optimize for runtime performance. Other examples of the separation of Java from the traditional JVM is Google Web Toolkit (GWT), which "transpiles" Java frontend code into JavaScript so that web applications can be written in Java syntax, and J2ObjC, which does a similar process from Java to Objective-C code so that mobile apps for Android and iOS can share a single codebase. Also available is Oracle's GraalVM, which compiles Java programs to native binaries, rather than bytecode.
The process of re-implementing the Java API onto a different backend was the basis of Google v. Oracle, which was a very expensive and time-consuming legal battle. I don't know of a single person who agreed with Oracle on this, but I tend to hang out with many more programmers than business people, so my impression is almost certainly biased. It turns out that explaining these highly technical details to a bunch of people with law degrees doesn't typically go well, so the cases are filled with badly framed metaphors. In any case, Google eventually came out on top in the Supreme Court, which means that we are free to implement whatever backend we may desire for an already-existing syntax. But none of that has anything to do with developing a game.
Truth be told, Java's syntax is one of my favorites. It is C-like, but with classes and interfaces, and does not include pointers. The language gets a bad reputation for being overly verbose, but I personally think that is a good thing. Java was once known for its enterprise-grade stability, as well as its reluctance to add new language features between its long release cycles. However, Java has adopted a 6-month release cadence over the last few years, where each version is a more incremental change from the last, but language features get deprecated and removed over time. This new model allows it to add new ways to do things, without bloating the language.
One thing that I have noticed about all of the different flavors of Java backends is that they all struggle to keep up with Java's release cadence. Even before switching to a 6-month cycle, GWT took years before allowing users to upgrade from Java 7 syntax to the much desired Java 8 syntax. GraalVM currently supports up to Java 11, even though the latest release of the language as of this writing is Java 17!
Swift is a very clean language with a focus on development for Apple products, which makes it well-suited for iOS (though it does seem to be getting support for other platforms, including Linux and Windows). It compiles to native binaries, which is certainly a good thing for the runtime performance of a video game. Truth be told, I've never used it, and haven't really kept up with it since its introduction, so I can't possibly place any judgement upon it or speak negatively about it with any anecdotal evidence. It has a lot of nice features, from a syntax perspective. It uses automatic memory management by default, but has mechanisms for opting into manual memory management, which tends to be required for high performance games, but I'm not sure I'll need it for my simple idea.
Python is, well, different. It supports a variety of different programming styles and doesn't really care if you switch between them within the same application. By default, it's duck-typed (if it walks like a duck and talks like a duck, then it must be a duck), which effectively allows you to invoke any function signature on any object, and hope that it resolves itself correctly at runtime. Honestly, I find it to be a nightmare to work with. It's an interpreted language, so there's no compiler to catch your syntax errors. It supports object-orientation, but the vast majority of Python code I've read just doesn't use it. It's very sensitive to whitespace, which is actually pretty smart from a readability standpoint, but I just prefer the explicit use of curly braces to organize my code.
To top it all off, you can't run Python on iOS without installing a development environment from the App Store. What kind of distribution model is that? Honestly, I only mention Python here because it was one of the examples I used at the beginning of the introduction - before I even knew what type of application I was going to build. It's certainly feasible to build a video game in Python, but targeting iOS makes it a non-starter.
There are so many programming languages out there, it would be an enormous task to enumerate them all here. Some languages that I enjoy (but won't be using for this project) include Kotlin and Rust, which provide refreshing modernizations to the JVM and native ecosystems respectively. I get excited about new languages that focus on a particular use case, such as Julia's focus on data science.
I can easily rule out Python and C. Python doesn't check enough boxes, and C's lack of language features (compared to C++) makes it pretty unremarkable. C is actually pretty well suited for a pure Entity-Component-System architecture, in which data is completely separate from logic, but in my experience, ECS doesn't apply to the entire game engine, just the entity selection logic, so having classes helps organize the rest of the code.
Java is the language I'm most comfortable with, which makes it pretty hard to rule out. Getting Java code to run on iOS is certainly possible, but the process supporting or debugging that code is completely unclear to me, which makes it a huge risk. That type of risk is perfectly fine for a learning project, but I'm trying to ship a product, so that type of risk is simply unacceptable.
So it boils down to C++ or Swift. C++ is a hot mess, but it's not such a big deal on a project of which I am the only developer - I can just keep to my own style and ignore the parts that I hate. Templates are a necessary evil, but I should be able to keep those to a minimum. While Swift is the preferred language for iOS application development, I won't actually be using much of the iOS framework. It's more likely that will write the entire game with my own architecture and patterns, and simply use a hollow shell of an application to forward events to my underlying code.
In Robert C. Martin's Clean Architecture, he preaches the importance of deferring decisions until the last possible moment so that options can remain available: "What if your company has made a commitment to a certain database, or a certain web server, or a certain framework? A good architect pretends that the decision has not been made, and shapes the system such that those decisions can still be deferred or changed for as long as possible. A good architect maximizes the number of decisions not made."
I bring up this quote because it has stuck with me for a long time, and has helped me tremendously throughout my career. I may be targeting iOS, but if I choose Swift, I will be ignoring the possibility of porting the game to Android or game consoles in the future. Writing the underlying code in C++ will require a little bit of pain in order to integrate with the iOS application framework, but it's nothing I can't handle. If I do ever decide to port the application to other platforms, I will have to repeat the pain for each one, but at least the underlying C++ code won't have to change (or be rewritten in another language entirely).
I will be writing the game in C++. There will likely be a shell application written in Swift, which forwards events and inputs to the underlying game code.
What other decisions do we need to make for the MVP? As I've explained, the iterative nature of game development makes it difficult to define anything substantial up front, but we do know a few things this game will require before we let people play it.
While we've defined the central mechanic of our game, we still need to come up with a core game loop - that is, the overarching flow of the game, which the player will repeat over and over. For example, the central mechanic of Super Mario Bros. (and most platformers) is simply "jumping", but the core game loop is the process of completing a level, which is achieved by jumping. The core loop for our game can be just as simple - complete each level by utilizing the central mechanic.
Unfortunately, it may not be so simple. Pre-built game engines are so popular because they enable game designers to iterate on their ideas fairly rapidly. The beauty of developing my own engine is that it can be tailored specifically for the game that I want to make, and therefore reduce the overall complexity. If I nail the game idea from the beginning and only write the absolute minimum amount of code to execute that idea, then it stands to reason that none of my effort will have been wasted - the very essence of being "lean". However, due to the iterative nature of game development, the likelihood that some of the code that I write will be thrown away is very high.
My plan to counter this is to split the development process into multiple phases:
1. Engine development
I can't iterate on game ideas until I can receive inputs from the user and display dynamic objects on the screen in a reasonably performant manner. I'll go ahead and add the ability to play sound effects and music to this requirement. We also need the ability to deploy, test, and debug the game on an actual device.
2. Prototype
This is the phase in which the most amount of wasted work will likely occur, so I can take precautions to minimize the amount. While the engine will be capable of displaying graphics and playing audio at this point, I won't be developing any artwork, sound effects, or music. The purpose of the prototyping phase is simply to test out ideas and find out if they are actually fun. Shortcuts can be taken, such as using simple shapes as placeholders for characters and objects. If those entities need to be distinguishable from one another, than using different colors or very badly whipped together artwork is totally acceptable.
3. Content creation
Once we've landed on an enjoyable prototype, we need to build out the rest of the game - levels, menus, characters, items, rewards, etc. It is important to get some playtesting done in this phase so we can iterate on the layout of levels and how any mechanics might interact with one another. This phase is also a bit exploratory, as I may discover some clever uses of my central mechanic as I'm developing different levels with it.
4. Polish
This final phase is often overlooked, but is very important. It involves tying up all the loose ends that weren't really accounted for in the earlier phases:
- Cohesive artwork
- Satisfying feedback (known as "juice" in the gamedev world)
- Clean user experience and logical menu layout
- Application icons and App Store content
I'm sure I'm forgetting quite a lot here, but you get the idea.
There is one thing I haven't accounted for yet: if you recall in my earlier story about releasing an MMO-canvas, I stated that we failed to account for marketing as part of our MVP. While I don't necessarily want the entire world to play the earliest version of my game, I do need some way to get the word out about my game. I have absolutely no experience with this type of thing, so I'll have to do some research and maybe consult a few friends. It is important that I don't forget about it though.
Now for my favorite part - the code!