Dev Log

Jeremy Wolf Jeremy Wolf

Split Screen: New Input System & Cinemachine

Some Background Knowledge ;)

While networked multiplayer is a nightmare that can easily double your development time local split-screen is much much easier to implement. And Unity has made it even easier to do thanks in big part to Unity’s New Input System and some of the tools that it ships with.

So in this tutorial, we’re going to look at a few things:

  • Using the built-in systems (new input system) to create local multiplayer.

  • Adding optional split-screen functionality

  • Modifying controller code for local multiplayer

  • Responding when players added through C# events

    • Spawning players at different points on the map.

    • Toggling objects

  • Using Cinemachince with split-screen.

We are NOT going to look at creating a player selection screen or menu. But! That is very possible with the system and could be a topic of a future tutorial. There is also a bit of crudeness to how Unity splits the screen. It initially splits left/right not up/down. Also if the split-screen sizes don’t fill the screen which means, for example, 3 player screens will not cover the entire screen. To fix these issues would require some customization that’s beyond the scope of this tutorial.

I’ll be using the character controller from my “Third Person Controller” video for this tutorial. Although any character controller (even just a jumping cube) using the new input system should work equally well. You can find the code for the Third Person Controller here and the code for this tutorial here.

Split Screen in Action

So What is ACTUALLY Happening?

There is a lot going on behind the scenes to create split-screen functionality most of which is handled by two components - Player Input Manager and Player Input. Both of these components ship with the New Input System. While these classes are not simple - 700 and 2000 (!!) lines respectively - the end result is pretty straightforward and relatively easy to use.

The Player Input Manager detects when a button on a new device (keyboard, gamepad, etc) is pressed. When that happens an object with a Player Input component is instantiated. The Player Input creates an instance of an Input Action Asset and assigns the device to that instance.

The object that is instantiated could be the player object but in reality, it’s just holding a reference to the Input Action Asset (via the Player Input component) for a given player and device. So if you do want to allow players to select their character, or perform some other action before jumping into the game, you could connect the character selection UI elements to the Input Action Asset and then when the player object is finally created you connect it to the Input Action Asset. This becomes easier if you create additional action maps - one for selection and one for in-game action.

The Basics

To get things started you’ll need to add in the New Input System through the Unity Package Manager. If you haven’t played with the New Input System, definitely check out the earlier post and video covering the basics.

