First time programmers usually start learning the trade with the classic Hello World program. From there, bigger and bigger assignments are bound to follow. Each new challenge drives home an important lesson:

The bigger the project, the bigger the spaghetti.

IMAGE: SPAGHETTI CODE

Soon, it is easy to see that in large or small teams, one cannot recklessly do as one pleases. Code must be maintained and may last for a long time. Companies you’ve worked for can’t just look up your contact information and ask you every time they want to fix or improve the codebase (and you don’t want them to either).

This is why software design patterns exist; they impose simple rules to dictate the overall structure of a software project. They help one or more programmers separate core pieces of a large project and organize them in a standardized way, eliminating confusion when some unfamiliar part of the codebase is encountered.

IMAGE: BAD PROGRAMMER GANDALF

These rules, when followed by everyone, allow legacy code to be better maintained and navigated, and new code to be added more swiftly. Less time is spent planning the methodology of development. Since problems don’t come in one flavor, there isn’t a silver bullet design pattern. One must carefully consider the strong and weak points of each pattern, and find the best fit for the challenge at hand.

In this tutorial I’ll relate my experience with the popular Unity game development platform and the Model-View-Controller (MVC) pattern for game development. In my seven years of development, having wrestled with my fair share of game dev spaghetti, I’ve been achieving great code structure and development speed using this design pattern.

I’ll start by explaining a bit of Unity’s base architecture, the Entity-Component pattern. Then I’ll move on to explain how MVC fits on top of it, and use a little mock project as example.

Motivation

In the literature of software we will find a great number of design patterns. Even though they have a set of rules, developers will usually do a little rule-bending in order to better adapt the pattern to their specific problem.

This “freedom of programming” is proof that we haven’t yet found a single, definitive method for designing software. Thus, this article isn’t meant to be the ultimate solution for your problem, but rather, to show the benefits and possibilities of two well known patterns: Entity-Component and Model-View-Controller.

The Entity-Component Pattern

Entity-Component (EC) is a design pattern where we first define the hierarchy of elements that make up the application (Entities), and later, we define the features and data each will contain (Components). In more “programmer” terms, an Entity can be an object with an array of 0 or more Components. Let’s depict an Entity like this:

some-entity [component0, component1, ...]

Here’s a simple example of an EC tree.

- app [Application]
   - game [Game]
      - player [KeyboardInput, Renderer]
      - enemies
         - spider [SpiderAI, Renderer]
         - ogre [OgreAI, Renderer]
      - ui [UI]
         - hud [HUD, MouseInput, Renderer]
         - pause-menu [PauseMenu, MouseInput, Renderer]
         - victory-modal [VictoryModal, MouseInput, Renderer]
         - defeat-modal [DefeatModal, MouseInput, Renderer]

EC is a good pattern for alleviating the problems of multiple inheritance, where a complex class structure can introduce problems like the diamond problem where a class D, inheriting two classes, B and C, with the same base class A, can introduce conflicts because how B and C modify A’s features differently.

IMAGE: DIAMOND PROBLEM

These kinds of problems can be common in game development where inheritance is often used extensively.

