Monthly Archives: October 2021

Building Experiences: “Making” vs “Designing”

Almost everyone who has played a game has also come up with ideas for games of their own. Many people take this even further and want to make their own games so they can share their ideas and potentially create something completely new and unique. This is great and I’d encourage anyone with a desire to do so to make their ideas become reality, but there’s a big issue that must be addressed if you want others to enjoy your creation, and that is mindset. Specifically the difference in perspective between “building your idea” and “designing an experience”, the key difference here being methodical and conscious decisions about how the player will experience your game.

It’s relatively easy to “make” some mechanics that sound cool and throw them together, but this rarely ever results in a fun experience, especially for anyone besides the creator. To truly “design” an experience you need to understand what makes something fun so that you can leverage that knowledge to create better systems. You need to understand what the individual (who is likely not yourself) wants from your game and how you can give them the tools to create an enjoyable experience. In doing so, your decisions become more deliberate and you can analyze and test if a given change accomplishes your goal. If not, then you can step back and find a different solution, eventually resulting in an experience that is refined and enjoyable to those who play it.

A good example of this can be found in mods for existing games. Oftentimes there will be large mods which have massive amounts of community support and are often recommended over the base game. These mods have been designed and built by members of the community for members of the community, and build upon the base game in interesting and unique ways. On the other side of this, if you look through mod catalogs you’ll find all kinds of weird mods that do things you’ve never thought of, but also you never really wanted and still don’t even though it exists. These kinds of mods highlight two important things, the first being that there are people out there who want very different things than you, especially because they took the time to build said mod. The second is that these are good examples of “building an idea” and not “designing an experience”. While the creator probably accomplished their goal of building the thing they wanted, the mod doesn’t really appeal to the larger community and as such probably won’t be used very often.

 All that said, you can absolutely always make something for yourself for fun. It doesn’t have to be big or even unique if it’s something you want to make, but if you ever want to turn game development into a career or even just want other people to like the things you make, you’re going to have to learn to design for someone besides yourself. At best, you’re making games for people similar to yourself. At worst you’re building things for someone who is nothing like you and has completely different interests. The reality is that if you want your game to be successful, you’re going to have to learn to design for both.

The Importance of Separating Ideas and Emotions

Working in game development I’ve seen my fair share of conflicts over ideas and directions for projects. This is something that is very common when working on games because of the creative elements involved and the sense of ownership people develop when they create something they believe is interesting and good for the game. However, this is not exclusive to games, this sort of thing often happens in traditional development as well. This can come from things like code reviews where people feel protective of their own work or even just high level design discussions for different systems. There is always a need for conversations about these ideas because a group of people can always make something better than any one individual whether that be from feedback for improvements or potentially ideas that are just better for the given situation. The problem then comes from the fact that people by nature become attached to their own ideas, they fixate on their own pitches and have a hard time accepting feedback or that their idea might be worse than someone else’s.

The goal for us as developers is to find the best solution for a given problem in whatever form that might entail. This is independent of who came up with the solution or who contributed to it because in the scope of the problem, those things don’t really matter. This is why the separation of an individual’s ideas and the emotions associated with them is important. Emotions cloud judgement and inhibit a group’s ability to properly discuss and analyze a given problem causing the solution to be worse off as a result. In addition, stress and antagonistic behavior in a group can make working together difficult and just make the development experience overall very unenjoyable.

The best environment for collaboration and problem solving is one where people can advocate for their ideas, but are also accepting of others. This is not easily done, as it requires individuals to step back from their work and objectively analyze a given problem and the solutions provided, but to also feel like their ideas are valued and heard within the group. Only in this kind of environment can people collaborate on the vision and direction of a project without having to have an individual act as the anchor and focus point of the team. The best projects come from groups of individuals whose goals are to support the best decisions for the entire team and to create something truly unique and interesting.

State Machines Are Bad and Why You Should Use Them Anyways

I remember my first introduction to state machines was by a professor exclaiming about how terrible they were. When I eventually discovered how they worked and what they did I thought he was crazy. In my head I thought “they give you so much control over logic flow and solve so many complexity problems, how is everyone not using these?!”, and in one sense I was right. They solve a lot of problems compared to not using any structure at all and they simplify a lot of common behavior and UI problems where you want distinct changes in functionality. 

