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 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 which 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.
Checkout Unity Game Architectures – Part 2 for more info!
With Unity MVCS it 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 project structure.
Scene Hierarchy Organization
The hierarchy structure mimics the MVCS code structure. The hierarchy structure is optional.
Approach #1 – I used this. (See screenshot above)
Approach #2 – Here is an alternative approach
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)
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 the logic. (See screenshot above)
There is a nice concept where in 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 stucture 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 for 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 sublass 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 its running. (See screenshot above)
C# BaseConfigData (ScriptableObject)
There are many benefits for 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 other 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.
Want to learn something more basic? Or are you ready for more advanced topics about UnityEngine and C#?Checkout SamuelAsherRivello.com/learning/ !
Checkout the full Unity MVCS source-code !
What are your thoughts?
Do you prefer an alternative to the suggestions above? Or do you have an additional recommendation?
Let me know! Twitter.com/srivello