Here’s what needs to happen:

  1. Add the New Input System to your project

  2. Create an Input Action Asset. (Saved it and generate C# class)

  3. Add the Player Input Manager component to a scene object.

  4. Create a “player” prefab and add the Player Input component.

  5. Assign the Input Action Asset to the Player Input component.

  6. Assign the player prefab to the Player Input Manager component.

With that done, kick Unity into play mode and press a button on your keyboard or mouse. You should see a character prefab get instantiated. If you have a controller press a button on it and another prefab should be created.

In some cases, I have seen Unity treat multiple devices all as one. This occurred when I connected the devices before setting up the Player Input Manager. For me, a quick restart of Unity resolved this issue.

A Little Refinement

I have had some issues with Unity detecting the mouse and keyboard as separate devices. One way to resolve this is by defining control schemes, but I haven’t found the secret sauce to make that work smoothly and consistently. Another way around this is in the Player Input Manager is to set “Join Behavior” to “Join Players When Join Action Is Triggered" and to create a Join action in the Input Action Asset. I set the join action to “any key” on the keyboard and the “start” button on a gamepad.

If you want your players to all play with the same camera, i.e. all have the same view for a co-op style game, then much of the next section can be skipped.

Adding Split Screen

If you want each player to have their own camera for example, in an FPS, the next step is to make sure that the player prefab has a camera component - this is important so that when each player object is instantiated it has it’s own camera object.

The structure of my Player PRefab

In my case, the camera and the player object need to be separate objects and I’d guess this is true for many games. To make this work, simply create an empty object and make the camera and player objects children of the empty. Then create a new prefab from the empty object (with attached children) and reassign this prefab to the Player Input Manager. The Player Input component (in my experience) can go on any object on the prefab - so put it where it makes the most sense to you - I kept mine on the player object itself rather than on the empty parent.

You may have noticed that the Player Input component has a camera object. So on the prefab, assign the camera to the slot. This is needed so the split-screen can be set up correctly.

The last step before testing is to click the “split screen” toggle on the Player Input Manager. If you are using Cinemachine for your camera control, you should still get split-screen functionality, but all the views are likely looking through the same camera. We’ll fix that in a bit.

Connection to the Input Action Asset

Old code is commented out. New code is directly below.

If you’ve been playing around with a player object that has a controller component you may have noticed that all the players are still being controlled by a single device - even if you have split-screen working.

To fix this we need that controller component to reference the Input Action Asset on the Player Input component. To do that we need to change the type of our Input Action Asset from whatever specific type you’ve created, in my case “Third Person Action Asset,” to the more general “Input Action Asset.” We can then get a reference to the Player Input component with GetComponent or GetComponentInChildren depending on the structure and location of your components. To access the actual Input Action Asset we need to add a “dot Actions” to the end.

Now for the messy bit. Since there is no way to know what type of Input Action Asset we’ve created we need to find the Action Maps and individual actions using strings. Yuck. But it works.

We can get references to action maps using FindActionsMaps and references to actions using FindActions. Take care to spell the names correctly and with the correct capitalization. And this is all we need to do. Update the references to the Input Action Asset, Action Maps, and Actions and the rest of your controller code can stay the same.

Give it a quick test and each player object should now be controlled by a unique device.

Reacting to Players Joining

If you want to control where players spawn or maybe turn off a scene overview camera once the first player spawns we’re going to need to add in a bit more functionality. Unity gives us an OnPlayerJoin (and OnPlayerLeft) action that we can subscribe to and allows us to do stuff when a player joins. In addition, the OnPlayerJoin Action sends a reference to the PlayerInput component - which turns out to be very useful.

To make use of this action, we need to change the “Notification Behavior” on the Player Input Manager to “Invoke C Sharp Events.” Unity won’t throw errors if this isn’t set correctly, but the actions won’t get invoked.

Spawn Locations

To demonstrate how to control where players spawn, let’s create a new PlayerManager class. This class will need access to UnityEngine.InputSystem so make sure to add that using statement to the top. The first task is to get a reference to the PlayerInputManager component and I’ve done that with FindObjectOfType. We can then subscribe and unsubscribe from the OnPlayerJoin action. In my case, I’ve subscribed an “AddPlayer” function that takes in the PlayerInput component.

There are several ways to make this work, but I choose to create a list of the PlayerInput components - effectively keeping a reference to all the spawned players - as well as a list of transforms that will function as in-game spawn points. These spawn points can be anything, but I used empty gameObjects.

When a player joins, I add the PlayerInput component to the list and then set the position of the player object to the corresponding transform’s position in the spawn list. I’ve kept it simple, so that player 1 always spawns in the first location, player 2 in the second location, and so on.

Because of the structure of my player prefab, I am setting the position of the parent not the character object. My player input component is also not on the prefab root object. So your code may look a bit different if your prefab is structured differently.

Toggling Objects on Player Join

If the only camera objects in your scene are part of the player objects that means that players see a black screen until the first player joins. Which is fine for testing, but isn’t exactly polished.

A quick way to fix this is to add a camera to the scene and attach a component that will toggle the camera off when a player joins. You could leave the camera on, but this would make the computer work harder than it needs to as it’s having to do an additional and unseen rendering.

So just like above when controlling the spawn location, we need a new component that has access to the Input System and will subscribe to the OnPlayerJoin action. Then we just need a simple function, subscribed to the action, that will toggle the gameObject off. Couldn’t be simpler.

This of course can be extended and used in as many systems as you need. Play a sound effect, update UI, whatever.

Cinemachine!

If you are using more than one camera with Cinemachine it’s going to take a bit more work. We need to get each virtual camera working with the corresponding Cinemachine Brain. This is done by putting the virtual camera on a specific layer and then setting the camera’s culling mask accordingly.

The first step is to create new layers - one for each possible player. In my case, I’ve set the player limit in the Player Input Manager component to 4 and I’ve created four layers called Player1 through Player4.

To make this easier, or really just a bit less error-prone once set up, I‘ve added a list of layer masks to the Player Manager component. One layer mask for each player that can be added. The value for the layer masks can then be set in the inspector - nice and easy.

Same Add Player Function from above

Then comes the ugly part. Layer masks are bit masks and layers are integers. Ugh. I’m sure there are other ways to do this but our first step is to convert our player layer mask (bitmask) to a layer (integer). So in our Player Manager component and in the Add Player function, we do the conversion with a base 2 logarithm - think powers of 2 and binary.

Next, we need to get references to the camera and virtual camera. In my case the Player Input component (which is what we get a reference to from the OnPlayerJoin action) is not on the parent object, so I first need to get a reference to the parent transform and then search for the CinemachineFreeLook and Camera components in the children. If you are using a different virtual camera you’ll need to search for the type you are using.

Once we have reference to the Cinemachine Virtual Camera component we can set the gameObject layer to the layer integer value we created above.

Go to 9:00 on the video for bitwise operations.

For the camera’s culling mask it’s a bit more work as we don’t want to just set the layer mask we need to add our player layer to the mask. This gets done with the black magic that is bitwise operations. Code Monkey has a pretty decent video explaining some of how this works (go to the 9:00 mark) albeit in a slightly different context.

If everything is set up correctly, we should be able to test our code and have each Cinemachine camera looking at the correct player.

But! You might still see an issue - depending on your camera and how it’s being controlled.

Cinemachine Input Handler

If you are using a Cinemachine Input Handler to control your camera you are likely still seeing all the cameras controlled by one device. This is because the Cinemachine Input Handler is using an Input Action Reference which connects to the Input Action Asset - the scriptable object version - not the instance of the Input Action Asset in the Player Input component. (You’ve got to love the naming…)

To fix this we are going to create our own Input Handler - so we’ll copy and modify the “Get Axis Value” function from the original Cinemachine Input Handler. This function takes in an integer related to an axis and returns a float value from the corresponding action.

Note that this component implements the IInputAxisProvider interface. This is what the Cinemachine virtual camera looks for to get the input.

Replace the Cinemachine Input Handler with this new component and you should be good to go.

Read More
Jeremy Wolf Jeremy Wolf

(Better) Object Pooling

Why Reinvent?

Object Pooling 1.0

Yes, I’ve made videos on Object Pooling already. Yes, there a written post on it too. Does the internet really need another Object Pooling post. No, not really. But I wanted something a bit slicker and easier to use. I wanted a system with:

  1. No Object Pool manager.

  2. Objects can return themselves without needing to “find” the Object Pool.

  3. Can store a reference to a component, not just the gameObject.

  4. An interface to make the initialization of objects easy and consistent.

Easily the best video on generics I’ve ever made.

So after a lot of staring at my computer and plenty of false starts in the wrong direction, I came up with what I’m calling Object Pool 2.0. It makes use of generics, interfaces, and actions. So it’s not the easiest to understand object pooling solution but it works. It’s clean and easier to use than my past solutions. I like it.

If you’re just here for the code. You can get it on github. But you should definitely skim a bit further to see how it’s implemented.

If you’re asking why not just use the Unity 2021 object pool solution. Well, I’m scared of Unity 2021 (at this point in time) AND I’ve seen some suggestions that it’s not quite ready for prime time. Plus my solution has some features that Unity’s doesn’t and creating an object pooling solution isn’t hard.

Implementation

Maybe this seems backward. Maybe it is? But I think in the case of this object pool solution, it makes sense to show how it’s implemented before going into the gory details. It’s a reasonably abstract solution and that can make it difficult to wrap your head around. So let’s start with how to use it. Then we’ll get to how it works.

First, there needs to be an object that is responsible for spawning the objects. This object owns the pool and needs a reference to the object being pooled - in the case shown I’m using a gameObject prefab. The spawner object then creates an instance of the object pool and sends a reference to the prefab to the pool so it knows what object it is storing and what to create if it runs out and more are being asked for. To get an object from the pool, we simply need to call Pull.

Spawning object with the Object Pool

Just Slap on the Pool Object component and you’re good to go.

The objects being stored also need some logic to work with the pool. The easiest way to attach that logic is to slap on the Pool Object component to the object prefab. This default component will return the object to the object pool when the object is disabled.

Do you see why I like this solution? Now on to the harder part. Maybe even the fun part. Let’s look at how it works.

A Couple Interfaces

To get things started, I created two interfaces. The first one could be useful if I ever have a need to pool non-monobehaviour objects - but is admittedly not 100% necessary at the moment.The second interface, however, is definitely useful and is a big part of this system working smoothly.

But let’s start with the first interface which helps define the object pool. It has just two functions, a push and a pull. It is a generic interface, where the generic parameter is the type of object that will be stored. This works nicely as then our push and pull functions know what types they will be handling

Is this strictly necessary? Probably not.

The second interface is used to define objects that can be in the object pool. When used as intended the object pool can only contain types that implement the IPoolable interface.

This interface has an initialize function that takes in an Action. This action will get set in the object pool and is intended to be the function that returns the object to the pool. This action is then invoked inside of the ReturnToPool function.

If that doesn’t all make sense. Well, that’s reasonable. It can feel a bit circular. Let’s hope that’s not still the case by the time we get finished.

Creating the Pool

Let’s next take a look at the Object Pool definition - or at least the definition I’m using for monobehaviours. The Object pool itself has a generic parameter T and implements the IPool interface. T is then constrained to be a monobehaviour that must also implement the IPoolable interface.

Next, come the variables and properties for the object pool.

First up are two optional actions. These actions can be assigned in a constructor. This allows you to call a function (or multiple functions) EVERY time an object is pulled out of the pool or pushed back to the pool. This could be used to play SFX, increment a score counter or just about anything. It seemed useful so I stuck it in there.

Next is the stack (first in first out collection) that holds all the pooled objects.

Since we know the object being stored is a component we also know it’s attached to a gameObject. It’s this gameObject that will be instantiated if and when the pool runs out of objects in the stack.

Lastly, I added a property to count the number of objects in the pool. I stole this directly from the Unity object pool solution. I haven’t found a use for it yet, but maybe at some point.

Constructors

When we create a pool we need to tell it what object it will store and I think the easiest and best way to do that is to inject the object (prefab) using a constructor. In some cases, it’s also nice to pre-fill the pool. So the first constructor (and easily added to the second) takes in a number of objects to pre-spawn using the Spawn function.

The second constructor takes in the prefab as well as references for the pullObject and pushObject actions.

Push and Pull

The pull function is called whenever an object from the pool is needed.

First, we check if there are objects in the pool, if there are we pop one out. The gameObject is then set to active and the initialize function on the IPoolable object is called. Notice here that we are providing a reference to the Push function. This is the secret sauce.

This push function is the function used to return an object to the pool. This means the spawned object has a reference to this function and can return itself to the pool. We’ll take a closer look at how this happens later.

We then check if the pullObject action was assigned and if it was we invoke it and pass in the object being spawned.

Finally! We return the object so that whatever object asked for it, can have a reference to it.

The push function is pretty simple. It takes in the object and pushes it onto the stack. It then checks if the pushObject action was assigned and invokes it if it was. Lastly, the gameObject is turned off.

As a side note. the turning on and off of the object in the pull and push functions is not 100% needed, but is there to ensure the object is toggled on and off correctly and to help keep the initialize functions clean.

Poolable Objects

Every object that can go into this pool needs to implement the IPoolable interface. Now in some cases, you might want to implement the interface specifically for a given class.

Both as an example of how to implement the interface and also to provide an easy to use and reusable solution I created the PoolObject class. This component can simply be added to any prefab to allow that prefab to work with the object pool.

When implementing the IPoolable interface we should set the generic parameter to the class that is implementing the interface - PoolObject in the example.

The class will also need an action to store a reference to the push function. The value of this action is set in the initialize function - which was called in the Pull function of the object pool.

The ReturnToPool function, in this example, is called in the OnDisable function. This means all we need to do to return the object to the pool is turn the object off! Inside the function, we check if the returnToPool action has a value and if so, we invoke the action and pass in a reference to the object being sent to the object pool.

Overloads

To make the object pool a bit more useful and user friendly I also added several overloads for the Pull function. These allow the position and rotation of the object to be set when pulling it.

I also created functions that return a gameObject, as in some cases this is what is really needed and not the poolable object component.

One More Example

Since actions (and delegates) can be confusing I thought I’d toss in one more example - that of using the second constructor and assigning functions that will be called EVERY time an object is pushed or pulled from the instance of the object pool.

In this example, I’ve added the CallOnPull and CallOnPush functions. Notice that they must have the input type that is being stored in the object pool. Again the idea here is that these functions could trigger an animation, SFX, a UI counter, just about anything.

And that’s it. It’s an abstract solution but actually pretty simple (note that simple is not the same as easy). That’s both why it took a while to create and why I like it.

Read More
Jeremy Wolf Jeremy Wolf

Designing a New Game - My Process

Some of my projects….

This is my process. It’s been refined over 8+ years of tinkering with Unity, 2 game jams, and 2 games published to Steam.

My goal with this post is just to share. Share what I’ve learned and share how I am designing my next project. My goal is not to suggest that I’ve found the golden ticket. Cause I haven’t. I’m pretty sure the perfect design process does not exist.

So these are my thoughts. These are the questions I ask myself as I stumble along in the process of designing a project. Maybe this post will be helpful. Maybe it won’t. If it feels long-winded. It probably is.

I’ve tried just opening Unity and designing as I go. It didn’t work out well. So again, this is just me sharing.

TL;DR

  • Set a Goal - To Learn? For fun? To sell?

  • Play games as research - Play small games and take notes.

  • Prototypes system - What don’t you know how to build? Is X or Y actually doable or fun?

  • Culling - What takes too long? What’s too hard? What is too complicated?

  • Plan - Do the hard work and plan the game. Big and small mechanics. Art. Major systems.

  • Minimal Viable Product - Not the game just the basics. Is it fun? How long did it take?

  • Build it! - The hardest part. Also the most rewarding.

What Is The Goal?

When starting a new project, I first think about the goal for the project. For me, this is THE key step in designing a project - which is a necessary step to the holy grail of actually FINISHING a project. EVERY other step and decision in the process should reflect back on the goal or should be seen through the lens of that goal. If the design choice doesn’t help to reach the goal, then I need to make a different decision.

Am I making a game to share with friends? Am I creating a tech demo to learn a process or technique? Am I wanting to add to my portfolio of work? What is the time frame? Weeks? Months? Maybe a year or two (scary)?

I want another title in this list!

For this next project, I want to add another game to the OWS Steam library and I’d like to generate some income in the process. I have no dreams of creating the next big hit, but if I could sell 1000 or 10,000 copies - that would be awesome.

I also want to do it somewhat quickly. Ideally, I could have the project done in 6 to 9 months, but 12 to 18 months is more likely with the time I can realistically devote to the project. One thing I do know, is that whatever amount of time I think it’ll take. It’ll likely take double.

Research!

After setting a goal, the next step is research. Always research. And yes. I mean playing games! I look for games that are of a similar scope to what I think I can make. Little games. Games with interesting or unique mechanics. Games made by individuals or MAYBE a team of 2 or 3. As I play I ask myself questions:

What elements do I find fun? What aspects do I not enjoy? Do I want to keep playing? What is making me want to quit? What mechanics or ideas can I steal? What systems do I know or not know how to make? Which systems are complex? What might be easy to add?

Then there are three more questions. These are key and crucial in designing a game and can help to keep the game scope (somewhat) in check. Which in turn is necessary if a game is going to get finished

How did a game developer’s clever design decisions simplify the design? How does a game make something fun without being complex? Why might the developer have made decisions X or Y? What problems did that decision avoid?

These last questions are tough and often have subtle answers. They take thought and intention. Often while designing a game my mind goes towards complexity. Making things bigger and more detailed! Can’t solve problem A? Well, lets bolt-on solution B!

For example, I’ve wanted to make a game where the player can create or build the world. Why not let the player shape the landscape? Add mountains and rivers? Place buildings? Harvest resources? It would be so cool! Right? But it’s a huge time sink. Even worse, it’s complex and could easily be a huge source of bugs.

So a clever solution? I like how I’m calling myself clever. Hex tiles. Yes. Hex tiles. Let the player build the world, but do it on a grid with prefabs. Bam! Same result. Same mechanic. Much simpler solution. It trades a pile of complex code for time spent in Blender designing tiles. Both Zen World and Dorf Romantic are great examples of allowing the player to create the world and doing so without undue complexity.

Navigation can be another tough nut to crack. Issues and bugs pop up all over the place. Units running into each other. Different movement costs. Obstacles. How about navigation in a procedural landscape? Not to mention performance can be an issue with a large number of units.

My “Research” List

Creeper World 4 gets around this in such a simple and elegant way. Have all the units fly in straight lines. Hover. Move. Land. Done.

I am a big believer that constraints can foster creativity. For me, identifying what I can’t do is more important than identifying what I can do.

When I was building Fracture the Flag I wanted the players to be able to claim territory. At first, I wanted to break the map up into regions - something like the Risk map. I struggled with it for a while. One dead end after another. I couldn’t figure out a good solution.

Then I asked, why define the regions? Instead, let the players place flags around the map to claim territory! If a flag gets knocked down the player loses that territory. Want to know if a player can build at position X or Y? They can if it’s close to a flag. So many problems solved. So much simpler and frankly so much more fun.

With research comes a flood of ideas. And it’s crucial to write them down. Grab a notebook. Open a google doc. Or as I recently discovered Google Keep - it’s super lightweight and easy to access on mobile for those ah-ha moments.

I keep track of big picture game ideas as well as smaller mechanics that I find interesting. I don’t limit myself to one idea or things that might nicely fit together. This is the throwing spaghetti at the wall stage of design. I’m throwing it out there and seeing what sticks. Even if, maybe especially if, I get excited about one idea I force myself to think beyond it and come up with multiple concepts and ideas. This is not the time to hyper focus.

At this stage, I also have to bring in a dose of reality. I’m not making an MMO or the next E-Sports tile. I’m dreaming big, but also trying not to waste my time with completely unrealistic dreams. I should probably know how to make at least 70, 80 or maybe 90 percent of the game!

While you’re playing games as “research” support small developers and leave them reviews! Use those reviews to process what you like and what you don’t like. What would you change? What would you keep? What feels good? What would feel better? Those reviews are so crucial to a developer. Yes, even negative ones are helpful.

Prototype Systems - Not The Game

At this point in the process, I get to start scratching the itch to build. Up until now, Unity hasn’t been opened. I’ve had to fight the urge, but it’s been for the best. Until now.

Now I get to prototype systems. Not a game or the game. Just parts of a potential game. This is when I start to explore systems that I haven’t made before or systems I don’t know how to make. I focus on parts that seem tricky or will be core to the game. I want to figure out the viability of an idea or concept.

At this stage, I dive into different research. Not playing games, but watching and reading tutorials and articles. I take notes. Lots of notes. For me, this is like going back to school. I need to learn how other people have created systems or mechanics. Why re-invent the wheel? Sometimes you need to roll your own solution, but why not at least see how other folks have done it first?

If I find a tutorial that feels too complex. I look for another. If that still feels wrong, I start to question the mechanic itself.

Maybe it’s beyond my skill set? Maybe it’s too complex for a guy doing this in his spare time? Or maybe I just need to slow down and read more carefully?

Some prototype Art for a possible Hex tile Game

Understanding and implementing a hex tile system was very much all of the above. Red Blob Games has an excellent guide to hex grids with all the math and examples of code to implement hex grids into your games. It’s not easy. Not even close. But it was fun to learn and with a healthy dose of effort, it’s understandable. (To help cement my understanding, I may do a series of videos on hex grids.)

This stage is also a chance to evaluate systems to see if they could be the basis of a game. I’ve been intrigued by ecosystems and evolution for a long while. Equilinox is a great example of a fairly recent ecosystem-based game made by a single (skilled) individual. Sebastian Lague put together an interesting video on evolution, which was inspired by the Primer videos. All of these made me want to explore the underlying mechanics.

So, I spent a day or two writing code, testing mechanics, and had some fun but ultimately decided it was too fiddly and too hard to base a game on. So I moved on, but it wasn’t a waste of time!

After each prototype is functional, but not polished, I ask myself more questions.

Does the system work? Is the system janky? What parts are missing or still need to be created? Is it too complex or hard to balance? Is there too much content to create? Or maybe it’s just crap?

For me, it’s also important that I’m not trying to integrate different system prototypes (at this point). Not yet. I for sure want to avoid coupling and keep things encapsulated, but I also don’t want to go down a giant rabbit hole. That time may come, but it’s not now. I’m also not trying to polish the prototypes. I want the systems to work and be reasonably robust, but at this point, I don’t even know if the systems will be in a game so I don’t want to waste time.

(Pre-Planning) Let The Culling Begin!

With prototypes of systems built, it’s now time to start chopping out the fluff, the junk, and start to give some shape to a game design. And yes, I start asking more questions.

What are the major systems of the game? What systems are easy or hard to make? Are there still systems I don’t know how to make? What do I still need to learn? What will be the singular core mechanic of the game?

And here’s a crucial question!

What are the time sinks? Even if I know how to do X or Y will it take too long?

3D Models, UI, art, animations, quests, stories, multiplayer, AI…. Basically, everything is a time sink. But!

Which ones play to my strengths? Which ones help me reach my goal? Which ones can I design around or ignore completely? What time sinks can be tossed out and still have a fun game?

Assets I Use

When I start asking these questions it’s easy to fall into the trap of using 3rd party assets to solve my design problems or fill in my lack of knowledge. It’s easy to use too many or use the wrong ones. I need to be very picky about what I use. Doubly so with assets that are used at runtime (as opposed to editor tools). For me, assets need to work out of the box AND work independently. If my 3rd party inventory system needs to talk to my 3rd party quest system which needs to talk to my 3rd party dialogue system I am asking for trouble and I will likely find it.

The asset store is full of shiny objects and rat holes. It’s worth a lot of time to think about what you really need from the asset store.

What you can create on your own? What should you NOT create on your own? What you can design around? Do you really need X or Y?

For me, simple is almost always better. If I do use 3rd party assets, and I do, they need to be part of the prototyping stage. I read the documentation and try to answer as many questions as I can before integrating the asset into my project. If the asset can’t do what I need, then I may have to make hard decisions amount the asset, my design, or even the game as a whole.

I constantly have to remind myself that games aren’t fun because they’re complex. Or at the very least, complexity does not equal fun. What makes games fun is something far more subtle. Complexity is a rat hole. A shiny object.

Deep Breath. Pause. Think.

At this point, I have a rough sketch in my head of the game and it’s easy to get excited and jump into building with both feet. But! I need to stop. Breathe. And think.

Does the game match my goals? Can I actually make the game? Are there mechanics that should be thrown out? Can I simplify the game and still reach my goal? Is this idea truly viable?

Depending on the answers, I might need be to go back and prototype, do more research, or scrap the entire design and start with something a single guy can actually make.

This point is a tipping point. I can slow down and potentially re-design the game or spend the next 6 months discovering my mistakes. Or worse, ignoring my mistakes and wasting even more time as I stick my head in the sand and insist I can build the game. I’ve been there. I’ve done that. And it wasn’t fun.

Now We Plan

Maybe a third of the items on my to do list for Grub Gauntlet

Ha! I bet you thought I was done planning. Not even close. I haven’t even really started.

There are a lot of opinions about the best planning tool. For me, I like Notion. Others like Milanote or just a simple google doc. The tool doesn’t matters, it’s the process. So pick what works for you and don’t spend too much time trying to find the “best” tool. There’s a poop ton of work to do, don’t waste time.

Finding the right level of detail in planning is tough and definitely not a waste of time. I’m not creating some 100+ page Game Design Document. Rather I think of what I'm creating as a to-do list. Big tasks. Small tasks. Medium tasks. I want to plan out all the major systems, all the art, and all the content. This is my chance to think through the game as a whole before sinking 100’s or likely 1000’s of hours into the project.

To some extent, the resulting document forms a contract with myself and helps prevent feature creep. The plan also helps when I’m tired or don’t know what to do next. I can pull up my list and tackle something small or something interesting.

Somewhere in the planning process, I need to decide on a theme or skin for the game. The naming of classes or objects may depend on the theme AND more importantly, some of the mechanics may be easier or harder to implement depending on the theme. For example, Creeper World 4’s flying tanks totally work in the sci-fi-themed world. Not so much if they were flying catapults or swordsmen in a fantasy world. Need to resupply units? Creeper World sends the resources over power lines. Again, way easier than an animated 3D model of a worker using a navigation system to run from point A to point B and back again.

Does the theme match the mechanics? Does it match my skillset? Can I make that style of art? Does the theme help reach the goal? Does the theme simplify mechanics or make them more complex?

Minimum Viable Product (MVP)

Upgrade that

Knowlegde

Finally! Now I get to start building the project structure, writing code, and bringing in some art. But! I’m still not building the game. I’m still testing. I want to get something playable as fast as possible. I need to answer the questions:

Is the game fun? Have I over-scoped the game? Can I actually build it with my current skills and available time?

If I spent 3 months working on an inventory system and all I can do is collect bits on a terrain and sell them to a store. I’ve over-scoped the game. If the game is tedious and not fun then I either need to scrap the game or dig deeper into the design and try to fix it. If the game breaks every time I add something or change a system then I need to rethink the architecture or maybe the scope of the game or upgrade my programming knowledge and skill set.

If I can create the MVP in less than a month and it’s fun then I’m on to something good!

Why so short a time frame? My last project, Grub Gauntlet was created during a 48-hour game jam. I spent roughly 20 hours during that time to essentially create an MVP. It then took another 10 months to release! I figure the MVP is somewhere around 1/10th or 1/20th of the total build time.

It’s way better to lose 1-2 months building, testing, and then decide to scrap the project than to spend 1-2 years building a pile of crap. Or worse! Spend years working only to give up without a finished product.

Can I Build It Now?

This is the part we’re all excited about. Now I get to build, polish, and finish a game. There’s no secret sauce. This part is the hardest. It’s the longest. It’s the most discouraging. It’s also the most rewarding.

If I’ve done my work ahead of time then I should be able to finish my project. And that? That is an amazing feeling!

Read More

Older Posts