I had other students echo my professor’s sentiment “state machines bad”, but when I asked them why they didn’t even really know what state machines were. From there I knew it was my sworn duty to convince everyone of how great state machines are and why they should use them for everything; except I was wrong. State machines are a great tool and they work great in specific situations, particularly simple ones, but they definitely have flaws. They struggle in complex situations where many things can be happening at once and can cause huge issues with multithreaded systems. They are definitely not the golden egg of programing paradigms that I once praised them to be, but they definitely still have a purpose in every developer’s toolkit.

So then what is a state machine and why would you want to use one? For me it always comes back to logic flow. Having a concrete and traceable system where you can easily identify the order of execution in messy complicated systems is a huge boon to development and your sanity. This is where state machines come in. They separate logic or “states” into independent execution channels with consistent behavior. While that may sound complex (and reading articles about state machines will definitely make you believe that is true), the reality is that they don’t really need a ton of features for them to be useful. A simple example (C++) is a switch statement with an enumerated state:

Here we know that every loop will only ever be in one state and that every state individually controls what other states it can enter. So from start to finish a given state controls everything about that execution sequence. It doesn’t have to worry about other states interfering and it doesn’t need to check if we’re flying because we’re obviously dying instead, otherwise we wouldn’t be in the dying state to begin with. This example leads to my next bit of code, otherwise known as “indicators you might need a state machine”:

This is a not so exaggerated example of code similar to some I’ve seen in my teammates work on previous projects. These often tend to spread all over the place as systems get more complex and often act as band aid fixes when characters are doing things when they aren’t supposed to. The problem here is that aiming has to care about all the other actions the player could take and for each new one you add there’s an exponential growth of systems that need to check for the status of other systems until you’re stringing along conditionals that become impossible to manage. We don’t want “aiming” to have to worry about if you’re “dancing” or “climbing” or “jumping” (well maybe jumping, but we’ll get to that later). We want “aiming” to handle aiming. That is its one responsibility and removing all the extra complexity makes designing a system that handles aiming much simpler and more straightforward. This makes any system where you can compartmentalize logic into separate states easy to handle and develop.

All this sounds pretty good right? Well then what’s the catch? Well a good example of the catch is jumping. You might not want to dance when you’re jumping, but if you’re making something fast paced you may want to be able to aim while you’re jumping. Well ok, one combination is simple, we can just put together a new state called “jump-aim”. But then what if we want to do flips while jumping and aiming? Then we need “jump-flip” and “jump-aim-flip” and then we need to consider how you transition between them. Can you jump-flip into jump-aim-flip? Can you jump to jump-aim to jump-aim-flip? Then how does non jumping aiming transition into all of these states? Wait, this is starting to turn into the problem we had before where things were handling too much at once. And this is where state machines start to break down. There are two main issues at hand here; the first being that states without clear boundaries start to break down when you try to put them together i.e. composite states. The second, and arguably the bigger issue, is that whenever you add a new state, suddenly every other state has to be considered for transitions. While we’ve limited the growth of long if statements, the growth still exists. For every additional state we need to handle all the different transitions, both in and out, to all the relevant existing states. These two issues combined can cause state machines to balloon out of control and become difficult to manage and expand over time. While I personally haven’t had to deal with extreme cases of this, I’ve definitely had to toe the line and it’s easy to see how something complex could make an unmanageable mess.

So while state machines definitely have their uses, they aren’t a magic bullet to every problem. An easy way to tell if they’re good for a given system is to consider how well the system’s tasks separate into independent functionality. If they don’t cross the line into other tasks’ functionality often, then the system could be a good candidate. In addition if the system isn’t meant to be dramatically expanded on then it also could be a good candidate. If neither of these are true then it may be time to learn something a bit more complex or to try and re-design the system to better accommodate the separation of responsibilities. 

All that said, just remember that using a state machine is probably better than going in without a plan at all. They can make your life easier when used correctly, just use caution so you don’t turn your team’s project into a monstrous machine.