How to Organize a Unity Project

There’s a lot to learn when it comes to making a game in Unity: complex maths, Unity’s API, the component system, the testing framework… Even though all of these things can be difficult, there’s usually resources and documentation to help in wrapping your head around the concepts and how to use them. Most of the “technical” aspects of Unity are documented and you can find examples of most (if not all) of them.

However, there is one aspect which I’ve found confusing from the very beginning and for which I haven’t found a lot of documentation and clarification (the main reason I’m writing this post): how to structure, architect and organize your Unity project.


Background

Coming from the world of Functional Programming and Backend (I use Haskell in my day-to-day work), I’m used to working with pretty structured and well-defined architectural patterns (ex: MVC). These basic guidelines usually help in structuring your project, avoiding easy pitfalls and guide the overall design of whatever you’re coding. So, when it came to working on my game in Unity, I found it surprising how there wasn’t much in the way of suggestions, examples, frameworks or recommendations about this.

Yes, we all know about the basics of “directories” in Unity: how there are certain folder names that are reserved and “magically” allow you to do some things; but what about “organizing” your code? As I coded my game I progressively started to find that the OOP and Component-based nature of Unity makes it very easy to take your code down a more and more complicated path. You can easily find yourself splitting logic among multiple different Monobehaviours and classes, replicating functionality across your entire code-base and even having issues with the concept of “single source of truth”.

A couple weeks ago, I started working on a new feature (“building”, which generates structures that work as tile modifiers) for my game and I found that I had split logic so much that there was no easy way of modifying the value of a given tile. I decided to leave that aside for a bit and work on refactoring my entire code-base.

In this article I’ll go into details of what architecture I came up with for organizing and designing the code for my game. I’m not saying it’s a silver bullet or that it’s a one-size-fits-all kind of thing; however, I do think that the ideas exposed here might work for others and that the concepts at the core of my pattern are pretty universal, so I hope this helps.


Architectural Patterns

First, let’s review some of the most popular patterns out there and I’ll briefly explain why I decided not to use them as-is.

ECS

The Entity Component System (ECS) follows the principle of composition over inheritance, defining every entity by the components it uses rather than by its’ type hierarchy. Entities are generally thought-of as game objects that consists only of a unique ID, it holds no other data or functionality. Each entity then implements a number of components, which can be understood as “aspects” that the associated entity has; components can be things like “health”, “speed”, “mana”, etc; and hold the data needed to model that aspect (entities with a specific set of components are called archetypes). Systems act on entities with specific components, for example, a movement system might receive all entities with the “speed” component and move them at different rates based on that.

Being honest, I really liked the idea of following the ECS pattern for the game: I find it’s very clear, well defined and easy to understand. It also provides clear boundaries to what goes where and eliminates problems with inheritance/dependency hierarchies while also centralizing and clearly defining a SSoT. However, when deciding to follow this pattern I was faced with two options: either implement my own take on ECS using Unity’s GameObjects/Components (which had already lead me down the path I was finding myself in) or implement Unity’s new ECS system (which would require a pretty sizeable refactor of my entire codebase and/or starting from scratch). I finally decided to create my own pattern, basing it loosely in ECS. This would avoid the need for a massive refactor (or spending loads of time learning the new API) while trying to get some of the benefits of ECS.

MVC

Typically used in Backend engineering, the Model View Controller (MVC) pattern divides code into three categories with well defined boundaries and interactions. Views can be thought of as what a user sees and/or interacts with, interactions with views will trigger calls to controllers. Controllers typically contain the business logic for the software: they receive certain interaction from the views, perform any operations needed based on them and then call models to remove, retrieve or modify data. Models, as already stated, limit themselves to interacting with data, they do not hold any other logic themselves but, changes performed by models can trigger changes in views.

I’m incredibly used to working with this particular pattern and, initially, thought it would fit perfectly into my game. I like the idea of having a centralized place to interact with data and keeping views and logic separate from each other, however, after giving the design a couple of go’s and trying to fit all the different kinds of components into it, I quickly realized that wouldn’t be the case. I spent some time looking through the internet and quickly realized most people arrived to the same conclusion. While there were a lot of people saying to “adapt it” to your needs and/or “not think of it so rigidly”, there weren’t many concrete examples of what those changes looked like (and I did want something more rigid and structured).

MVVM

Being honest, I’m not very familiar with the Model-View-ViewModel (MVVM) pattern but I saw it mentioned as an alternative quite a few times while reading about MVC for game development. In general, it’s seems similar to an MVC pattern, having Models be non-visual classes that encapsulate data, Views which represent what a user sees on screen and ViewModels which work as an in-between (like the “C” in MVC). One of the key differences seems to be the flow of interactions/data: rather than having views be based directly on models/controllers; Views only interact with ViewModels (Views bind to data and execute commands in ViewModels and ViewModels send notifications to Views for updates) and ViewModels interact with Models (ViewModels update the Models and Models send notifications to ViewModels).

While investigating this, I found the same potential pitfalls I already saw with MVC (as well as, again, not finding many examples of game development code).


HAMES

Combining all the options mentioned above, I came up with my own pattern which I call HAMES (pronounced /(heɪmz)/ because I found the definition funny): Handler, Archetype, Model, Entity, System. This pattern is based around the event pattern and tries to keep things as separate as possible, keeping flow of interactions in one direction exclusively and minimizing inter-dependencies (it doesn’t strictly enforce composition over inheritance but it does limit derived classes quite a bit). In the following sections I’ll explain each of the components of the pattern.