By breaking down the features and data handlers into smaller Components, they can be attached and reused in different Entities without relying on multiple inheritance (which, by the way, isn’t even a feature of C# or Javascript, the main languages used by Unity).

Where Entity-Component Falls Short

Being one level above OOP, EC helps to defragment and better organize your code architecture. However, in large projects we are still “too free” and we can find ourselves in a “feature ocean”, having a hard time finding the right Entities and Components, or figuring out how they should interact. There are infinite ways to assemble Entities and Components for a given task.

IMAGE: EC FEATURE OCEAN

One way to avoid a mess is to impose some additional guidelines on top of Entity-Component. For example, one way I like to think about software is to divide it up into three different categories:

  • Some handle the raw data, allowing it to be created, read, updated, deleted or searched (i.e., the CRUD concept).
  • Others implement the interface for other elements to interact with, detecting events related to their scope and triggering notifications when they occur.
  • Finally, some elements are responsible for receiving these notifications, making business logic decisions, and deciding how the data should be manipulated.

Fortunately, we already have a pattern that behaves in this exact way.

The Model-View-Controller (MVC) Pattern

The Model-View-Controller pattern (MVC) splits the software into three major components: Models (Data CRUD), Views (Interface/Detection) and Controllers (Decision/Action). MVC is flexible enough to be implemented even on top of ECS or OOP.

Game and UI development have the usual workflow of waiting for a user’s input, or other triggering condition, sending notification of those events somewhere appropriate, deciding what to do in response, and updating the data accordingly. These actions clearly show the compatibility of these applications with MVC.

This methodology introduces another abstraction layer that will help with the software planning, and also allow new programmers to navigate even in a bigger codebase. By splitting the thinking process into data, interface, and decisions, developers can reduce the number of source files that must be searched in order to add or fix functionality.

Unity and EC

Let’s first take a closer look at what Unity gives us up front.

Unity is an EC-based development platform, where all Entities are instances of GameObject and the features that makes them be “visible,” “moveable,” “interactable,” and so on, are provided by classes extending Component.

The Unity editor’s Hierarchy Panel and Inspector Panel provide a powerful way to assemble your application, attach Components, configure their initial state and bootstrap your game with a lot less source code than it would normally.

SCREENSHOT: HIERARCHY PANEL Hierarchy Panel with four GameObjects on the right

SCREENSHOT: INSPECTOR PANEL Inspector Panel with a GameObject’s components

Still, as we’ve discussed, we can hit the “too many features” problem and find ourselves in a gigantic hierarchy, with features scattered everywhere, making the life of a developer a lot harder.

Thinking in the MVC way, we can, instead, start by dividing things according to their function, structuring our application like the example below:

SCREENSHOT: UNITY MVC EXAMPLE STRUCTURE

Adapting MVC to a Game Development Environment

Now, I would like to introduce two small modifications to the generic MVC pattern, which help adapt it to unique situations I’ve come across building Unity projects with MVC:

  1. The MVC class references easily get scattered throughout the code.
    • Within Unity, developers typically must drag and drop instances around to make them accessible, or else reach them through cumbersome find statements like GetComponent( ... ).
    • Lost-reference hell will ensue if Unity crashes or some bug makes all the dragged references disappear.
    • This makes it necessary to have a single root reference object, through which all instances in the Application can be reached and recovered.
  2. Some elements encapsulate general functionality that should be highly reusable, and which does not naturally fall into one of the three main categories of Model, View, or Controller. These I like to call simply Components. They are also “Components” in the Entity-Component sense, but merely act as helpers in the MVC framework.
    • For example, a Rotator Component, which only rotates things by a given angular velocity and doesn’t notify, store, or decide anything.

To help alleviate these two issues, I came up with a modified pattern I call AMVCC, or Application-Model-View-Controller-Component.

IMAGE: AMVCC DIAGRAM

  • Application - Single entry point to your application and container of all critical instances and application-related data.
  • MVC - You should know this by now. :)
  • Component - Small, well-contained script that can be reused.

These two modifications have satisfied my needs for all projects I’ve used them in.

Example: 10 Bounces

As a simple example, let’s look at a small game called 10 Bounces, where I’ll make use of the core elements of the AMVCC pattern.

The game setup is simple: A Ball with a SphereCollider and a Rigidbody (which will start to fall after “Play”), a Cube as ground and 5 scripts to make up the AMVCC.

Hierarchy

Before scripting, I usually start at the hierarchy and create an outline of my class and assets. Always following this new AMVCC style.

SCREENSHOT: BUILDING THE HIERARCHY

As we can see, the view GameObject contains all visual elements and also ones with other View scripts. The model and controller GameObjects, for small projects, usually contain only their respective scripts. For bigger projects, they will contain GameObjects with more specific scripts.

When someone navigating your project wants to access:

  • Data: Go to application > model > ...
  • Logic/Workflow: Go to application > controller > ...
  • Rendering/Interface/Detection: Go to application > view > ...

If all teams follow these simple rules, legacy projects shouldn’t become a problem.

Note that there is no Component container because, as we’ve discussed, they are more flexible and can be attached to different elements at the developer’s leisure.

Scripting

Note: The scripts shown below are abstract versions of real-world implementations. A detailed implementation wouldn’t benefit the reader much. However, if you would like to explore more, here’s the link to my personal MVC framework for Unity, Unity MVC. You will find core classes that implement the AMVCC structural framework needed for most applications.

Let’s take a look at the structure of the scripts for 10 Bounces.

