Unity MVCS (Model-View-Controller-Service) Architecture
I created this from scratch for fun, for learning, and for teaching.
It is Unity-centric. Its 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.
Enjoy!
Details
Features
- 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.
The hierarchy structure mimics the MVCS code structure. The hierarchy structure is optional.
Approach #1 - I used this. (See screenshot above)
- App
- Model
- View
- Controller
- Service
Approach #2 - Here is an alternative approach
- App
- Main
- Model
- View
- Controller
- Service
- Audio
- Model
- View
- Controller
- Service
- Main
For prefabs I followed the same concept, however, I made exceptions.
Approach #1 - I wanted to use this, but there are challenges
- Prefab
- Model
- View
- Controller
- (Service)
Approach #2 - I used this (See screenshot above)
- Prefab/View
- Model
- Controller
- (Service)
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.
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)
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
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.
- Samuel Asher Rivello
- Over 20 years XP with game development (2020)
- Over 8 years XP with Unity (2020)
- Twitter - @srivello
- Resume & Portfolio - SamuelAsherRivello.com
- Git - Github.com/SamuelAsherRivello
- LinkedIn - Linkedin.com/in/SamuelAsherRivello <--- Say Hello! :)