Unity is a powerful suite of tools (Project IDE, Code IDE, run-time) for game development.
I have explored many architectural approaches to game projects. Architecture is a hot topic in Unity — especially related to scaling up to larger Unity game projects.
My architectural background includes many frameworks. Highlights include heavy usage of Robotlegs (I event contributed to the project and drew the official diagram), PureMVC (I was the technical editor for the O’Reilly book on it), and PushButton (I published related articles for Adobe).
I am a Unity Game & Tools Developer with over 20 years of game development experience. I am available for hire (Remote, Contract).
I also presented and taught about the three at conferences and weekend workshops. Of these, only PushButton is a game-specific architecture, but all can be applied to games.
What are the qualities of a good gaming architecture? Most architects applaud solutions that are flexible, readable, D.R.Y., testable, orthogonal, and “refactorable” (at an acceptable cost). I will discuss the philosophy of design in a future post, but for now, I simply list the major pros and cons of each approach.
Here is my Unity MVCS (Model-View-Controller-Service) Architecture
I created this from scratch for fun, for learning, and for teaching.
I did this after creating a game from scratch SIX TIMES each with a different Unity architecture.
Check out Unity Game Architectures — Part 2 for more info!
Unity MVCS is specifically designed for the unique aspects of game development in the Unity platform (Scenes, Prefabs, Serialization, GameObjects, MonoBehaviours, etc…)
It is MonoBehaviour-centric. There are pros and cons to this approach.
- A light-weight custom MVCS Architecture — for high-level organization
- A custom StateMachine — For managing runtime complexity
- A custom Project Organization — for assets best practices
- A custom CommandManager — For decoupled, global communication
- Project Organization — A prescriptive solution for structuring your work
- Code Template — A prescriptive solution for best practices
The high-level architecture is taken from the industry-standard MVCS.
I used my own Project Organization for the project structure.
Scene Hierarchy Organization
The hierarchy structure mimics the MVCS code structure. This specific hierarchy structure is suggested, but not required.
Not every area of concern needs all 4 framework parts; M,V,C,S. Quite often, the S (Service) is not needed.
Approach #1 — I used this. (See screenshot above)
Approach #2 — Here is an alternative approach with a “mini” MVCS for each area of concern.
This solution will may lead to more items in the structure where each “mini” is modular and portable for reuse between projects.
│ ├── Model
│ ├── View
│ ├── Controller
│ └── Service
For prefabs I followed the same concept, however, I made exceptions.
Approach #1 — I wanted to use this, but there are challenges.
Approach #2 — I used this (See screenshot above). With Unity, it is especially beneficial if the View is at the root of a Prefab.
UNITY-FRIENDLY MVCS REFERENCES
The serialized references allow for high-level references in a Unity-friendly way. Of course, these references do represent ‘coupling’ but each major class type has a specific responsibility and these major concerns are indeed separate from the logic. (See screenshot above)
There is a nice concept wherein the C# you can specify ‘null’ for the Model, View, Controller, Service, and it will not appear in the inspector.
(See screenshot above)
- I structured the high-level classes using generics
- The boilerplate superclass structure does allow some (optional) recasting in the subclasses.
- The CommandManager works like an “EventBus” or “EventDispatcher”. To prevent naming confusion with the “UnityEvents” I use, I call this tier of communication “Commands”.
- For View->Controller communication “UnityEvents” are used.
I chose for BaseModel to extend MonoBehaviour. There are pros and cons to this.
C# BaseModel (MonoBehaviour)
A project can have as many models as needed. Typically one for each major domain (Player, Enemy, World, Physics, Audio, etc…)
Your BaseModel subclass is meant for reading at Runtime.
One of the pros of MonoBehaviour is the serialization in the inspector for the data. This is very helpful for debugging and seeing the current state of the app while it's running. (See screenshot above)
C# BaseConfigData (ScriptableObject)
There are many benefits to using ScriptableObjects for data storage. It is written to disk and easily editable at both editor time and run time. (See screenshot above)
Your BaseConfigData subclass is meant for writing at Edit Time and reading at runtime.
I included Unit Tests (with limited coverage). In some cases, I used TDD to develop sections and in others I added tests for academic (teaching) reasons. Generally, I recommend using Unit Tests for game projects and providing a coverage strategy
- Use TDD (per developer preference)
- Increase coverage on higher-risk and/or highly-testable areas
- Add a failing test for reported bugs. Fix the bug. Leave the passing test.
Complete source code and examples are provided for you. Enjoy!
- uMVCS (Github)
More By Samuel Asher Rivello
- Unity — Game Architectures — Part 2
- Unity — C# Coding Standards
- Unity — Project Structure Best Practices!
- Unity — uEvent & ScriptableObjects
Available For Unity Hire: Remote, Contract
20 years of game development experience. SamuelAsherRivello.com
Let me know! Twitter.com/srivello