Before starting, for those not familiar with Unity’s workflow, let’s clarify briefly how scripts and GameObjects work together. In Unity, “Components,” in the Entity-Component sense, are represented by the MonoBehaviour class. For one to exist during runtime, the developer should either drag and drop its source file into a GameObject (which is the “Entity” of the Entity-Component pattern) or use the command AddComponent<YourMonobehaviour>(). After this, the script will be instantiated and ready to use during execution.

To begin, we define the Application class (the “A” in AMVCC), which will be the main class containing references to all instantiated game elements. We’ll also create a helper base class called Element, which gives us access to the instance of the Application and its children’s MVC instances.

With this in mind, let’s define the Application class (the “A” in AMVCC), which will have a unique instance. Inside it, three variables, model, view, and controller, will give us access points for all MVC instances during runtime. These variables should be MonoBehaviours with public references to the desired scripts.

Then, we’ll also create a helper base class called Element, which gives us access to the instance of the Application. This access will allow every MVC class to reach every other.

Note that both classes extend MonoBehaviour. They are “Components” that will be attached to GameObject “Entities”.

// BounceApplication.cs

// Base class for all elements in this application.
public class BounceElement : MonoBehaviour
{
   // Gives access to the application and all instances.
   public BounceApplication app { get { return GameObject.FindObjectOfType<BounceApplication>(); }}
}

// 10 Bounces Entry Point.
public class BounceApplication : MonoBehaviour
{
   // Reference to the root instances of the MVC.
   public BounceModel model;
   public BounceView view;
   public BounceController controller;

   // Init things here
   void Start() { }
}

From BounceElement we can create the MVC core classes. The BounceModel, BounceView, and BounceController scripts usually act as containers for more specialized instances, but since this is a simple example only the View will have a nested structure. The Model and Controller can be done in one script for each:

// BounceModel.cs

// Contains all data related to the app.
public class BounceModel : BounceElement
{
   // Data
   public int bounces;	
   public int winCondition;
}
// BounceView .cs

// Contains all views related to the app.
public class BounceView : BounceElement
{
   // Reference to the ball
   public BallView ball;
}
// BallView.cs

// Describes the Ball view and its features.
public class BallView : BounceElement
{
   // Only this is necessary. Physics is doing the rest of work.
   // Callback called upon collision.
   void OnCollisionEnter() { app.controller.OnBallGroundHit(); }
}
// BounceController.cs

// Controls the app workflow.
public class BounceController : BounceElement
{
   // Handles the ball hit event
   public void OnBallGroundHit()
   {
      app.model.bounces++;
      Debug.Log(“Bounce ”+app.model.bounce);
      if(app.model.bounces >= app.model.winCondition)
      {
         app.view.ball.enabled = false;
         app.view.ball.GetComponent<RigidBody>().isKinematic=true; // stops the ball
         OnGameComplete();
      }	
   }

   // Handles the win condition
   public void OnGameComplete() { Debug.Log(“Victory!!”); }
}

With all scripts created, we can proceed to attaching and configuring them.

The hierarchy layout should be like this:

- application [BounceApplication]
    - model [BounceModel]
    - controller [BounceController]
    - view [BounceView]
        - ...
        - ball [BallView]
        - ...

Using the BounceModel as an example, we can see how it looks in Unity’s editor:

SCREENSHOT: BounceModel IN INSPECTOR BounceModel with the bounces and winCondition fields.

With all scripts set and the game running, we should get this output in the Console Panel.

SCREENSHOT: CONSOLE OUTPUT

Notifications

As shown in the example above, when the ball hits the ground its view executes app.controller.OnBallGroundHit() which is a method. It isn’t, by any means, “wrong” to do that for all notifications in the application. However, in my experience, I’ve achieved better results using a simple notification system implemented in the AMVCC Application class.

To implement that, let’s update the layout of the BounceApplication to be:

// BounceApplication.cs

class BounceApplication 
{
   // Iterates all Controllers and delegates the notification data
   // This method can easily be found because every class is “BounceElement” and has an “app” 
   // instance.
   public void Notify(string p_event_path, Object p_target, params object[] p_data)
   {
      BounceController[] controller_list = GetAllControllers();
      foreach(BounceController c in controller_list)
      {
         c.OnNotification(p_event_path,p_target,p_data);
      }
   }

