Implementing common game programming design patterns in your Unity project can help you efficiently build and maintain a clean, organized, and readable codebase. Design patterns reduce refactoring and testing time, speeding up development processes and contributing to a solid foundation for growing your game, team, and business.
Think of design patterns not as finished solutions you can copy and paste into your code, but as extra tools that can help you build larger, scalable applications.
This page explains the Model View Controller (MVC) and Model View Presenter (MVP) design patterns.
The content here is based on the free e-book, Level up your code with game programming patterns.
Check out more articles in the Unity game programming design patterns series on the Unity best practices hub or via these links:
Benefits of using design patterns
You can use the Model View Controller (MVC) and Model View Presenter (MVP) design patterns to separate the data and logic in your application from how it’s being presented. These patterns apply the principles of “separation of concerns,” which can improve the flexibility and maintainability of your codebase.
In Unity game development, you can use these patterns to separate the logic of a game into distinct components, like the data (Model), the visual representation (View), and the logic that controls the interaction between the two (Controller or Presenter).
The MVC design pattern
MVC is a family of design patterns commonly used when developing user interfaces in software applications.
The general idea behind MVC is to separate the logical portion of your software from the data and the presentation. This helps reduce unnecessary dependencies and potentially cut down on spaghetti code.
As the name implies, the MVC pattern splits your application into three layers:
- The Model stores data: The Model is strictly a data container that holds values. It does not perform gameplay logic or run calculations.
- The View is the interface: The View formats and renders a graphical presentation of your data onscreen.
- The Controller handles logic: Think of this as the brain. It processes the game data and calculates how the values change at runtime.
This separation of concerns also specifically defines how these three parts interact with one another. The Model manages the application data, while the View displays that data to the user. The Controller handles input and performs any decisions or calculations on the game data. Then it sends the results back to the Model.
The Controller does not contain any game data unto itself. Nor does the View. The MVC design limits what each layer does. One part holds the data, another part processes the data, and the last one displays that data to the user.
On the surface, you can think of this as an extension of the single-responsibility principle. Each part does one thing and does it well, which is the key advantage of MVC architecture.
MVP and Unity
When developing a Unity project with MVC, the existing UI framework (either the UI Toolkit or Unity UI) naturally functions as the View. Because the engine gives you a complete user interface implementation, you won’t need to develop individual UI components from scratch.
However, following the traditional MVC pattern would require View-specific code to listen for any changes in the Model’s data at runtime.
While this is a valid approach, many Unity developers opt to use a variation on MVC where the Controller acts as an intermediary. Here, the View doesn’t directly observe the Model. Instead, it does something like in the diagram above.
This variation on MVC is called the Model View Presenter design, or MVP. MVP still preserves the separation of concerns with three distinct application layers. However, it slightly changes each part’s responsibilities.
In MVP, the Presenter (called the Controller in MVC) acts as a go-between for the other layers. It retrieves data from the Model and then formats it for display in the View. MVP switches which layer handles input. Rather than the Controller, the View is responsible for handling user input.
Notice how events and the observer pattern figure into this design. The user can interact with Unity UI’s Button, Toggle, and Slider components. The View layer sends this input back to the Presenter via UI events, and the Presenter, in turn, manipulates the Model. A state-change event from the Model tells the Presenter that the data has been updated. The Presenter passes the modified data to the View, which refreshes the UI.
Try our sample project
A sample project is available on Github which demonstrates different programming design patterns, including an example of how to implement a variation of the MVP.
The MVP example consists of a simple system which shows the health of a character or item. This example has everything in one class that mixes the data and UI, but that wouldn’t scale well in real-world productions. Adding more functionality would become more complicated as you need to expand it. In addition, testing and refactoring would result in a lot of overhead.
Instead, you can rewrite your health components in a more MVP-centric way, starting by dividing your scripts into a Health and HealthPresenter.
In the sample project, you can click to damage the target object represented by a shooting disc (ClickDamage.cs), or reset the health with the button. These events inform the HealthPresenter (which invokes Damage or Reset) rather than change the Health directly. The UI Text and UI Slider update when the Health raises an event and notifies the HealthPresenter that its values have changed.
The Health interface
Let’s dive deeper into what a Health component could look like. In this version, Health serves as the Model. It stores the actual health value and invokes an event, HealthChanged, every time that value changes. Health does not contain gameplay logic, only methods to increment and decrement the data.
This allows a clear distinction between the data, the way it’s presented, and the way it’s controlled.
In the example discussed above, most objects won’t manipulate the Health itself. You’ll reserve a HealthPresenter for that task.
Other GameObjects will need to use the HealthPresenter to modify the health values using Damage, Heal, and Reset. The HealthPresenter usually waits to update the user interface with the UpdateView until the Health raises its HealthChanged event. This is useful if setting the values in the Model takes a short duration (e.g., saving values to disk or storing them in a database).
Pros and cons
MVP (and MVC) really shine for larger and UI-heavy software applications, but it’s not limited to those use cases. If your game requires a sizable team to develop and you expect to maintain it for a long time after launch, you might see the following benefits:
- Smooth division of work: Because you’ve separated the View from the Presenter, developing and updating your user interface can happen nearly independently from the rest of the codebase.
This lets you divide your labor between specialized developers. Do you have expert front-end developers on your team? Let them take care of the View.
- Simplified unit testing with MVP and MVC: These design patterns separate gameplay logic from the user interface. As such, you can simulate objects to work with your code without actually needing to enter Play mode in the Editor. This can save considerable amounts of time.
- Readable code that can be maintained: You’ll tend to make smaller classes with this design pattern, which makes them easier to read. Fewer dependencies usually means fewer places for your software to break, fewer places that might be hiding bugs, and easier testing.
Though MVC and MVP are widespread in web development or enterprise software, often, the benefits won’t be apparent until your application reaches a sufficient size and complexity. You’ll need to consider the following before implementing either pattern in your Unity project:
- You need to plan ahead: MVC and MVP are larger architectural patterns. To use one of them, you’ll need to split your classes by responsibility, which takes some organization and requires more work up front. Design patterns are best used consistently, so you’ll want to establish a practice for organizing your UI and ensure that your team is onboard.
- Not everything in your Unity project will fit the pattern: In a “pure” MVC or MVP implementation, anything that renders to screen really is part of the View. Not every Unity component is easily split between data, logic, and interface (e.g., a MeshRenderer). Also, simple scripts may not yield many benefits from MVC/MVP.
You’ll need to judge where you can benefit the most from the pattern. Usually, you can let the unit tests guide you. If MVC/MVP can facilitate testing, consider them for that aspect of the application. Otherwise, don’t try to force the pattern onto your project.
You’ll find many more tips on how to use design patterns in your Unity applications, as well as the SOLID principles, in the free e-book Level up your code with game programming patterns.
If you’d like more in-depth instruction on using Unity UI and UI Toolkit, see the extensive guide, User interface design and implementation in Unity guide, written by UI professionals.