Archetypes

An archetype is defined as “a very typical example of a certain person or thing”, as such, archetypes in HAMES are the base classes from which Entities derive. They are generally abstract classes (though they could easily be interfaces) and contain the logic/data structure that all entities deriving from them will use. All archetypes provide factories for generating entities of their derived classes. Example:

The ActionArchetype is an abstract class from which all Action Entities derive from. It provides the initialization logic for actions (setting up the executor, end/start position, energy cost, etc… of an action) as well as the ActionFactory which Action Entities will then use.

Entities

Entities are closer to “Components” in ECS than to actual “entities” (however, since in HAMES you never directly interact with objects, I liked the name better). Entities are concrete implementations of archetypes, they contain all logic/data needed by the entity itself. The idea here is that entities are in charge of their own data and provide the API for modifying and updating themselves. Entities can be broken down into two sub-classes: Game and System Entities.

Game Entities

Game Entities are completely self-contained entities. They typically represent concrete things in-game, derive from MonoBehaviour and should never interact with other models, systems, entities or handlers. Examples of game entities are: units in a strategy game, a car in a racing game, NPCs, etc…

System Entities

System Entities are dynamically created entities that shouldn’t have a representation in-game (don’t need to derive from MonoBehaviour). They are called System Entities because they can interact with other models directly (they are essentially dynamically generated systems). Example:

The DivideAction is a System Entity that derives from the ActionArchetype class and interacts with the SquadModel, LocationModel and GridModel to divide a given SquadEntity in two. It also provides an implementation of the ActionFactory for creating new instances of itself.

Models

Models are directly pulled off from the MVC and MVVM patterns. They should follow the single responsibility principle “to the T” and are the data layer of a game and allow for retrieving, removing, updating and adding one specific archetype. They don’t need to derive from MonoBehaviour and keep a reference/database/state of all entities of the specific archetype. Example:

The ActionModel uses the ActionFactory instance implemented by Action Entities to allow for the creation of new ActionArchetypes. It also keeps a list of all Action Entities currently in the game and provides an API for removing, retrieving and updating them.

Systems

Systems make use of models to accomplish a given functionality. They are the “business logic” layer of a game and expose functions in their API to accomplish specific functionalities. Systems should not keep a state (rellying instead in models for all their state needs), should limit their logic (ex: the PhaseSystem is in charge of executing Phases and only that), don’t need to derive from MonoBehaviour and should limit their interactions to models as much as possible (ie: they should try to avoid calling other systems). Example:

The PhaseSystem makes use of the ActionModel to retrieve all Action Entities in the game and perform their Execute method.

Handlers

Handlers are pulled straight from the event pattern. They combine multiple systems and - in my current implementation - make use of Unity’s InputSystem: receiving player inputs, parsing the InputAction.CallBackContext into the specific arguments required by System’s functions and calling said functions. Again, Handlers should not keep a state and should be divided by the source of the events that trigger them (ex: InputHandler receives events from input devices while the ButtonHandler receives events from UI Buttons). In order to interact with Unity’s InputSystem they most likely will need to derive from MonoBehaviour. Example:

The InputHandler receives the LeftClickDown event, pulls the mouse position from the InputAction.CallBackContext and calls PlanningSystem.SelectSquad and HighlightSystem.HighlightTilesInRange to select a given squad (if one exists in the current mouse position) and highlight the tiles reachable by said squad.

Where do GameObjects Fit In?

In HAMES, GameObjects are seen as the “View” in an MVC or MVVM pattern: they don’t contain any data or logic, they are simply what the user sees and interacts with. GameObjects should essentially be a sprite and a position (both of which can be updated from their associated Game Entities or Models). Keeping GameObjects separate from the data and logic layers allows for clean design, facilitates an easy to follow structure and simplifies coding/game development (since all logic lives in code rather than in the Unity editor).


Example Structure

What follows is an example folder structure for a Unity project that implements HAMES:

> Assets/
  > AddressablesAssetsData/
  > Library/
  > Src/
    > Archetypes/
      > Entities/
        > Game/
          > SquadArchetype.cs
          > StructureArchetype.cs
        > System/
          > ActionArchetype.cs
      > Enums/
        > ActionType.cs
        > UnitType.cs
      > Structs/
        > ActionStruct.cs
    > Entities/
      > Game/
        > SquadEntity.cs
        > Structures/
          > OffensiveStructureEntity.cs
          > DefensiveStructureEntity.cs
      > System/
        > Actions/
          > DivideActionEntity.cs
          > MovementActionEntity.cs
    > Handlers/
      > InputHandler.cs
      > ButtonHandler.cs
    > Models/
      > SquadModel.cs
      > ActionModel.cs
      > StructureModel.cs
      > GridModel.cs
      > TileModel.cs
    > Objects/
      > Squad.prefab
      > OffensiveStructure.prefab
      > DefensiveStructure.prefab
    > Systems/
      > PhaseSystem.cs
      > HighlightSystem.cs
      > PlanningSystem.cs
      > StartupSystem.cs
  > Tests/
    > IntegrationTests/
    > E2ETests/
> Packages/
> ProjectSettings/
> UserSettings/

That’s it!

I truly hope this was helpful, please let me know what you think of the HAMES pattern in the comments below. I’d love to get your feedback, ideas, suggestions and any issues you think it might have. Thanks for reading!

Next
Next

Of Hexagons and Databases