   // Fetches all scene Controllers.
   public BounceController[] GetAllControllers() { /* ... */ }
}

Next, we need a new script where all developers will add the notification event’s names, which can be dispatched during execution.

// BounceNotifications.cs

// This class will give static access to the events strings.
class BounceNotification
{
   static public string BallHitGround = “ball.hit.ground”;
   static public string GameComplete  = “game.complete”;
   /* ...  */
   static public string GameStart     = “game.start”;
   static public string SceneLoad     = “scene.load”;
   /* ... */
}

It is easy to see that, this way, the code legibility is improved because developers don’t need to search all over the source code for controller.OnSomethingComplexName methods in order understand what kind of actions can happen during execution. By only checking one file, it is possible to understand the overall behaviour of the application.

Now, we only need to adapt the BallView and BounceController to handle this new system.

// BallView.cs

// Describes the Ball view and its features.
public class BallView : BounceElement
{
   // Only this is necessary. Physics is doing the rest of work.
   // Callback called upon collision.
   void OnCollisionEnter() { app.Notify(BounceNotification.BallHitGround,this); }
}
// BounceController.cs

// Controls the app workflow.
public class BounceController : BounceElement
{
   // Handles the ball hit event
   public void OnNotification(string p_event_path,Object p_target,params object[] p_data)
   {
      switch(p_event_path)
      {
         case BounceNotification.BallHitGround:
            app.model.bounces++;
            Debug.Log(“Bounce ”+app.model.bounce);
            if(app.model.bounces >= app.model.winCondition)
            {
               app.view.ball.enabled = false;
               app.view.ball.GetComponent<RigidBody>().isKinematic=true; // stops the ball
               // Notify itself and other controllers possibly interested in the event
               app.Notify(BounceNotification.GameComplete,this);            
            }
         break;
         
         case BounceNotification.GameComplete:
            Debug.Log(“Victory!!”);
         break;
      }	
   }
}

Bigger projects will have a lot of notifications. So, to avoid getting a big switch-case structure, it is advisable to create different controllers and make them handle different notification scopes.

AMVCC in the Real World

This example has shown a simple use case for the AMVCC pattern. Adjusting your way of thinking in terms of the three elements of MVC, and learning to visualize the entities as an ordered hierarchy, are the skills that ought to be polished.

In bigger projects, developers will be faced with more complex scenarios and doubts about whether something should be a View or a Controller, or if a given class should be more thoroughly separated in smaller ones.

Rules of Thumb (by Eduardo)

There isn’t any “Universal Guide for MVC sorting” anywhere. But there are some simple rules that I typically follow to help me determine whether to define something as a Model, View, or Controller, and also when to split a given class in smaller pieces.

Usually this happens organically while I think about the software architecture or during scripting.

Class Sorting

Models

  • Hold the application’s core data and state, such as player health or gun ammo.
  • Serialize, deserialize, and/or convert between types.
  • Load/save data (locally or on the web).
  • Notify Controllers of the progress of operations.
  • Store the Game State for the Game’s Finite State Machine.
  • Never access Views.

Views

  • Can get data from Models in order to represent up-to-date game state to the user. For example, a View method player.Run() can internally use model.speed to manifest the player abilities.
  • Should never mutate Models.
  • Strictly implements the functionalities of its class. For example:
    • A PlayerView should not implement input detection or modify the Game State.
    • A View should act as a black box that has an interface, and notifies of important events.
    • Does not store core data (like speed, health, lives,…).

Controllers

  • Do not store core data.
  • Can sometimes filter notifications from undesired Views.
  • Update and use the Model’s data.
  • Manages Unity’s scene workflow.

Class Hierarchy

In this case, there aren’t a lot of steps I follow. Usually, I perceive that some class needs to be split when variables start to show too many “prefixes,” or too many variants of the same element start to appear (like Player classes in an MMO or Gun types in an FPS).

For example, a single Model containing the Player data would have a lot of playerDataA, playerDataB,... or a Controller handling Player notifications would have OnPlayerDidA,OnPlayerDidB,.... We want to reduce the script size and get rid of player and OnPlayer prefixes.

Let me demonstrate using a Model class because it is more simple to understand using data only.

During programming, I usually start with a single Model class holding all data for the game.

// Model.cs

