When In Doubt, Split It Out
Table of contents
No headings in the article.
Indie game development requires you to wear many different hats. I wish someone had told me earlier on how critical it was to keep each aspect of your game separated out more under these various "hats." I want to detail specific cases in this post where you can stay "organized" within your project so you can hopefully avoid the same roadblocks I unintentionally created for myself.
Why Should I Focus More On "Split Logic?"
Essentially, when you keep everything in your game deeply integrated, you set up a difficult path for developing it further. I've found that game complexities often result in the following:
- A struggle to extend existing systems in your game
- Twisted pipelines for adding new content
- Frustration and fleeting motivation
In a general view, these complexities can be avoided when you dedicate time to address them early on in your project. It may seem like you're spending unnecessary time upfront, but when these considerations are cut, they run the risk of taking up more time in trying to unravel what's already been developed.
So, What Are We Splitting Out Exactly?
The three areas include:
- Code logic
- Subsystems and critical features
- Project items, such as scripts, assets, etc…
With this, let's dive into the examples and bits of advice for each of these areas!
When reading through indie game dev forums before starting my first game, I constantly saw the advice of "nothing else matters beyond creating the initial proof of concept/demo for your game." I certainly agree with the statement, but my understanding of it fell flat during the first few implementations.
In a well-intended pursuit to complete the core aspects of my game, I found myself hard-coding a lot of it. I was able to get the bare bones completed after a few weeks, but quickly reached a roadblock when wanting to extend features. For instance, my game involves collecting resources - nothing novel for most games, but much of it was hard-coded in the scripts for which tiles drop a given item. When I wanted to add basic content like more types of tiles and more resources, it involved digging through multiple scripts with vague comments in an attempt to make the change.
For advanced coders or those who have published games before, this seems like a "duh" moment, but for beginners it's super easy to get trapped here. Every feature you implement should be designed with the intent for easy expansion. It's 100% worth the extra time to design your systems in a way that content can be added without needing to dig several layers down into your game's code. This also goes with systems that involve repeated actions. This is more of a coding "best practice," but if a condition or action is repeated, break it down into separate functions.
The perfect example also comes with my inventory system, which required many more revisions than I care to admit! With collecting resources and moving items around in the inventory, I continually had to add and subtract quantities. Initially, this logic was embedded with larger functions that worked every time and I crossed it off my to-do list accordingly. When it came time to implement systems like questing though, I had no way of triggering these same actions externally. The quick act of making those actions of addition and subtraction into separate functions helped immensely in resolving the issue.
Even if you plan to never update your game past its release, I would strongly advise still coding it as though you were to update it years later. Afford yourself some flexibility when this is not possible, but look for opportunities to refactor your code in a way that allows you to stay in control of your game.
If you think a piece of your code can be split out, do it, and your future self will thank you! And when it comes to the game's data where the same values are continually referenced, place it at a smarter level such as with JSON, XML, databases, etc…
Subsystems and Critical Features
This directly continues after the previous point, in that subsystems are also code logic, but in this case I’m looking at the bigger picture for development. I found that Godot easily enabled this kind of development, but I'm sure it's achievable through most other engines as well. Consider the following example:
"I want to talk with NPC's, have dialogue, accept quests, gain rewards, and repeat - best game ever!"
This can easily become overwhelming, especially in the later stages where these features cross. Your quest may require collecting resources, and then you'll later receive rewards, and both of these interact with a larger inventory system. This means that although the scenario is written in a linear format, the actual execution is a giant, complex web of systems that work off one another.
Instead of coding from the beginning of the scenario and trying to chain features into one another, first break down all of the subsystems needed. In this case:
- NPC Controllers
- And so on…
Take one of these subsystems, and start implementing it independent of all other subsystems. Of course, you should still consider what features are needed for the subsystems to connect, but keep the actual development under one subsystem at a time. What you'll discover is that as you go from one subsystem to the next, you'll have a better initial idea of development and be able to connect each part more effectively.
With Godot, I achieve this by saving entire subsystems as separate scenes, and then instancing those where needed. Instead of having a hierarchy with 100's of nodes for each GUI element, I'll have bundles, such as:
- Player Controller
Under "Inventory-Backpack" I may have dozens of slot and text label nodes, but when viewing it from the "PlayerController," this complexity isn't visible upfront. And if I want to make changes to Inventory, rather than digging through a giant list of random elements under "PlayerController," I can simply navigate to the packaged "Inventory" scene to make any edits.
Splitting up subsystems and features provides both logical and visual benefits - I find now with this approach that I'm more excited to implement new features. I can develop separately where needed, and when changes are required, I can easily find where to make those.
This is much less technical, but also just as important, if not the key "split" you can make for your game. Organize everything, and I mean everything!!! Instead of dumping all your assets into one giant folder, experiment with different folder structures that make the most sense to you.
There's 2 different setups I would recommend trying out first:
- Categorize by asset format, so folders could include > Audio, Art, Fonts, Scripts, Scenes, etc…
- Under each of those folders, you can go more specific > Audio includes Music, Sounds, and Stingers, while Art includes Textures, Models, Atlases, and so on
- This makes navigation super easy for adding new assets - if you need to import a new sound bite you developed, you know exactly where to place it.
- Organize based on elements of your game, such as having a Character folder with each NPC getting their own respective folder - each Character folder would then have subfolders like Sounds, Animations, Data Files, etc…
- This would apply to most unique elements in your world, making it easy to locate resources for a specific aspect of your world - if you need to adjust the animation of an NPC, you could easily navigate to their respective subfolder
- Keep in mind that it may require additional upkeep in maintaining consistent organization inside each element, but it largely depends on project requirements as well
I tend to use a mix of these as my games grow in scope. I've often found that making the effort to reorganize your project files every so often not only keeps things clean, but provides a good boost of productivity for the day. Sometimes, it's difficult to stay motivated on your game, and organizing files helps you feel accomplished without doing a lot of deep, complex work.
The idea of "splitting" is essentially the separation of aspects of your game to help prevent development overhead and simplify complex systems. Between code logic, larger features, and project files, there's several ways to divide out your game into a more manageable state - it's absolutely worth the extra time invested upfront over the potential frustrations later on!