class Model
{
   public float playerHealth;
   public int playerLives;

   public GameObject playerGunPrefabA;
   public int playerGunAmmoA;

   public GameObject playerGunPrefabB;
   public int playerGunAmmoB;

   // Ops Gun[C D E ...] will appear...
   /* ... */

   public float gameSpeed;
   public int gameLevel;
}

It is easy to see that the more complex the game, the more numerous variables will get. With enough complexity we could end up with a giant class containing model.playerABCDFoo variables. Nesting elements will simplify the code completion and also give room to switch between variations of data.

// Model.cs

class Model
{
   public PlayerModel player;  // Container of the Player data.
   public GameModel game;      // Container of the Game data.
}
// GameModel.cs

class GameModel
{
   public float speed;         // Game running speed (influencing the difficulty)
   public int level;           // Current game level/stage loaded
}
// PlayerModel.cs

class PlayerModel
{
   public float health;        // Player health from 0.0 to 1.0.
   public int lives;           // Player “retry” count after he dies.
   public GunModel[] guns;     // Now a Player can have an array of guns to switch ingame.
}
// GunModel.cs

class GunModel
{
   public GunType type;        // Enumeration of Gun types.
   public GameObject prefab;   // Template of the 3D Asset of the weapon.
   public int ammo;            // Current number of bullets
   public int clips;           // Number of reloads possible
}

With this configuration of classes, developers can intuitively navigate in the source code one concept at a time. Let’s assume a first-person shooter game, where weapons and their configurations can get really numerous. The fact that GunModel is contained in a class allows the creation of a list of Prefabs (preconfigured GameObjects to be quickly duplicated and reused in-game) for each category and stored for later use.

In contrast, if the gun information was all stored together in the single GunModel class, in variables like gun0Ammo, gun1Ammo, gun0Clips, and so on, then the user, when faced with the need to store Gun data, would need to store the entire Model including the unwanted Player data. In this case, it would be obvious that a new GunModel class would be better.

IMAGE: CLASS HIERARCHY Improving the class hierarchy.

As with everything, there are two sides of the coin. Sometimes one can unnecessarily over-compartmentalize and increase the code complexity. Only experience can hone your skills enough to find the best MVC sorting for your project.

New game dev Special Ability unlocked: Unity games with the MVC pattern.

Conclusion

There are tons of software patterns out there. In this post I tried to show the one that helped me most in past projects. Developers should always absorb new knowledge, but always question it, too. I hope this tutorial helps you to learn something new, and at the same time, serves as a stepping stone as you develop your own style.

Also, I really encourage you to research other patterns and find the one that suits you best. One good starting point is this Wikipedia article, with its excellent list of patterns and their characteristics.

If you like the AMVCC pattern and would like to test it out, don’t forget to try out my library, Unity MVC, which contains all the core classes necessary to start an AMVCC application.

About the author

Eduardo Dias da Costa, Brazil
member since March 7, 2015
Eduardo is a developer with over a decade of experience focused on client and front-end applications. He is always open to learn and take up new challenges that can make him handle new languages and/or technologies. He specializes in computer graphics, image processing, game development, tools development (CLI, desktop ...) and UI/UX/front-end development. [click to continue...]
Hiring? Meet the Top 10 Freelance Unity or Unity3D Developers for Hire in September 2016

Comments

hahahaag
Great article! Organizing big projects in Unity is always a hassle, and that looks like a solid solution.
DnlCRG
A very instructive article providing instant insight. Well written, kudos. I want to develop an Anroid game over the next 4 weekends which is to be a reboot of an ancient simulation game I used to play 13 years ago. It has no graphics, onlly a 2D GUI, so unity would be overkill in my opinion. Do you agree ? I am looking for ways to model the game's "game logic" before wildly jumping into the development. Can you recommend a source for typical patterns ?
Leonardo Amora
Thank you for the article DUDU!
John Ryan
I don't think Unity is overkill for anything, really. It is great for 2D games, and its UI works well. It's ready to build to Android, as well as other platforms with no problem.
abdullah
Does every scene has own AMVCC model right?
Eduardo Dias da Costa
I use the same application but create 1 root controller per scene. login.unity3d == class LoginController game.unity3d == class GameController
abdullah
Why does not BallView extends BounceView instead of GameElement?
Rxanadu
I'm still confused how to use this sort of architecture structure with the new uGUI introduced within version 4.6. Given what the structure asks for, I'll have to make a separate model for both a Player and PlayerUI which require connections with one another. Is this correct?
Richard
Really great article, thanks a lot. I am trying the framework and came across this question: is any form of user input (keyboard, mouse, swipes, taps) a view or a controller in AMVCC, and why? What do you think? Thanks!
Charles Han
Do you think it's possible to share an example with multiple scenes? Thanks!
Atif Ali
Great the AMVCC! I am new to Unity but it is very helpful for me.
Kirill Avramenko
Why code in bitBucket is a different from examples here?
Carlos Sala Samper
Great tip!
Łukasz Michniewicz
I think that code here is just simplified version showing how model, view and controller objects interact with each other, while BitBucket code is generic framework to enforce MVC structure in your project.
Eduardo Dias da Costa
You can start with single few Model View Controller classes. They will be bigger than ideal but will allow you to prototype your game really quickly.
Eduardo Alvarado D
A really good information Eduardo, thank you very much. I will apply this pattern on my game, I really like it!. Thanks
Jan Rabe
The kicker is that EC was developed because MVC is insufficient for a lot of use cases in game development context. Mainly because you want to think about _behaviours_ rather than mere _presentation_ for an object in your environment. In my opinion you've misunderstood the idea behind EC completely. :(
Eduardo Dias da Costa
Both patterns have their strengths. I just said that EC can give too much freedom, and too much freedom in a dev team leads to chaos. I usually use MVC in the top levels of the app. When I reach the actual MVC elements (Player, Game, Enemies,...) I usually start applying EC and create smaller pieces for a more custom Behaviour on each. In Unity MVC is a layer of abstraction on top of EC. For instance, -model [SceneModel] --game [GameModel,JsonComponent,WebComponent] ---player [PlayerModel,JsonComponent] -controller [SceneController] --game [GameController] -view [SceneView] --game [GameView] ---player [PlayerView,MultiplayerComponent,InputComponent,...] Some Model classes re-use the 'JsonComponent' to serialize the data. The hi-level GameModel use a WebComponent to handle the webservices. The MultiplayerComponent handle incoming/outgoing data and applies to its target. The InputComponent receives input and applies to the target. The PlayerView can handle its extra components behaviours and relay these events to Controllers.
Eduardo Alvarado D
Of course!! In any software that have a MVC structure, the view is always like the software's face, and here is where the users interact with the program, so certainly they insert data too.
SMorris Morris
Excellent article, I've bookmarked it and downloaded the source; so thanks a LOT!! I'm trying to implement this correctly and wondered if you could tell me how you'd do this... I have a drone flight app which contains a drone, flight path, waypoints and some buttons. I have a camera button, a path button and a waypoint button. The camera button simply swaps the camera view from following the drone (over the shoulder), to the drone's onboard camera (fpv). The path button simply makes the path visible in the view (show/hide toggle). The waypoint button toggles the visibility of waypoints in the view. So would you create a BtnCameraView, BtnPathView and a BtnWaypointView class, each of which would "notify" the controller class that an event happened. And then from there would the controller then call a CameraView method, or a PathView, or a WaypointView? This is hard to explain but essentially would you create a view class for the path button, AND a view class for the path? The button view notifies the controller which the notifies the path view? Exactly how many "things" become a view. You bounce example is good, but there's not enough detail for me to understand how a more involved app w/UI would work. Any suggestions would be super appreciated. Again thanks so much for this article.
Andy
I think the idea here is to extend every gameObject from GameElement regardless if they are in the view, controller, or app. If a new gameObject call CarView was created, it will still extend GameElement, and never BounceView.
comments powered by Disqus
Subscribe
The #1 Blog for Engineers
Get the latest content first.
No spam. Just great engineering and design posts.
The #1 Blog for Engineers
Get the latest content first.
Thank you for subscribing!
You can edit your subscription preferences here.
Trending articles
Relevant technologies
About the author
Eduardo Dias da Costa
HTML5 Developer
Eduardo is a developer with over a decade of experience focused on client and front-end applications. He is always open to learn and take up new challenges that can make him handle new languages and/or technologies. He specializes in computer graphics, image processing, game development, tools development (CLI, desktop ...) and UI/UX/front-end development.