A Model-View-Controller (MVC) Pattern for Unity
What do you do if you want to use the Model-View-Controller (MVC) design pattern in your Unity app but you don’t want to use a framework like StrangeIoC? With a little thinking about the problem I think I’ve come up with a simple yet effective pattern to follow that doesn’t require you to use any framework. In today’s article I’ll talk about each part, how the parts fit together, and how you can use MVC to cleanly organize your “pure code” app. Whether you’re an MVC newbie or just want to see a new take on MVC in Unity, you’re sure to learn something today!
Model-View-Controller (MVC) comes in three parts which are, obviously, the Model, the View, and the Controller. Today I’ll be describing a particular, Unity-specific manifestation of this design pattern that strongly resembles many other MVC designs, but surely differs in some ways.
Let’s start with the Model. A Model is a class representing the data of a particular concept. The concept can be anything in your app that you need to represent: an enemy, the score HUD element, or a archery target. Let’s use an enemy as an example. Here’s what the enemy model would look like:
using System; using UnityEngine; // Dispatched when the enemy's position changes public class EnemyPositionChangedEventArgs : EventArgs { } // Interface for the model public interface IEnemyModel { // Dispatched when the position changes event EventHandler<EnemyPositionChangedEventArgs> OnPositionChanged; // Position of the enemy Vector3 Position { get; set; } } // Implementation of the enemy model interface public class EnemyModel : IEnemyModel { // Backing field for the enemy's position private Vector3 position; public event EventHandler<EnemyPositionChangedEventArgs> OnPositionChanged = (sender, e) => {}; public Vector3 Position { get { return position; } set { // Only if the position changes if (position != value) { // Set new position position = value; // Dispatch the 'position changed' event var eventArgs = new EnemyPositionChangedEventArgs(); OnPositionChanged(this, eventArgs); } } } }
There are a few key things to understand about the above code. First, the model is just the data representation of the enemy. It doesn’t have any GameObject
, MonoBehaviour
, or anything else that Unity will actually display or keep as part of the scene. If it weren’t for using Vector3
from UnityEngine
this code would be portable even outside of Unity.
The second key is that an event is dispatched whenever the model changes. This lets anyone who’s interested in tracking the model’s state know that it has changed. Part of this is that the set
part of the model’s properties needs to check if the value is actually changing so as to avoid dispatching “changed” events when nothing has actually changed, potentially triggering expensive operations by those who are tracking the model.
Lastly, the model should always implement an interface so that other implementations of it can be used. This is particularly useful when unit-testing users of the model, but also allows for general flexibility at runtime.
Next up is the View. This part of the design pattern is the most Unity-specific. A View derives from MonoBehaviour
to provide two functions: input and output. I use the word “input” broadly here as it covers anything from mouse, keyboard, and touch input to messages from Unity delivered via magic function names like OnCollisionEnter
and Update
. For more on these, check out Capturing and Forwarding Unity Events.
The “output” part of a view includes anything it does to effect the graphics and sound output of the app, including changing transforms, spawning particles, and playing sound effects. While Unity will let you do most of the “output” part without a MonoBehaviour
, the “input” is only achieved by those magically-named functions.
Here’s an example view for the enemy:
using System; using UnityEngine; // Dispatched when the enemy is clicked public class EnemyClickedEventArgs : EventArgs { } // Interface for the enemy view public interface IEnemyView { // Dispatched when the enemy is clicked event EventHandler<EnemyClickedEventArgs> OnClicked; // Set the enemy's position Vector3 Position { set; } } // Implementation of the enemy view public class EnemyView : MonoBehaviour, IEnemyView { // Dispatched when the enemy is clicked public event EventHandler<EnemyClickedEventArgs> OnClicked = (sender, e) => {}; // Set the enemy's position public Vector3 Position { set { transform.position = value; } } void Update() { // If the primary mouse button was pressed this frame if (Input.GetMouseButtonDown(0)) { // If the mouse hit this enemy var ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if ( Physics.Raycast(ray, out hit) && hit.transform == transform ) { // Dispatch the 'on clicked' event var eventArgs = new EnemyClickedEventArgs(); OnClicked(this, eventArgs); } } } }
You may have noticed that there’s a certain symmetry between the model and the view. They’re similar, but different in very important ways. Both have an interface and a class implementing it, but it’s even more important that you make an interface when it comes to the view. When unit testing those who use the view you’ll definitely want to create a fake (a.k.a. “stub”, “mock”, “substitute”) view so that you don’t need an actual GameObject
, don’t do any actual output (like playing sounds!), and don’t need any actual input so the test can be automated.
The view has events for any input that it would like to pass along to those using it to gather input. In this case the enemy dispatches an event when it is clicked, but it doesn’t take any direct action in response to the input. For example, the view does not highlight the enemy even though that would be an “output” function. It certainly doesn’t shoot the enemy or anything like that.
On the “output” side, the view provides set
-only properties and other write-only functions so that it can take in data necessary to change the app’s graphics and sound output. In this case there is just a simple position setter that passes the Vector3
along to the GameObject
transform
.
Lastly we come to the Controller. This is the part of the pattern that binds the model and the view together. It has references to both of and acts to coordinate them. The controller listens to the model’s events and tells the view to update accordingly. It also listens to the view’s events and tells the model to update accordingly. Let’s take a look at a controller for the enemy:
using UnityEngine; // Interface for the enemy controller public interface IEnemyController { } // Implementation of the enemy controller public class EnemyController : IEnemyController { // Keep references to the model and view private readonly IEnemyModel model; private readonly IEnemyView view; // Controller depends on interfaces for the model and view public EnemyController(IEnemyModel model, IEnemyView view) { this.model = model; this.view = view; // Listen to input from the view view.OnClicked += HandleClicked; // Listen to changes in the model model.OnPositionChanged += HandlePositionChanged; // Set the view's initial state by synching with the model SyncPosition(); } // Called when the view is clicked private void HandleClicked(object sender, EnemyClickedEventArgs e) { // Do something to the model // This example just moves the model +1 along the X axis model.Position += new Vector3(1, 0, 0); } // Called when the model's position changes private void HandlePositionChanged(object sender, EnemyPositionChangedEventArgs e) { // Update the view with the new position SyncPosition(); } // Sync the view's position with the model's position private void SyncPosition() { view.Position = model.Position; } }
The controller should be very straightforward as it’s mostly just a middle-man between the model and the view. There are some details to take into account though. The first of these is that the controller should take interfaces for the model and view as parameters to its constructor so that alternate versions can be used. Unit testing a controller is where one of the biggest payoffs of having interfaces for the model and view comes into play. You can easily give it fake models and views to isolate your testing to just the controller. Additionally, providing an interface to the view means that the controller isn’t tempted to directly access all the properties of MonoBehaviour
, like the transform
or renderer
.
Second is that you should also have an interface for the controller. It may be empty if your controller has no public functions—as in this example—but you may add functions later. This class, like the model, also doesn’t have any necessary dependencies on Unity so it’s quite portable in case you want to move one or both of them to a DLL used by a command-line app, a server, or any other .NET environment.
This takes care of the three main pieces of the pattern, but it’s often a good idea to create factory or builder classes to build one or all of the parts. Here are some example factories:
// Interface for the model factory public interface IEnemyModelFactory { // Get the created model IEnemyModel Model { get; } } // Implementation of the model factory public class EnemyModelFactory : IEnemyModelFactory { public IEnemyModel Model { get; private set; } // Create the model public EnemyModelFactory() { Model = new EnemyModel(); } } // Interface for the view factory public interface IEnemyViewFactory { // Get the created view IEnemyView View { get; } } // Implementation of the view factory public class EnemyViewFactory : IEnemyViewFactory { public IEnemyView View { get; private set; } // Create the view public EnemyViewFactory() { var prefab = Resources.Load<GameObject>("Enemy"); var instance = UnityEngine.Object.Instantiate(prefab); View = instance.GetComponent<IEnemyView>(); } } // Interface of the view factory public interface IEnemyControllerFactory { // Get the created controller IEnemyController Controller { get; } } // Implementation of the controller factory public class EnemyControllerFactory : IEnemyControllerFactory { public IEnemyController Controller { get; private set; } // Create just the controller public EnemyControllerFactory(IEnemyModel model, IEnemyView view) { Controller = new EnemyController(model, view); } // Create the model, view, and controller public EnemyControllerFactory() : this(new EnemyModel(), new EnemyView()) { } }
Exactly how you implement your factory classes is up to you and, in the case of the view, very specific to your app’s assets. The model and controller factories are more straightforward, but may have some variation depending on how much or little you’d like them to do. You may have multiple factories for different kinds of construction or simply multiple constructors in a single factory. It’s your call, but the interfaces should remain so you can create more factories for different kinds of construction. For example, a WWWEnemyViewFactory
could load an enemy model from the web with the WWW
class.
For the final bit of code, let’s take a look at how you’d use these MVC classes in a Unity app. With all of the setup we’ve done so far, this part is really simple.
using UnityEngine; public class MainScript : MonoBehaviour { void Awake() { // Create the model var modelFactory = new EnemyModelFactory(); var model = modelFactory.Model; // Set some initial state model.Position = new Vector3(1, 2, 3); // Create the view var viewFactory = new EnemyViewFactory(); var view = viewFactory.View; // Create the controller var controllerFactory = new EnemyControllerFactory(model, view); var controller = controllerFactory.Controller; } }
It’s really just a simple matter of using the factories to build everything. In this case we don’t use the controller since it has no functions, but simply creating it allows it to do its job.
At this point the first question I would have is “do I really have to make 6 classes and 6 interfaces to use MVC?” The answer is “no” since many of them are optional. You certainly need the model, view, and controller classes since they are the core of the pattern. You don’t strictly need interfaces for these, but it will greatly help with unit testing and general flexibility. If you’re trying to cut down, at least make an interface for the view. All of the factories and their interfaces are optional and replaceable by other concepts such as dependency injection (DI) frameworks like StrangeIoC. Even if you decide to keep all 12, it’s easy to make a little script that creates them for you based on template versions.
Another common question is “how can the concepts interact with each other?” This can be done easily by providing multiple models to a single controller. For example, if you want to display the selected enemy on the HUD (a la StarCraft), you could create a SelectionModel
and pass it to the EnemyController
. When the enemy is clicked, simply set the selection on the SelectionModel
and its associated SelectionView
will be updated via the SelectionController
. Alternatively, you could pass the SelectionModel
to EnemyModel
and have it set the selection instead of the EnemyController
.
The last common question I’ll answer here is “where should I put my logic?” The answer to this comes in the form of the saying “fat models, skinny controllers”. The general idea is to put most of your logic in the models and keep the controllers small. Certainly the views should never do any of the app’s logic.
There are plenty of details and questions that could be asked about this MVC system. If you have any, feel free to ask in the comments. Likewise, let me know if you’re employing MVC in your Unity apps and how your version compares to mine.
#1 by inozemcev on July 12th, 2015 ·
Hello Jackson. Thank you four your amazing blog. I am game developer and allmost a year I was working on collection card game rather close to Hearthstone. I wanted just try realising all aptitudes of unit and spell cards and now I almost finished my ccg engine. Now I have I dillema I need to concentrate all my attention on the graphics part at the moment. I have a client side running on classic flash without Starling, with robotlegs achitecture. I want to publish this game as standart browser game. So I can continue working with flash and redesign my views with startling or I can migrate on ue4 or unity4 but I need to find some way to save my command view contoller achitecture if it is possible. What you could suggest me?!
#2 by jackson on July 12th, 2015 ·
I’m glad you’re enjoying the blog! The band you linked is cool, too. I had a listen and Under Our Illusion sounds pretty good to me. :)
As to your question, it seems like you have two main options: stick with Flash or port to another platform.
If you stick with Flash, you should definitely use Starling or some other
Stage3D
-accelerated framework. Starling is probably the best choice just due to popularity.If you port to another platform, you have many options. Unreal Engine and Unity3D both only have experimental support for HTML5/WebGL, but great support for mobile and desktop. Unity probably has the advantage for your particular project just because StrangeIoC is a close replacement for Robotlegs.
You could also code directly for the web with JavaScript, TypeScript, or CoffeeScript. There are a million JavaScript game engines you could use. If you want to support mobile or desktop, you’ll need to choose a tool like PhoneGap or CocoonJS to bundle your web game as a mobile app. Bundling as a desktop app is harder, but there are tools for that too.
In the end, I’d probably go with Unity. StrangeIoC will make porting from Robotlegs the easiest experience you’re going to find. You’ll get great support on mobile and desktop and the web is pretty good and getting better with each release. The toolset in Unity for things like asset management is tons better than in Flash, which will be a huge win for content creators. Unreal, JavaScript, TypeScript, or CoffeeScript just seem like they would be harder for you since none has StrangeIoC or any other direct Robotlegs port. I wouldn’t stay with Flash for reasons stated a year ago that haven’t changed. If you’re new to C#, check out my 23-part series titled From AS3 to C#.
#3 by inozemcev on July 13th, 2015 ·
Thank you so much for your total complete and quick answer! I also feel that migrate is better than stay on flash but I lettle bit fearfully. I have been studing and working with flash for 6 years. Then I lost some time migrrating on Unity becouse was studing python and now I think about studing ue4 as more perspective than unity. But Unity in this project really look like as better choice. The only reason I afraid choose Unity is that it tooks to many times to studing. But with your lessons about migrating it doesn’t seems to fearfull. Thank you one more time.
#4 by jackson on July 13th, 2015 ·
You’re very welcome. Best of luck with your project. Please post any questions that come up on this site or mail me directly. Send me a link when you have something public to check out, too. I’m a Hearthstone fan so it’d be come to see where your CCG ends up. :)
#5 by Osman Zeki on September 28th, 2015 ·
Hello from Montreal!
Great article as always. Thank you for sharing! I feel like you have been throught the same line of thinking that I have and that I seem to be seeing more and more regarding code and the Unity community.
That is to try and reduce clutter in the scenes by removing as much logic as possible from them. Reading your blog is always inspiring but even more so in that regard. :)
I have taken a look at Slash Framework recently and without having tried it on a real use case, I can appreciate their different ECS approach for Unity. Same goes for uFrame MVVM and uFrame ECS. What are you opinions on those if you have any?
http://www.slashgames.org/framework/
https://invertgamestudios.com/ecs/overview
https://invertgamestudios.com/mvvm/overview
#6 by jackson on September 28th, 2015 ·
Thanks for the kind words about the article. :) I haven’t seen the frameworks you linked, but I’ll definitely take a look at them now!
#7 by jackson on September 30th, 2015 ·
I took a look at these frameworks, but haven’t gone so far as to actually use them. It’s hard to comment on the uFrame system because it’d cost $90 to buy it off the Asset Store. Visual scripting systems like that are indeed popular among many game teams, but not always applicable. Sometimes the designers don’t want to script the logic and prefer a more hands-off approach. Other times the designers are capable programmers who can write C# or UnityScript and contribute on that level. Visual scripting represents a middle ground and, from my brief look, uFrame seems to offer a pretty good system for that. There are many more visual scripting systems available for Unity though, so it’s best to do a comparison if that’s something you’re seriously considering.
As for the Slash framework, I suppose I don’t really understand the point of building a component-based architecture on top of Unity’s architecture which is already component-based.
MonoBehaviour
extends fromComponent
as do all the other familiar classes likeRigidbody
,Animation
, andLight
. What justifies their approach over the more conventional approach of creating a set of game- or library-specificMonoBehaviour
scripts?#8 by jeff on October 29th, 2015 ·
Thanks for your great article, I have a few questions here.
1. How to access model data from another model?
2. I don’t understand how the factory work, why we need the factory? And are the factory can access in Controller or Model?
3. Can Controller control more than one View and access data from more than one Model?
#9 by jackson on October 29th, 2015 ·
Good questions!
1. Normally you wouldn’t, but if you needed to you could pass a reference in to the model’s constructor via the model factory or set a property of the model after it was constructed.
2. The factories exist to encapsulate the creation of an object. Instead of directly using the
new
operator, you use a factory instead. This is useful for many reasons, including being able to write unit tests where you provide a factory that creates mock/fake objects instead of real ones. The factories should only create instances and the usually don’t have access to any controller or view. You typically just pass them what they need to construct the instance.3. Yes. While a controller normally just has one model and one view, you can easily have it control more than one. However, in most situations it’s better for controllers to only have one model and one view but to coordinate via changes to the one model they have. For example, a
HudController
would listen toPlayerModel.OnHeathChanged
to update theHudView
rather thanPlayerController
directly updating theHudView
.I hope that all made sense. Let me know if I can clarify.
#10 by jeff on October 30th, 2015 ·
Thanks for the reply, normally a game will have a lot of View component, if only use Controller to access data from Model and control View component, there will be a lot of controller? And I thought Controller is use for get data from Model and direct update the View? Please correct me if I’m wrong…
Where will you manage to destroy the View and Model if there are no more use on the stage?
And will you use Value Object to pass data between Model and View Component?
#11 by jackson on October 30th, 2015 ·
I’m always happy to reply to comments, especially when they have good questions like yours. :)
Q: Will there be a lot of controllers?
A: Yes, a typical game will have tons of controllers. Generally there’s one per entity in the scene. For example, if you had a Match Three game like Candy Crush Saga then you’d have one instance for each candy in the grid, one for the grid itself, one per HUD element, one for each floating score text, and so forth. This isn’t really much different than other game architectures though.
Q: Is the purpose of the controller to get data from the model and update the view?
A: Yes, that’s one of its main functions. The other is to take input from the view and update the model’s data.
Q: Where do you destroy the view and model?
A: The controller normally destroys the view and model in response to either input from the view or data changes from the model. One way of destroying them is to call a
Dispose
function on them to let them take care of their own destruction. For example, the view may callDestroy(GameObject)
on game objects it renders with and takes input by. The model may remove all the event listeners from its events.Q: Do you use value objects to pass data between the model and view?
A: You can pass the data however you like, including using value objects. The
struct
type is a good way to avoid creating garbage, but you may pass individual parameters orclass
instances instead. Just make sure you don’t pass the whole model to the view or the whole view to the model. Keeping them from having references to each other means they won’t be tempted to bypass the controller and directly make changes to each other.#12 by jeff on November 4th, 2015 ·
Thanks again :)
Do you use EventHandler to update data from Model to View component? If yes, will you use back same Event to update data? And is Enum is a good practice to control data?
eg,
#13 by jackson on November 4th, 2015 ·
I think events are a great way for your controller to listen to input from the view and changes in the model. In response to these events, the controller can make changes to either the model or the view. Those changes may trigger more events to be dispatched.
The example I gave in the article uses events and the
System.EventHandler
delegate similar to the way your example does. I prefer to make a uniqueEventArgs
type for each kind of event so that they can each have their own different data, but you could use anenum
like you did in some cases.Typically you won’t want to have views and models dispatching the same events though. This is due to the roles of the views and models being quite different. For example, the view dispatches events corresponding to input it receives so it’ll have
EventArgs
types likeEnemyClickedEventArgs
. Models won’t ever deal with input, so they wouldn’t ever dispatch that type of event. Instead, models dispatch types likeEnemyPositionChangedEventArgs
becausePosition
is part of the model’s data. A view doesn’t even have a reference to the model’s data, so it would never dispatch such an event.If you use a different
EventArgs
type for each action that can occur and your views and models don’t dispatch the same events, your event-handling code can be a lot more straightforward and easy to read. For example, here’s how youronDataEventHandler
could be split up:You do end up with a lot of types, but you can eliminate a lot of
if
andswitch
code. I think it’s worth it, but the MVC model easily supports both approaches.Hope that helps! :)
#14 by jeff on November 11th, 2015 ·
Thanks again, I have few question here.
1. Will you use 2 different Controller to access a same Model? like there are landing scene, and game scene, both of them will access and update user data from UserModel. Will you write a LandingController and GameController to control LandingView and GameView?
2. Will you destroy the Controller if no use anymore? If yes where will you destroy it?
3. I saw you create all instance on MainScript, what if you destroy the view and model, where will you initial again? like on the game scene, I will destroy landing instance, if game over, where should I initial the landing instance?
#15 by jeff on November 11th, 2015 ·
4. Can Controller communicate with the other Controller? eg, in LandingContoller and GameController, how will you manage to change scene?
#16 by jackson on November 11th, 2015 ·
1. You can definitely provide two models to a single controller. That’s actually a good way to communicate between controllers. Controller A and B have the same model. Controller A sets a property on the model. The model dispatches an event indicating that the value has changed. Controller B handles the event by updating its view.
2. Yes, the controller should destroy itself when no longer needed. That usually means calling a function to have the view destroy itself (e.g.
Destroy(GameObject)
), cleaning up event listeners, then letting the garbage collector kick in.3. I only created them there to keep the example simple. In a real game or app you’d only create enough to get the ball rolling. For example, your main script might create a single controller for the loading screen and the loading screen controller would create further controllers for progress bars, logos, the next screen, etc.
4. See #1. :)
These are excellent questions. Feel free to ask more! :)
#17 by jeff on November 11th, 2015 ·
Do you mean that Controller not access to the other Controller directly?
In the loading Controller, after the loading completes, you will get next screen Model, View and Controller from factory and put them on the screen?
#18 by jackson on November 12th, 2015 ·
You could definitely have controllers access other controllers, as long as you avoid accessing their views. In the loading screen example, the loading screen controller could basically do what the
MainScript.Awake
function did in the article: use factories to create more models, views, and controllers. Those could be for the loading screen itself (e.g. a progress bar) or for the next screen (e.g. the main menu).#19 by LAS on November 13th, 2015 ·
Hey it’s a great article. I believe it will help many programmers about how to make MVC on Unity.
I want to copy paste some basic codes and have some quick questions about it. Here it is:
As it seems it is a basic code. It listens GameManager (which follows singleton pattern) OnStateChange events and disables the views. As you noticed there is no model instances, there are only Views and thing is; it is quite enough to disable them, because they are there for just to be shown to the user and will be disabled by the event. Question is: What do you think to this approach? I mean is it okay going without models sometimes if they are not necessary. Otherwise i feel like doing some extra work like defining a model for these views with putting enable/disable variable in it and some delegates and events you know. Or am i totally wrong :) ? Thank you.
#20 by LAS on November 13th, 2015 ·
When i check the code later on, i realized it is not good to change a value of a view from a controller. It should have a Disable () function or similar something instead of “gameObject.SetActive()”. But well just wanted to mention. Question still exists :) Thanks.
#21 by jackson on November 13th, 2015 ·
I’m glad you enjoyed the article. :)
The idea of the model is to contain the data representing the state of the entity. In the case of your example, the “high scores” would go into a model. Perhaps that’d be a
HighScoresModel
like this:Then you’d have a view:
And finally a controller:
That’s an incomplete example compared to the article since it’s lacking interfaces, factories, and events, but it should illustrate how you’d handle the displaying of a high scores screen.
The only remaining issue is how to switch between the game modes. One way would be to have the controllers destroy themselves and their views based on some input. Here’s an example of transitioning from the high scores screen to the root menu screen:
Again, this is a shell of an example. Factories and interfaces would really help out here, but the point is that one controller can create another and can “tear down” its view. That’s one way to switch between them.
For more on application state switching, check out my article A Finite State Machine (FSM) for Unity.
Hope that helps clear things up.
#22 by Chom1k on August 4th, 2016 ·
More and more questions from me here :) But I got this problem:
I have a menu with 3 buttons and everyone of them creates the same menu but with different label and when the view is already created it should only change it’s label or just destroy previous view and create new but I don’t know how to do it. I mean, should I keep reference to this newly created view in controller? Is it a good approach?
It’s located in controller
#23 by jackson on August 4th, 2016 ·
Destroying and creating UI elements is really expensive, so I’d avoid it if you can. In this case you say the only difference between the menus is the label so you could change the label and keep the rest of the UI elements the same. For example, you could set the text and styling of the label when the menu changes. (e.g.
titleView.Text = "Settings";
).#24 by Chom1k on August 5th, 2016 ·
That’s a good idea! Thanks.
1. So you rather have them all created at the start and then activate them when needed or creating them the first time is not a big deal and then pooling them? Or just have view already created and just create models/controllers?
2. Is it a good idea to put any/extra parameters during constructors (model/view/controller) to initialize some values?
But if I do none will be notified cause controller is created after model/view so should controller init those values like that or just forget about it at all?
3. When I subscribe events for example in controller constructor, should I also unsubscribe them when I destroy view? Like:
View implements MonoBehaviour so I can use Destroy on it’s object but how can I destroy/dispose Controller/Model, should I do it? Or is it done automatically when there is no references to them (like event references)?
#25 by Chom1k on August 5th, 2016 ·
To extend qustion since you cannot edit :X
1. Would be a good idea to store reference to the next view in first view (linked?) or to pass it in constructor in controller?
public void RootController(IMenuModel model, IMenuView view, IOptionMenuView view2)
and then
public void OpenOptionView()
{
// creating model and controller for view2 and passing view2 to controller?
view.Deactivate();
view2.Activate();
}
#26 by Chom1k on August 5th, 2016 ·
Hey it’s me again, sorry for nesting my comments but now I am anwering to my own questions lol. I think I found out cleanest solution or rather I haven’t thought it better earlier so:
1. When creating side-menu from
MainMenuController
I store it’sISideMenuController
reference (instead of view).If it’s null then I create it using factories and store reference to controller.
If it’s not null then I activate view from stored
ISideMenuController
which I given ability to do so, this way I don’t manage ISideMenuView from not right controller. So it’sMainMenuController -> ISideMenuController
talk and everything is clean and smooth. Is it good way of thinking and approach? Because I think it is :D2. I pass init values through Controller constructor (overloading) or calling IController set method just after I created it.
3. Still need to know more details about unsubscribing :) Cheers!
PS you should really make edit option and damn those they don’t work me me ^^”
#27 by jackson on August 5th, 2016 ·
1. You can certainly have a controller create another controller, especially via a factory. That’s typically how you’d spawn something new, like a
BarracksController
spawning aMarineController
in a StarCraft-like game. You probably don’t want the controllers to keep references to each other for ongoing operations though. It’s better for them to communicate via some shared model (e.g.ScoreModel
).2. The number of constructor parameters for a view should be determined on a case-by-case basis. If you have sensible defaults that can be later changed without a big performance hit, it’s probably best to keep the parameter list smaller.
3. It’s good practice to unsubscribe from the event when you don’t want to hear it anymore. You wouldn’t want to hear from a destroyed view, so that seems reasonable. You might be able to get away with not unsubscribing if you happen to know that you’re going to be releasing all references to the object with the event, but you’re probably better off playing it safe.
#28 by Chom1k on August 6th, 2016 ·
1. Well I don’t know other better option how to check it it’s already created, as you said it’s better not to create it every call if I can repool it. I just keep reference to see if it’s not null.
3. So should I have event for activation and deactivation view and event handler in controller to subscribe/unsubscribe to it every time?
#29 by jackson on August 7th, 2016 ·
3. In your code above you have the controller subscribe to the view’s events in its constructor and unsubscribe from the view’s events when it destroys the view. I think that’s a perfectly reasonable way to do it.
#30 by LAS on November 14th, 2015 ·
Thanks for the detailed explanation. Yes HighScores should go for a model definitely, as it has a data. And creating controller from another controller is nice to have. I have very quick question, as it was mentioned above, but want to ask to be sure: So HighScoresController takes the model and view via its constructor. Question is; If i have another view called BackButtonView, is it okay to pass this to the HighScoresController as another view so that i can register to its events? (Updated your shell code below to demonstrate) In other saying; is it okay for a controller to have more views? Or is this a bad approach in manners of MVC? I feel like it is necessary sometimes but well jsut wanted to ask. Thank you Jackson.
#31 by LAS on November 14th, 2015 ·
Another but last question is:) : I derive Controller classes from MonoBehaviour sometimes because the Views was created directly from the Editor, so i can easily assign these Views via drag drop to the Controller game object. Do you think am i violating the MVC with this setup? Instead of passing them via Controller constructor, i pass it via Editor. Thanks and Cheerz :)
#32 by LAS on November 14th, 2015 ·
And would like to add that i liked your FSM approach. It is better than using state enums with a singleton GameManager. :)
#33 by jackson on November 14th, 2015 ·
I’m glad you enjoyed the FSM, too. :)
Q: Should controllers ever have more than one view?
A: No, they should just control one view. If they’re controlling more than one then it becomes confusing as to which controller is controlling it and they can often conflict with each other.
Q: Can controllers derive from
MonoBehaviour
?A: They can, but that’s not the MVC approach this article is about or the one that I favor. I try to keep the Unity-specific parts (e.g.
MonoBehaviour
,GameObject
) in the view only. This means there’s only one place to look for how the scene and hierarchy are controlled: the view. For more on my “pure code” approach, see these articles. This MVC approach is really compatible with “pure code” because it limits the Unity parts to just being a thin layer that takes orders from the rest of the non-Unity C#.#34 by LAS on November 15th, 2015 ·
Jackson, thank you again for the detailed explanation. All is clear and done. I have a feeling like when we go with “pure code” it will be well structured but with not using some unity editor features (i’m not sure if they are so vital). Focus goes to code from editor. On the other hand when i want to use Unity Editor features and if i’m a bit careless about the architecture, there it can be a spaghetti code in the future. So going with pure code is okay with me as a developer. Happy with it :) Do you have some research of how much it effects in all ways? As i’m a developer i don’t want to define public variables around which becomes editable from editor, nahh :) We can wrap that of course, but well. Anything about it is welcome :) I know still we are using Unity as you mentioned one of your posts like sounds, rendering, physics etc. I also comfortable using editor with designing some ui or any gameobject, prefab it and create it from Factory class like: CreateView function (within pure code approach). So are there any possible places that Unity supports but “pure code” stuff don’t use? because i like the approach and using it :)
Oh man hope it is clear, thanks :)
#35 by jackson on November 15th, 2015 ·
Plenty clear. :)
I shared your concern about losing valuable features from the Unity editor when using the “pure code” approach, but it was easy to work around. It definitely, intentionally puts the focus on the code rather than the editor, which is great for programmers that are comfortable in that space and less great for newbie programmers that are more comfortable making things work by drag-and-drop in the editor. As always, choose the right tool for the job. In this case, the right tool for your team.
You make a good point about prefabs. If you treat them like views then it’s a natural fit with this MVC approach. As you see in the article, views are already a
MonoBehaviour
so they fit really well there. The view factory in the article even uses a prefab to instantiate the view. This allows for designers to lay out a GUI, for example, however they want and simply attach your view script to it to make it usable by your controller.To directly answer your question, I wouldn’t use “pure code” in some scenarios such as rapid prototyping, with a lower-skilled programming team, and for the creation of UI screens where a manually-created prefab makes more sense than programmatic layout. For general programming purposes though, it’s my preferred approach for most tasks.
#36 by LAS on November 16th, 2015 ·
Thank you Jackson. All sound clear to me. Keep up the good work friend. ;)
#37 by Kel on November 23rd, 2015 ·
Hi I am currently starting a new project using the MVC approach mentioned in this article. Must agree this is a great article to follow through. However, I am facing a issue where i have two function (1 controls Scene Change, the other controls a list to display the different room available). The list is only instantiated when user activates by clicking on the main button to open the list. Every icon in the list is attached with an listDisplayView which will dispatch an event when click. SceneChange will listen for the click event and update the loaded scene accordingly. The problem is that my SceneChange is trying to listen for the click event even before it is created. How can I overcome this? I am currently listening for the click event in start function of SceneChangeMainScript. Am I doing it wrongly? Typically how should different functions listens for events dispatched by other views? Thanks in advance.
#38 by jackson on November 23rd, 2015 ·
Hi Kel. That’s so cool that you’re basing your project’s design off of this MVC approach! I’d be happy to help answer some questions as you go, starting with this one.
It’s hard to tell exactly what’s going on here, but here’s how I imagine the classes to look and the responsibilities they would have:
ScenesListView
– displays the list, listens for clicks, dispatches click eventSceneModel
– holds current scene setting, dispatches event when changedScenesListController
– listens to click event on the view, changes current scene on modelSceneChangeController
– listens to scene change event on model, destroys MVC objects for existing scene, creates MVC objects for new sceneThat’s just one possible design. You could have lots of variations to this with more models, more views, or more controllers. You might also switch scenes using the technique described in A Finite State Machine (FSM) for Unity.
In general, you’re only doing it wrong if you violate the basic, simple MVC diagram:
Model ← Controller → View
. That is, the model and view should never know about each other or the controller. The controller gets to know about models and views.Let me know if any of this is unclear or you’ve got any follow-up questions. :)
#39 by Kel on November 24th, 2015 ·
I currently have multiple scenes implemented. SceneChange is attached to my main scene. The room list display is a function in one of the scene. It would only be activated if the scene is loaded and user click on the button to load the room list display. So on default, the main scene with sceneChange is loaded but not the scene with room list display. So how do I ensure that my room list display is able to dispatch a event that can be listened by scene change? Currently i add listener for the event in scene change but because roomlist is not yet instantiated, it will complain object reference not set to an instance of an object.
#40 by jackson on November 24th, 2015 ·
In order to survive loading a new scene, your view’s
GameObject
will need to be set toDontDestroyOnLoad()
. The view can then dispatch events when the scene changes (OnLevelWasLoaded()
). The controller for that view can listen to the view’sOnSceneLoaded
event and do whatever’s necessary to set up that new scene. For example, it might create MVC objects and attach them to the hierarchy. You might have View components already attached in the scene, so setting up the scene might just involve creating Model and Controller instances for those already existing View components.In the case of your Room List UI, you’ll probably have laid it all out with the Editor’s GUI tools and have an attached
RoomListView
on aCanvas
GameObject
. So whenSceneChangeController
handlesOnSceneLoaded
fromSceneChangeView
, it would create aRoomListController
and pass it theRoomListView
attached to yourCanvas
GameObject
.That’s one way to handle the scene loading transition. It’s tricky because Unity destroys the whole hierarchy by default, so you need to use
DontDestroyOnLoad()
to keep your scripts running during the transition.#41 by Jason on November 25th, 2015 ·
Brilliant ! I’ve been learning Unity for a year now and I’m just starting to write unit tests with Moq and wondering how to refactor my code to make it testable. This article is just what I’ve been looking for and it’s very clearly explained. Thanks. A few questions please –
1. How will the MVC approach affect cpu/memory performance on older mobile devices?
2. What’s the benefit of using event handlers instead of just referencing a public property of another class?
3. I’m thinking about learning ASP.Net MVC. How does this unity pattern differ from ASP.Net MVC in terms of the roles of the model, view and controller?
#42 by jackson on November 26th, 2015 ·
I’m glad you enjoyed the article. :) I’d be happy to answer your questions, too.
1. I doubt it will have much effect. Most of the CPU time will go to more expensive operations like physics and asset loading. Dispatching a few events and calling a few functions shouldn’t even register in the profiler.
2. Events allow your class to not have any explicit reference to the class who’s using it. In this MVC approach, both the Model and the View dispatch events that the Controller handles. If they directly called functions or set fields on the Controller, they would need to have an explicit reference to an instance of the Controller. If they have such a reference, what’s to stop them from calling other functions and setting other fields than the ones they should? For example, if the Model is supposed to call
HandleScoreChanged
and the View is supposed to callHandleJumpInput
then what’s to stop the View from callingHandleScoreChanged
or the Model from callingHandleJumpInput
? By using events, neither the Model nor the View need to have any knowledge of which class is interested in them. Cutting out these references simplifies coding the Model and View and makes it impossible for them to use the Controller in inappropriate ways.3. I’ve actually never used ASP.NET MVC, so I can’t comment on that particular MVC design.
One final tip: consider NSubstitute as an alternative to Moq. I use it on a daily basis and it’s really nice. :)
#43 by Jason on January 5th, 2016 ·
2. Yes I really like using events. It’s worth noting that you have to unsubscribe from delegate events before you destroy the listening object otherwise you get memory leaks. Have you tried using UnityEvents? I believe they create WeakEvents which can be garbage collected.
3. The way Microsoft have implemented MVC it’s really just intended for web development. The controller receives the user input. It then updates the model and decides what view (web page) to display to the user. This view gets populated with data from the model. That’s nothing like game development.
I was confused because I read articles about MVC from 5 years ago and everybody had slightly different ideas as to how it worked. I think your pattern is called MVP but I might be wrong.
4. I can’t find much to put in my model. I’m making a driving game, and the the only data I have is users, game scores and racetracks. In a physics based game, would it be easier just to split my code into Unity code and logic code?
#44 by jackson on January 5th, 2016 ·
2. I haven’t heard of UnityEvents before. Can you post a link? And yes, you definitely need to unsubscribe from events (with
-=
) when destroying the listening object.3. Sounds like it might be interesting to get ideas from, but not useful for games or Unity since it’s web-centric. As for MVP, it seems like a fine line between it and MVC. Which one did I describe in the article? Even I’m not entirely sure. :)
4. It’s up to you how much you want to put into the MVC system. Unity’s physics API is component-based, so one interesting approach would be to have a model of your own that has a reference to Unity’s physics components (e.g.
Rigidbody
). In that case the model wouldn’t have any data of its own, but instead abstract the physics components by passing through.#45 by Jason on November 27th, 2015 ·
Brilliant. Thanks you’re a star!
#46 by jeff on December 8th, 2015 ·
I am currently start a project with MVC, there is a sound control on the menu panel, do I need to create a Sound Controller to handler the background music and sfx?
Because there will be more than one View Component in the app, I am trying to send every sound inside a SoundEvent on every button to Sound Controller, and play the sound on Sound Model. Is this a good idea?
Or I just create a Singleton to handle for sound management?
#47 by jackson on December 8th, 2015 ·
I don’t think having a single
SoundController
makes any more sense than having a singleGraphicsController
. Each view should emit sound just like each view should emit graphics. For example, thePlayerView
might emit the “footsteps” sound and theEnemyView
might emit the “die” sound, just like they would have their own models and animations for graphics.That’s not to say that each view has to directly use the Unity API for graphics and sound rendering. You’re totally free to make classes that the views use to indirectly call the Unity API. For example,
PlayerView
andEnemyView
might both have a reference to anIGameSoundPlayer
with functions likePlayFootsteps
andPlayDie
. That kind of arrangement would give you a funnel through which all sound playing goes. You could implement aNoOpGameSoundPlayer
for testing purposes or just for a little silence during development. :)I hope that makes sense and answers your questions.
#48 by jeff on December 9th, 2015 ·
Then how will you put the AudioSource? And how to control the volume or mute the sound? Does this make sense to handle on a Model?
#49 by jackson on December 9th, 2015 ·
You could attach the
AudioSource
to theGameObject
in several ways. The way shown in the article is for the View to be aMonoBehaviour
attached to a prefab. That prefab presumably has all of the graphics, physics, and sound-related components the entity needs. So you could add theAudioSource
to the prefab you attach the View script to. Alternatively, you could add theAudioSource
in code by various means.As for controlling the volume (or mute), there are two places to do this in Unity. First, there’s the
AudioListener
which is like a camera for sound. There’s just one of these, so you can set the volume on that directly. You could do that with MVC quite nicely to accommodate a GUI control, for example. For that you’d want the usual Model, View, and Controller classes with an emphasis on the global sound volume. The View would change thevolume
property ofAudioListener
and take input from the GUI, the Model would hold the volumefloat
, and the Controller would act as the middle-man.The other place to change volume (or mute) is the
AudioSource
itself. In that case you’re changing the volume of individual sound emitters in the scene. For that, you’d treat it just like when you change the graphics of something individual in the scene. The entity would have MVC classes where the Model holds the volume and the view sets the volume of theAudioSource
. The controller is a middle-man who determines when to tell the View to change the volume.#50 by Tim on January 15th, 2016 ·
I had a couple questions.
1. Does using EventArgs like:
var eventArgs = new EnemyClickedEventArgs();
Cause garbage collection on every event? If you were sending events for every position update from the view to the model, couldn’t that cause tons of garbage collection?
2. Is there a reason you don’t allow the view to have read only access to the model, so the view could for example access the models speed with model.speed?
3. I’m not sure I understand the point of the controller here. If it’s just passing events from the view to the model and the model to the view, why not just pass them to each other and remove the controller completely? Can you give an example of logic that might be used in the controller?
Great article, Thanks!
#51 by jackson on January 16th, 2016 ·
Great questions. Here are my answers:
1.
EventArgs
is a class, as are any derived types. Instantiating a class (i.e. withnew
) creates garbage. So this would normally create garbage, but there are techniques to avoid this. For example. you could re-use a single instance for each event you dispatch since they’re not meant to stick around beyond the event dispatch. Alternatively, you could use a non-EventArgs
type based on astruct
to not create garbage at all. In that case you’d be doing more copying, which has its own costs.2. The view is supposed to be “dumb”. It’s supposed to be told what to display and simply follow orders. It’s not “smart” enough to know where to look for anything it’s supposed to display, let alone keep up with the changes in that. Of course you can vary from this, but this is the normal way to separate the concerns of each part in MVC. Doing so has various advantages including not needing a model to test a view, basing views of different types of models, and viewing the same type of model based on different criteria. In short, it moves the logic about what to view to the controller so the view can focus on how to view it.
3. The examples in the article do make it look like the controller is just a passthrough between the model and view and that’s largely their point. The term “fat models, skinny controllers” (or some variant) is often thrown around in MVC design. You can structure it however you like, but most people would say that either the model or the controller should have all the logic. The view is pretty much universally considered to be “skinny” or “dumb”, as described in #2. If you keep the model “fat”, i.e. the place with all the logic, then the controller is mostly there to coordinate the model and the view. It is “skinny”, but still quite useful because it frees the model from needing to update a view and take input and it frees the view from needing to monitor a model (see #2). This decoupling is really valuable.
#52 by Tim on January 27th, 2016 ·
Thanks for the great answers! Sorry for the delay, but I’ve been putting together a prototype using your MVC system and something to automatically create all the scripts for me (it’s a lot of framework classes!).
So far everything seems like it should work well, I did run the profiler on both caching and non-caching the EventArgs, and as expected there was tons of garbage collection on non-cached, but the cached version removed all of it.
While trying to mock up a game idea, I did run into another question. When you answered another question in this post you gave the example of:
“For example, if you had a Match Three game like Candy Crush Saga then you’d have one instance for each candy in the grid, one for the grid itself”
Can you elaborate on how you would structure that? for instance how would the Grid contain references to the objects within the grid? Would the GridModel contain an array of CandyModels?
Also, how would you populate those models into the grid? Communication between the MVC structures has been a point of confusion for me.
Thanks again for the great article!
#53 by jackson on January 27th, 2016 ·
That’s cool that you’re using this MVC system and even cooler that you’re making a code generator for it. I considered making a code generator for it, too. :)
Now for your questions about my Match Three game example. There’s a lot of leeway for you to make various choices about how to structure your data. That might depend on gameplay specifics of the particular Match Three. That said, I think most MVC implementations of the game would involve the grid (or board or level or whatever you want to call it) having references to the items in the grid (e.g. candies). You’d probably want them in a two-dimensional array since the game is two-dimensional, but you could also use a nested
List
, a one-dimensional array, aDictionary
or anything else.That said, your
GridView
doesn’t need to render all of the data in theGridModel
. It might just render the grid part (border, etc.) and leave the candy rendering up toCandyView
. You can decide either way.Populating is, I think, the easy part. The models are designed to be pure data without any interference by Unity in the form of rendering, physics, input, etc. This means you can do traditional OO-style object creation like this:
From there you set up your controllers and views based on the models you’ve created, just like in the article.
Best of luck with your project! Feel free to ask any follow-up questions that come up.
#54 by Chom1k on July 24th, 2016 ·
Hello there! I love your MVC explanation, to be honest I couldn’t grasp it for a little while and on top of that comments questions (i.e jeff’s ones) were very good and helped me to understand it better. Anyway I will try to implement it to my next projects but I’m little afraid of so no little to no variables in Editor.
I have few questions to ask if possible :)
1. Is it possible to connect this approach with UNET? I think it will be kind of a problem because model would have to derive from NetworkBehaviour to be able to use [SyncVars] and such or is there work around?
2. I’m still not quite sure how should View connect with UI. If I want to add UI element at runtime who should create it? Should controller give an order and View instantiate it?
I think I need to read this article one more time :) Anyway thanks!
#55 by jackson on July 24th, 2016 ·
Glad to hear you enjoyed the article. :)
As to your questions…
1. Sure, you can definitely connect this with UNET. If you need to expose any variables to the Unity Editor, including for UNET purposes, I recommend you check out this article.
2. The view should definitely handle creating the UI element. It should also handle input related to that UI element, such as clicks. The view is meant to be dumb though, so it doesn’t even have a reference to the model or the controller. Instead, the controller should have a reference to the view and the view in turn has references to the UI elements. The controller listens to the model events and updates the view accordingly when they change. Since you’re already going to re-read the article, I suggest mentally swapping out every instance of
GameObject
forButton
or some other UI control.Have fun with MVC and let me know if you’ve got any more questions!
#56 by Chom1k on July 25th, 2016 ·
Thanks for the response, I already read it again and I will do it again and again for sure! :) I’m currently working on my project so I don’t want to mix code and I will stick with my old methods but next one will try this out and I feel I will love it because (since I am a beginner) I always felt that there need to be some design to do it “properly” with good flow and to do it clean and I think I found it here :)
To end this suck up :D
As you said Controller is the middle man so it’s directly influence View and Models properties right?
Or can I use events all the time? Are there any cons from directly calling a methods/changing properties from Controller? What is the best approach? If you could list up what can I directly influence and from where please :)
Thanks!
#57 by Chom1k on July 25th, 2016 ·
Ohh… I just realised that only Controller has reference to Model and View and Model and View have only reference to itself so they cannot listen for events only Dispatch them, am I correct?
#58 by jackson on July 25th, 2016 ·
Right. Controllers coordinate models and views. A controller takes user input from a view and uses it to update a model. It also takes data from a model and outputs it to the user using a view. Usually the way it takes in the input from the view and the data from the model is via an event. It then changes the model or displays via the view using property setters. Something like this:
#59 by Chom1k on July 26th, 2016 ·
Thank you very much :) I just went to that conclusion yesterday at night about that Controller and how important the interfaces are in this pattern because you only change things from interface not from model/view directly! Now it’s so clear. Sorry Im slow sometimes hehe
Cheers for help! :)
#60 by Chom1k on July 27th, 2016 ·
Hey again I was just wondering how can you manage situation where you add view content in runtime? E.g if I add 10 buttons on runtime how should I add listeners on controller? Sorry for that newbie question :)
#61 by jackson on July 27th, 2016 ·
No problem at all. I’m happy to answer all types of questions. :)
To create more buttons at runtime, just have a controller do the same thing you did to create the buttons at startup time. You might consider using a factory, as in the article, to make creation easier and more testable.
#62 by Chom1k on July 29th, 2016 ·
What if I have a view with many fields like some kind of form with main Submit button? Should I keep it within one view or should I split it? I was thinking about splitting it but it kinda makes it hard to collect all the data (e.g event for a every single field). Any ideas/thoughts about it please? :)
#63 by jackson on July 29th, 2016 ·
Just how finely you divide your MVC components is up to you. The system is flexible enough to allow you to have one view per button, one view for the whole form, or anything in between. Keep in mind that there’s no need to have a one-to-one correlation between models, views, and controllers. You could have a single model for the whole form but a different view for every UI element. In the end you should decide based on your individual application and what makes sense to logically group together.
#64 by Chom1k on July 30th, 2016 ·
Glad to hear that I am not limited to some fixed numbers :) I was just wondering how can I gather all the data and wait for all forms to send it before collecting and sending it else where (they need to be send as one being) should I use Tasks or other async operation? What would you chose? Cheers!
#65 by jackson on August 2nd, 2016 ·
Unfortunately C#
await
andasync
keywords aren’t available in Unity’s Mono/.NET implementation, but we can work around that pretty easily.Consider a simple form consisting of two text fields for username and password and a submit button. You might have these classes:
FormModel
UsernameController
PasswordController
SubmitController
UsernameView
PasswordView
SubmitView
The form model holds the username and password strings. The username and password views display the text fields and have events for when their contents change. The username and password controllers listen to those events and update the form. The submit view displays the button and has an event for when it’s clicked. The submit controller listens to that event, reads the username and password strings from the model, and performs the form action (e.g. make a web call).
#66 by Chom1k on August 3rd, 2016 ·
Thanks again! I just started using your MVC approach and I love it! I don’t use factories yet though I will leave them for later, don’t need them for now.
Anyway quick question. You said it’s a good approach to have 2 controllers with the same model but what about single controller with 2 models?
#67 by jackson on August 3rd, 2016 ·
Of course. Imagine the same form example from my previous comment where the
FormModel
is split into theUsernameModel
andPasswordModel
. The username and password controllers would then have just their respective models and the submit controller would have both. This would have the advantage of making it impossible for the username controller to modify the password model and visa versa. It would have the downside of additional complexity, especially for the submit controller.#68 by Chom1k on August 3rd, 2016 ·
You also said that it’s better that one controller manages one view but can different controller access the same view?
#69 by jackson on August 3rd, 2016 ·
You can set it up however you want. If you want two controllers to modify the same view, you can do that. I think that more often then not you’ll have controllers “fighting” for control of the same view though, so I usually recommend against it. One way to help with the conflict between multiple controllers is to make all but one of them read-only like this:
This way many controllers can “read” the events but only one of them can “write” by setting properties.
#70 by Valerie on August 3rd, 2016 ·
Thanks so much for this awesome MVC implementation example for Unity. I’ve just started my second Unity game, and was in the process of using the MVC implementation found
<a href="https://www.toptal.com/unity-unity3d/unity-with-mvc-how-to-level-up-your-game-development" rel="nofollow">here</a>
, when I came across your article. I know there are multiple ways of doing things, including MVC. I’m just so new at this that I wanted to ask you your opinion…My understanding (which could be way off) of the referenced links way of MVC (for Unity) is that it uses one main controller to handle communication between all of the models and views. The article does mention that “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.” I’m assuming this would mean for example, a PlayerController in addition to the main AppNameHereController. This approach seems very different than your examples where all views have their own controller.
Since I’m so new at this I don’t know what to do with respect to controllers and how many to actually have. Which method is better if they both achieve MVC? I know it comes down to personal preference, but I thought I’d see what you think. Maybe there’s no difference between your method and the one I linked, and I’m assuming they are different because the accompanying example (BounceApplication) is too small to illustrate. It’s just confusing to me. Any help or thoughts from you would be great. I’ve already bookmarked your article so I can re-read it a few times, so thanks again for your work here and all of the comments you’ve replied to. You have helped countless developers like me :)
#71 by jackson on August 3rd, 2016 ·
You’re very welcome. I’m glad you enjoyed the article. :)
It’s true that my MVC approach utilizes more than one controller. At least that’s true for any non-trivial application. For example, I’d have a model, view, and controller for each player, AI, enemy, obstacle, etc. If there was just one controller then you’d have a massive class that has to handle everything in the whole game. That’s way too much for one class and will lead to a huge headache trying to keep it all straight. So I’d rather split up the responsibilities to keep thing’s simpler to implement, understand, and extend.
Also, I don’t use strings to differentiate events for various reasons including that it’s slow, error-prone, hard to read. Consider this string-based approach: (not necessarily identical to the linked article)
And here’s a version without strings:
In short, I’d use many controllers and avoid strings for communication. Remember that models and controllers are really cheap because (in my version) they’re not a Unity component like
MonoBehaviour
. Use as many as you want and enjoy the simplicity of breaking your game down into small, logical chunks! :)Hope this helps. Let me know if any more questions come up.
#72 by Valerie on August 5th, 2016 ·
WOW, thank you sooo much. This is some great information. I love you! JK.
You mentioned that for example, you might have M,V, and C for Player, AI, Enemy and Obstacle. I understand Player and Enemy, and obstacle, but how does AI get an MVC or for what reason?
THANK YOU a-million times!!
#73 by jackson on August 5th, 2016 ·
You’re quite welcome. :)
An AI would use MVC just like the player or enemy would:
AiModel
,AiController
,AiView
. The AI controller would then be able to apply the AI logic to simulate the entity in a game-appropriate way such as chasing a player character. You can think of this like theUpdate
function of aMonoBehaviour
: it’s the scripting that controls the game entity.#74 by Valerie on August 5th, 2016 ·
Last request, if at all possible do you have a sample project that you don’t mind sharing? It really helps to see a unity project in action. Even if you just email it to me privately, it’s for learning purposes. Maybe some project that you never finished?? Thanks!
#75 by jackson on August 5th, 2016 ·
Sorry, I don’t have a sample project to share. Perhaps in a future article about MVC I’ll create one and post it for reference.
#76 by Valerie on August 5th, 2016 ·
I know the best way to learn is by trying to figure out a solution on your own. Using the example application outlined in your
<a href="http://jacksondunstan.com/articles/3137">Finite State Machine </a>
article, would an MVC approach have the following classes:PlayButtonView
PlayButtonController
TextView
TextController
MainMenuModel (holds frame count)
MainMenuView
MainMenuController
TargetView
TargetController
PlayView
PlayController
My thought is to convert this
<a href="http://jacksondunstan.com/articles/3137">FSM</a>
example to one which utilizes your MVC pattern for Unity. From there I hope to try to implement a solution for my project; starting from scratch of course.Am I missing any classes?
#77 by jackson on August 7th, 2016 ·
That’s a good, small project to convert. The choice of classes and design of the MVC system is up to you, of course. One approach would be to look only at the desired functionality that’s achieved by the FSM example and duplicate that without looking at the FSM code that implemented it, since that may negatively influence your design. You might even take an example (e.g. Pong) to make sure you don’t have the source code to tempt you.
#78 by Valerie on August 6th, 2016 ·
Can you look at this attempt at converting the FSM example to MVC, and just let me know if I’m doing it correctly? That is, without breaking any pattern rules or if you see any problems. Here’s what I’m wondering…
1. If the constructor of the MainMenuController is the correct place for creating instances of the FrameCountController, etc. This runs fine as best I can tell. I’m going to move forward with PlayButtonModel, View and Controller, which will also be constructed in the MainMenuController.
2. Regarding the FSM and implementing transition to PlayState, anything I should be doing here with respect to a PlayModel, PlayView and PlayController?
Any thoughts would be greatly appreciated.
Here’s the FSM class method I’ve converted:
And here is the MainMenuController:
Here’s the MainMenuView which provides an interface for getting the FrameCountView GameObject, which is needed by MainMenuController for constructing the FrameCountController:
Here are the FrameCount classes M,V and C…
#79 by Valerie on August 6th, 2016 ·
Dang, sorry I used the wrong code blocks.
#80 by jackson on August 7th, 2016 ·
No problem; I fixed the formatting. Here are some notes:
1.
MainMenuState.BeginEnter
doesn’t needif (!(controller is MainMenuController))
since it can’t happen. Either the constructor throws an exception or it returns aMainMenuController
.2.
MainMenuController
’s controller shouldn’t create the frame model, view, and controller. Instead, create it outside (e.g. inMainMenuState
) and pass it in to the constructor as a parameter if it’s needed at all (it’s currently not needed).3. Views like
IMainMenuView
shouldn’t expose implementation details like theGameObject GetFrameCountView ();
. Instead, expose functions on a more logical level (e.g.GetPosition()
,SetHealth()
).4. Frame count is rightly a property of the
FrameCountModel
, but then improperly duplicated and provided byFrameCountView
. The model should be the “source of truth” for this data with the view simply displaying it. In this case, you can drop theUpdate
function andOnFrameCountUpdated
event fromFrameCountView
and instead update theFrameCountModel
elsewhere. Alternatively, you could downgradeOnFrameCountUpdated
to justOnUpdated
and let the controller change the model. In both cases the view has to get out of the business of calculating and storing the frame count.MVC is very flexible, but try to keep to the core tenants. Models store data. Views do I/O. Controllers coordinate them. It seems like you’re mostly on the right track so far.
#81 by Valerie on August 8th, 2016 ·
Thanks Jackson, my code looks MUCH better and I really appreciate your help. I have a couple of questions:
1. Using the FSM example, let’s say I have a PlayEasy and PlayHard levels. So the PlayEasy is your example with the 3 targets, and the PlayHard is the same but with 8 targets for instance. Say the user clicks PlayHard button, would PlayState -> BeginEnter() then spawn 8 targets, or would that be better handled by the PlayView (assuming I have PlayModel, PlayView, and PlayController)? I guess what I’m really asking is shouldn’t the views themselves be constructing their UI (prefabs or primitives), rather than the IState classes? Since I’m converting the example, I just want to be sure I see what would be the best way possible.
2. When I test the case above, there were times when I clicked the play button too fast causing the button handler to dispatch a state change event. This dispatch (I’m assuming) kept the MainMenuState -> EndExit() from being called. This caused a blank screen; an app in limbo. So I moved the MainMenuState += HandleClick assignments to the EndExit() method. This prevented my fast button clicks from being handled, until such time that the EndExit() was called and the assignments made. The button clicks would then properly launch the transition to PlayState. This is okay as a work-around but doesn’t actually solve the problem. Either I’ve coded something incredibly inefficient, which is certainly reasonable in my case, or what??
I hesitate to post more code, but figured it might help others:
#82 by jackson on August 8th, 2016 ·
Happy to be of help. :)
1. Views should be “thin”. They simply collect input from the user and display whatever the controller tells them to. They shouldn’t do anything complex like setting up a state of the game or even constructing more views. Controllers are the place to do this kind of setup, but it’s even better if you move this setup code out into a factory class. That way you encapsulate all the code that sets up a game state so it’s not mingled with the rest of the controller. The controller can then simply use the factory to set up the state.
2. You should only subscribe to an event when you’re ready to handle it. If the play button isn’t ready to be used, keep it “disabled” by not subscribing to the event yet. Your approach of moving the subscription to
EndEnter
seems reasonable to me. On the other side, you should unsubscribe from events when you no longer want to handle them. In this case you don’t want to handle the play buttons twice so you should unsubscribe as soon as they’re clicked in theHandle...
functions rather than allowing for more clicks untilEndExit
is called.By the way, I’ve had a note in my list of article ideas to do a FSM+MVC article for quite some time now. Perhaps you’ll see something from me along these lines sometime soon…
#83 by Valerie on August 9th, 2016 ·
Awesome, thanks!
Regarding the factories, which I see now can come in very handy, how would I structure for example the PlayState -> BeginEnter() method
Here’s what I think it should look like:
Here’s the original method (from FSM):
Not sure if I should be using TargetView and TargetController either.
Any suggestions?
Thanks Jackson!
#84 by jackson on August 9th, 2016 ·
Check out the article’s example of factories for models, views, and controllers. You’d use those factories to create the MVC components wherever you want to create them, such as
BeginEnter
. You might even make a factory that uses the model, view, and controller factories to create them all with one function call.#85 by Valerie on August 10th, 2016 ·
I posted a follow-up reply, but it’s not showing up on the page. Is it due to the level of nested comments? Let me know if you got it. Thanks!
#86 by jackson on August 10th, 2016 ·
I don’t see any comments of yours that I haven’t replied to. I guess it could be due to the indent level, but that’s never happened before. Can you re-post the comment here?
#87 by Valerie on August 11th, 2016 ·
part 2 of the comment…
PlayViewFactory…
PlayControllerFactory…
TargetView…
Thanks again for all of your support, this has been the best learning experience!
#88 by Valerie on August 11th, 2016 ·
Part 1 of the comment…
I think this should be the last time I bother you Jackson, as this example is working, but I wanted to get your opinion on how I structured everything.
I struggled a lot with where the targets should be created. For example, is this a “controller factory” job or a “view factory” job.
Also, I didn’t solve how to set the color of the targets to green (which indicates they are ready to be clicked). In the FSM example, this happens in the EndEnter() method; which IMO is the BEST place to do this. My thought is to save a reference to the ITargetView array in the FSM, and then when EndEnter is called I can set the color on the views. But should the FSM really be setting properties of the view, or is this a duty of the controller?
I’m not sure if passing the array of ITargetViews in the constructor of the controller factory is the best way to do things.
Since moving the code in MVC, I don’t actually destroy any canvas objects. Nor have I figured out when/how to remove listeners inside of the MVC classes. Basic cleanup stuff the IState classes were doing this, but now it’s TBD.
Let me know what you think…
Here’s the code:
#89 by jackson on August 11th, 2016 ·
Here are some thoughts about these four sections of code:
ITargetFactory
into thePlayState
constructor to allow for better testing.TargetView
is, but what’s aPlayView
? APlayController
makes sense if you want a sort of “master” controller, but there’s no need to add the model and controller for it just to have all of the MVC parts.bool IsReady
in the model for its controller to listen to changes and forward along to the view. This can then be set in yourEndEnter
.#90 by Valerie on August 12th, 2016 ·
Great points. Regarding the PlayState constructor injection of ITargetFactory, awesome concept that I had to research in order to understand. So let’s say I implement this ITargetFactory. Should it be responsible for creating 1 target, or x number of targets? I considered it and decided it would be best to have it responsible for constructing the necessary MVC for 1 target. Correct me if I’m wrong, but If that’s the case, then if a level had 3 targets to hit, I could then create another factory class which would be responsible for creating the x number (3 in this case) of ITargetFactory objects. Maybe call it IPlayFactory. If this is also correct, then I would inject IPlayFactory into the PlayState.
What do you think, pretty good idea?
My only small struggle if the above is good, is I would need to notify the targets when it was time to turn green. I used your idea and went with IsReady, which worked great. Now I want to use this IPlayFactory/ITargetFactory idea, so if I rewrote the code to use it (and then inject it into PlayState constructor), I expect I would need to save a reference to the playFactory object and then in BeginEnter I would use a getter to retrieve all of the ITargetFactory models, which I could then loop through, updating
IsReady = true
. This is what I came up with, but it seems to me I’ve got this part wrong.BTW, please feel free to email if you’d like me to stop pestering you with a million questions. Thanks again Jackson!
Valerie
#91 by Valerie on August 14th, 2016 ·
No need to reply Jackson, I’ve re-worked my design and have settled on an Abstract Factory method for an IGame product. This is passed view the constructor to the PlayState FSM. Works like a charm!
Thanks for all of your help!
Valerie
#92 by Valerie on August 11th, 2016 ·
part 3 of the comment…
#93 by Valerie on August 11th, 2016 ·
last part 4…
#94 by Chom1k on August 17th, 2016 ·
You said that connecting it with UNET is possible but will [Commands]/[RpcClient]/[SyncVar] work in Models? Or should there be implemented some kind of event forwarder? Could you give me some insight, examples how should it work? AFAIK to use e.g [SyncVar] you need to derive from NetworkBehaviour and we try to avoid that :)
#95 by jackson on August 18th, 2016 ·
Nothing about MVC will change the requirements to use UNET. You’ll still need a
NetworkBehaviour
with those attributes on it. Keep in mind that it extendsMonoBehaviour
and that network traffic can be considered another kind of input (like touch, mouse, keyboard, and joystick) and output (like graphics, sound, and vibration). Since your views are already extendingMonoBehaviour
, it makes perfect sense to convert them to extendNetworkBehaviour
so you can use them as I/O for UNET.#96 by Chom1k on August 18th, 2016 ·
But e.g fields/properties like health and that kind of stuff should be kept on model don’t they? And health should be a SyncVar and SyncVar should be on NetworkBehaviour – View? Sorry I am a little bit confused :X
#97 by jackson on August 18th, 2016 ·
Think of the
SyncVar
as a form of input like a keyboard, not like data stored in a model. Nobody consults the view to get the current state of the data. Instead, the view dispatches an event when theSyncVar
changes and the controller handles that event by setting a property on the model. TheSyncVar
is just a mechanism to know that the model should be updated, not the model itself.#98 by Chom1k on August 22nd, 2016 ·
Thanks :) What about situation when View has the same output/input?
E.g one View depends on getting clicked/hovered whatever, should both Views raycast that thing in their seperate views or there should be like major view for handling inputs like clicking that, pushing that button etc?
#99 by jackson on August 22nd, 2016 ·
Say you have a light switch and a light. The light would go on and off like this:
OnToggled
eventIsEnabled
propertyOnEnabledChanged
eventIsEnabled
propertyDoes that answer your question?
#100 by Chom1k on August 22nd, 2016 ·
I think you didn’t understand my question or I described it poorly :) Anyway I meant something like that:
I click on a Player and a View catches it and dispatches an event and then controller gets it etc. like you described BUT there is another MVC which need the same event onClick.
So my question is, should I duplicate the same code in 2nd View to catch an onClick thing or is there better solution? Because I would duplicate Raycasting twice in 2 different Update functions.
#101 by jackson on August 22nd, 2016 ·
Ah, I think I understand now. You could share the view between two controllers. Since you probably want to avoid having conflicts between those controllers both changing the view, you could use a read-only interface technique like this:
This way all the controllers can share the same
OnToggled
event, which was generated by ray casting. Only one of them can change the view since only one of them has an interface with the ability to setIsToggled
.#102 by Chom1k on August 22nd, 2016 ·
Exactly what I wanted to hear/read :) Thank you very much! :)
In that case if I have for example:
– Model A, Controller A, Write&Read View A
then if I want to read from View A:
– Model B, Controller B, Read View A and only Read View A, no space for it’s own View B right?
#103 by jackson on August 22nd, 2016 ·
Almost. Controller A has a Write&Read View and Controller B has a Read View, but both views are the same instance of the same class. It’s just that Controller B doesn’t “see” the write functionality on the read-only interface it gets, so it can’t “write” anything.
There’s no need to duplicate the code to make one read-write class and one read-only class. :)
#104 by Chom1k on August 22nd, 2016 ·
Yes I understood that part :) I meant that if Controller B has readonly View A it cannot have it’s own write/read View B?
#105 by jackson on August 22nd, 2016 ·
If you have a read-write view, why bother also having a read-only view? The idea would be that each controller has exactly one view and exactly one of those views is read-write with the others being read-only. That means all the controllers have read-only access to the same view and one of them also has write access to that same view.
#106 by Valerie on August 27th, 2016 ·
I’m trying to remove the mono script on my player game object, rewriting as pure code MVC. I was curious how I should handle the user touch input. I wrote it so that the view simply sends an event when the joystick is touched. So far so good. I want the controller to handle the touch event and set a property on the model, however it’s not as simple as your Vector3 example above. I’m using Rigidbody MovePosition() to move my vehicle in an arcade style.
Here’s the old script code Move() method that does the work to move the player…
Here’s the new PlayerView code…
Lastly, here’s the player controller handling the touch input…(compile error in code)
The player controller receives the event and then needs to update the player’s model; typically with set Position. I’m trying to do fat models skinny controllers, but either way the transform.forward is required. How do I get it? And I guess if I was to do this correctly the speed and delta time would be part of the logic in updating the player’s model Position. I’m getting closer, but still have no experience with little problems like this.
Thanks Jackson!
#107 by jackson on August 27th, 2016 ·
There seem to be two questions here. First, how do you apply the physics force to your rigid body? For that, you’ll need to pass the force to the view so it can be applied. Think of physics like an output similar to graphics or sound. Second, how do you get physics data such as the position and orientation? For that, think of physics like an input. When the value changes, the view emits an event for the controller.
It’s actually not too different from a controller with vibration support. It’s both input (buttons) and output (vibration). You can capture the input via view events and optionally cache it in your model for other controllers to access. You can output to it via view properties.
#108 by Valerie on August 28th, 2016 ·
Brilliant Jackson, thanks! I did manage to get things working using your suggestions for view input/output and it works great. I’m sure it’s not perfect, but it’s the best my little brain can come up with at the moment. I wanted to ask about cleaning up as I haven’t focused much effort here and now that I have more than a few event listeners, without destroying them I believe it’s having an affect on my testing. That is, I’m seeing drops in frame rate as I start and stop the game in Unity. I’m assuming it’s due to memory leaks as restarting Unity brings the frame rate back up to 60fps. In any case…
As you suggest, I use a factory to construct my model, view and controller. The factory interface provides getters for the Model, View and Controller Interfaces. For example, IPlayerFactory.Controller returns IPlayerController. Using the factory pattern, if I want to tell the controller to remove event listeners I would have to provide the interface for the method Destroy() correct?
like so…
And yet if I skip the factories, like so…
Then I simply call
_frameCountController.Destroy()
I’m not advocating for the use of factories or not, just wanted to be sure I was connecting everything correctly when using them. I prefer them actually…
Thanks again Jackson!
#109 by jackson on August 28th, 2016 ·
Factories are for creating the model, view, and controller instances. Those instances add event listeners, create game objects, open files, etc. That’s the part you want to clean up, so you should tell those instances to clean up. The normal way in C#/.NET is to implement the
IDisposable
interface and add aDispose
function. That’s an ideal place to do whatever cleanup you need to do. If you want to go further, you can throwObjectDisposedException
if anyone calls your functions after they callDispose
.#110 by Valerie on August 28th, 2016 ·
Thanks for the help here. I did some research on the IDisposable, and it seemed pretty straight-forward. Based on my simple understanding, I would need to properly scope the use of my class or the Dispose method won’t get called. I’m not sure how this would work on a controller as like you’ve said, sometimes we just construct a controller so that it can do its job.
The other problem I see with Dispose/IDisposable is I read: “…the using statement generates a type-safe implicit cast to IDisposable. Unfortunately, this prevents the using statement from being used with interfaces”
So I’m assuming you were just giving me extra information about c# programming in general, and not actually intending for me to implement this, but let me know if I’m wrong here.
Should I just create an Interface method for my controllers, like a CleanUp(). This seems like a lame solution, not very graceful that is, but certainly do-able.
#111 by jackson on August 28th, 2016 ·
I just meant that
IDisposable
andDispose
are more common than function names likeDestroy
orCleanUp
, so you might as well go along with the convention.Dispose
is super common in .NET and even Unity uses it, such asWWW.Dispose
. Theusing
block feature is just a bonus and probably, as you point out, not one that applies here.#112 by Valerie on August 31st, 2016 ·
Hi Jackson, would love an article about how to Unit Test your MVC pure code approach with Unity. You mention the benefits of interfaces and factories several times, so I thought I’d check and see if maybe you could provide a simple example; even just using your MVC example above.
Thanks!
#113 by jackson on August 31st, 2016 ·
Since the view derives from an interface, you can swap it out. For example:
Since the controller takes an
IEnemyModel
we can implementFakeEnemyModel
and pass it to the controller. We don’t need a realMonoBehaviour
orGameObject
. Instead of real mouse click input, we fake it by callingDispatchOnClicked
.Factories are just an extension of this concept. You can make a
FakeEnemyViewFactory
that implementsIEnemyViewFactory
and createsFakeEnemyView
instances.#114 by Valerie on August 31st, 2016 ·
Is it “okay” for the view to provide an interface for another script on the game object?
Background: I have an IEnemyView at the root game object for an enemy prefab. This enemy moves via Simple Waypoint System (SWS) path object and a “navMesh” script on the enemy prefab.
My thought was to modify the IEnemyView like so:
I have to ask how you would approach this because I’m sure it will blow my mind LOL.
Thanks.
#115 by Valerie on August 31st, 2016 ·
Wish I could edit my previous post…
Just wanted to add that in-order for the runtime enemy to work I must connect the path object from a waypointManager to the navMesh script. This is what I’m trying to solve. Maybe I’m not looking at this from a deep enough angle though…hmmm.
#116 by jackson on August 31st, 2016 ·
It’s hard to say what you should do without knowing how the “waypointManager” and “navMesh” work. I’m also a bit confused about why you’d need to modify the view factory. Regardless, I’m guessing you’d treat the navigation system similar to the physics system I mentioned in this comment. For example, you’d have a
Destination
property on the view which would feed that into the navigation system to start moving the enemy via a nav mesh or something. This is like an output, similar to sound or graphics. The view would also dispatch events telling the controller that it’s position and orientation had been updated. This is like an input, similar to mouse or keyboard buttons.I hope that’s somewhat close to what you were asking about.
#117 by Chom1k on September 2nd, 2016 ·
Hello, it’s me again :) I just wanted to ask what are the best approaches to talk to other MVCs?
Model A to Model B communication for example. Should I use Mediator or something like that?
Examples would be nice.
Cheers!
#118 by jackson on September 2nd, 2016 ·
Sure, you could use the mediator pattern. Or you could pass a model to another model, just like you pass it to a controller. That’d establish a link where one model could read from and write to another model. Something like this:
In this example the
PlayerModel
andShopModel
share aWalletModel
.Or coordinate via the controllers. There are many options to choose from. You’ll have to choose based on your own app’s design, of course. You may even be able to merge the models together so the problem simply doesn’t exist.
#119 by Valerie on September 4th, 2016 ·
I have a similar problem…
I create a a player MVC
I create an enemy MVC injecting the IPlayerView into the enemy controller constructor, so that the enemy can handle player position updated events. The enemy sleeps while the player is far away, patrols when the enemy is within range, and attacks within a tighter range. I’m not sure if this is ideal, but for now this is the simplest way I could get it working. So when in attack state, the enemy stops patrolling and “looks at” the player, and fires a shell at the player every 5 seconds while in this state.
The shell projectile has an interface for MVC as well, but the player is not aware as the shell instances are created on demand by the enemy when attacking. So while the shell can “apply force” to the player rigid body, I don’t see a way for the player to receive events from the shell. The event data for the shell hit is a structure, which I was hoping to use in the calculation for applying damage as a percentage of player health, and vise versa when a shell fired from the player hits an enemy. I was hoping there was a way that that the player (and enemy) could receive the ShellHit data when they were hit.
I’m trying out structures as you suggested, so that I don’t have GC. For example…
Here’s the damage calc…
Once again I think I’m looking at this wrong, or possibly I’m just missing something. Just thinking out loud here…the player view is receiving the explosion force in the form of input. I just need to know a little bit about the input in order to calculate the damage amount. If I had to guess, I could look at where the player was, before the force and then where the player is positioned after the force and see what the difference is.
Does this seem like the right way to look at it?
Thanks Jackson.
#120 by jackson on September 4th, 2016 ·
I think the core issue here is that the enemy controller has the player view rather than the player model. The player view is really just for input and output of the player, not as an authoritative source on where the player is, how much life he has, etc. That’s the player model’s job.
So instead, try coordinating the enemy, shell, and player via models rather than views. For example, the enemy controller uses the player model to get the position (manually or via events) and changes state based on that. If the enemy decides to shoot the player it creates a shell model, view, and controller and sets the shell model’s
Target
to be the player model. The shell’s controller updates by moving the shell toward the target and then setting the player model’sHealthPoints
when the shell hits. The player controller hears the event that the health points changed and updates the player view accordingly, such as by flashing red or playing an “ouch” sound.#121 by Valerie on September 4th, 2016 ·
Okay thanks Jackson. I posted a follow-up before seeing this response. This is great and I will implement the solution for the enemy shooting the player. I still need to work out the reverse, where the player is shooting the enemy; but doing this might “help” expose what needs to happen there. First things first…
Thanks for the help, you’re super awesome. : )
#122 by Valerie on September 4th, 2016 ·
Thanks for the previous help with the unit testing example. It’s gonna take me a few re-reads and actually “doing” before it makes better sense, but I really appreciate your taking the time to show examples.
Regarding my last post, I think I can solve it for the player by using the following:
pseudo code…
Enemy attacks: constructs a Shell view and controller, injecting IPlayerModel to the controller. When the controller receives the OnHitEvent, it can update the player health via property _playerModel.TakeDamage.
The problem is when the player attacks the various enemies spawned in the game level. The player was constructed before the enemies. That is, the player doesn’t have the references to IEnemyModel in order to inject into the Shell controller. My thought here, which I’m hoping you can confirm, is to use a Setter Property injection or Method injection.
This seems kind of like a hack job, but it might work. The alternative is I think that I can’t do MVC with an actual commercial Unity Game project. I know that may seem like a big leap, but I’m hitting a lot of brick walls while trying to develop my game in a modular fashion. Too many times I see where objects need to communicate and I can’t figure out how to get there. I’m hoping this is simply due to my inexperience with not only design patterns, but with MVC, as opposed to the reason why you don’t see MVC used in many games.
I don’t want to give up because using this modular style seems way more natural (and correct) than the mess I end-up with when using a ton of component scripts. Lastly, even if I manage to complete the first level with players, enemies, sounds, animations, etc. all on a mobile platform, I’m not sure what the performance hit will be due to all of the “events”. I’m worried the mobile game might not perform well enough and I’ll have to scrap the MVC architecture altogether. We will see (hopefully). The good news is, so far the frame rate has been a constant 30 fps on my mobile device. The only slight hesitation I see happens when the enemy is switching between states, the player tends to hesitate.
Thanks for your encouragement Jackson!
#123 by Valerie on September 10th, 2016 ·
What’s the best way to handle my shooting projectile which has 3 sounds: charging the projectile, firing, and exploding on impact.
I have a shell (projectile) model, view and controller. The view is attached to a shell prefab which has the graphics, physics, and sound-related components it needs; except for the clips. The audio source has a reference for 1 of the clips to be assigned.
I thought about creating prefabs for the other 2 clips, each with a View and Controller. The controller would listen to events from the projectile and emit sounds when required.
Or I could add public AudioClip references to the View script and make connections to the wav files via the editor.
I was just curious if you had any suggestions.
#124 by jackson on September 10th, 2016 ·
Those sound like three states: charge, fire, impact. You could store the a
ProjectileState
enum on the model. When it changes, tell the view to play an appropriate sound, such asPlayChargingSound()
. The view should take care of the internal details such asAudioClip
and WAV files.#125 by Chom1k on September 13th, 2016 ·
Hello again Jackson, thanks for help for previous post!
Anyway I got a new question, if I send Inputs to model every update (for example movement) and I send back the results and I need to lerp it or multiply it by Time.deltaTime and such, where should I do it?
Should I rather do that everything on model (lerping/Time.deltaTiming etc.) or instead sending movement vector and then multiply it and lerp that on the View?
I hope you understood .
Cheers!
#126 by jackson on September 13th, 2016 ·
Either the model or the controller, depending on your preference. The view should be really dumb: just blindly pass through input and output without applying any logic. Most people would argue that the model should be responsible so it’s not dependent on a particular controller to apply the logic. Hence the phrase “fat models, skinny views”.
#127 by Ryan on September 13th, 2016 ·
MVC was a design pattern conceived of for 2D GUIs. A ‘View’ in it’s actual context is a class which contains code that draws the actual elements on a page, namely a webpage, it constructs html to then be sent to the html renderer (web browser).
Unity has no equivalent to this, what actually constructs your ‘view’ in Unity is your stack of game objects and components. That constructs the data structure that will be sent to the renderer to present the ‘view’ of your game.
So you may say, you’ve adapted it. To what? A class that takes input for a player? That in no way represents what a view is in actual MVC context. You’ve simply butchered the entire MVC context and turned it into something else.
You may say, of course it’s not the same, some interpretation is needed because there is nothing directly corresponding to actual MVC. To which I would have to ask, if you recognize it doesn’t correspond to actual MVC then why are you are trying to butcher it into it some franken-pattern?
Because MVC is so good and great it needs to be made to work for everything? Please go to google and type in ‘Problems with MVC’. It is, at best, an amateur design pattern that may be suitable for small to mid size sites or apps that revolve around 2D GUI. It is hardly anything close to ideal, nor anything to aspire to.
Why not think of a design pattern and paradigm that is actually gestated in the problem domain which you are working? Instead of trying to painfully and abstractly contort one from a far different problem domain?
Then you may end up calling and thinking of a MonoBehaviour what it actually is, a MonoBehaviour. It’s named MonoBehaviour and not View for a reason. Unity themselves are not simply just ignorant to the great MVC and thus decided not to use it. Epic games are not simply just ignorant to the great MVC and thus decided not use it any way. It’s because MVC, a pattern inadequate for extremely large and complex apps in it’s own problem domain (2D GUI), is even more inadequate for large and complex apps not in it’s own problem domain.
#128 by jackson on September 13th, 2016 ·
Thanks for your comment! I welcome criticism as much as praise, so it’s nice to have a bit of balance to this thread after so much excitement to use the MVC approach described in the article. Now on to your points…
According to the history section on Wikipedia, MVC originated in the 1970s for Smalltalk-76. That’s well before the web. It’s also used in various related forms for all sorts of other problems, such as MVVM for WPF applications. So I think it’s fair that I’ve adapted it to yet-another GUI interface system.
As for that adaptation to Unity, adjustments had to be made just as they were for web pages. On a web site the view is used as both input and output. It outputs HTML for a web browser to render just like my views output game objects for Unity’s engine to render. The web page has links and forms that take input by contacting the controller just like my views take input from Unity and contact the controller via events. It doesn’t seem so wildly different from web-based MVC.
Lastly, I definitely never claimed that MVC is “so good and great it needs to be made to work for everything” or that Unity and Epic are “ignorant to the great MVC”. My opening line makes it clear that this is just another tool that Unity programmers have when architecting their apps: (emphasis added)
You seem to have thought about design patterns quite a lot, including for Unity. I’m planning on writing some followup articles to this MVC one to cover more architectural patterns. Would you mind sharing some that have worked especially well on your experience?
#129 by Ryan on September 14th, 2016 ·
MVC is before web, but it was concieved of for 2D GUI. I use the example of HTML because it’s the one I have used, I was not alive when they were doing 2D GUI is smalltalk 76. But I would guess it would be similiar. There would be a smalltalk class which actually describes the coordinates and position of elements.
I should also note that when it comes to creating 2D GUI in Unity, I am not entirely opposed to MVC. It’s rather the trying to take MVC to 3D interactive environments which I think is a great contortion and abstraction. I would still argue that in an Unity UI, your actual view, is essentially your stack of GameObjects and Components, thus having a View class for your UI would be irrelevant. In the same way that Storyboards can replace your View class in an iOS application, iOS gives you the option to use the visual editor or make a view class. Unity only lets you use the visual editor.
I personally argue for object-orientation. Which is itself not a pattern but is rather a mentality. The patterns you create will be dependent on the problem domain you are solving. It centers around object decomposition, the notion of how do you identify the major categories of behavior and conceptualize those as concrete an entity as possible.
MVC is something that comes from this mentality of object-orientation, but it’s decomposition was done around the problem domain of 2D GUI. These concepts of Model-View-Controller were conceived of to conceptually encapsulate a 2D GUI.
When we come to a game world and we now have a Player, Weapon, Item etc. etc. Conceptually these are exactly what they are. Your classes should be decomposed by these concepts, they are very concrete. To decompose those concepts further into MVC, concepts defined for 2D GUI, your priorly very concrete concept of Player now turns into essentially three abstract concepts PlayerModel, PlayerController, PlayerView. Where is a PlayerView in the real world? There is none, you only have a Player. The purpose of object-orientation is to decompose behavior around the most concrete of concepts possible.
The reason MVC was concieved of for 2D GUI, is because in that problem domain those three concepts were very concrete for it. That was exactly what 2D GUI frameworks at the time required you to do. Write something to draw your UI, write something to control your UI and write something to encapsulate a database. In the context of a 3D game they are abstract concepts. The actual visual of your car is a Car, or a Body. What actually moves your car around is an Engine. Those are the most concrete concepts to decompose around. To then decompose into MVC is decomposing into less concrete concepts for the problem domain.
I see the effort of people trying to take MVC into 3D game worlds as essentially an odd game of telephone. MVC was created for old 2D GUI frameworks, was heralded as great, and it was reasonable. This continued on for a while and MVC became it’s own thing. It’s goodness became justified by its own self. People forgot the reason why it was good is because it was a good decomposition of a specific problem domain. MVC now being it’s own good entity by itself, separated from the reasoning of it’s initial creation, seems appropriate to interject into other things. But really it isn’t. Just go back to object-orientation itself and decompose the behavior for your exact problem domain.
Unity itself is an alteration of this design:
https://en.wikipedia.org/wiki/Entity_component_system
Continuing something off of that mentality is more appropriate I think.
I would personally highlight this Unity project as a good example of this mentality in Unity:
https://github.com/Microsoft/GalaxyExplorer
#130 by jackson on September 14th, 2016 ·
Thanks for the thorough critique of MVC in Unity games. You make a lot of valid points in there. Unity is indeed built as an Entity Component System (ECS). Game objects have components on them, including
MonoBehaviour
. Directly adding scripts to game objects is definitely the most natural way to program Unity games. I’m sure a great many successful Unity games were programmed this way and will continue to be programmed this way for the foreseeable future.ECS, like any architecture, has its pros and cons. Simplicity is a huge pro. The difficulty of untangling a
MonoBehaviour
from the Unity engine so you can unit test it is a huge con. Likewise, MVC has a huge pro in that its separation of concerns makes writing unit tests for the “fat” model and controller parts really easy. It has a huge con in its added complexity compared to ECS. There are plenty more pros and cons to be listed for both architectures.In the end the decision is up to the individual programmer and their team, time and budget, specific game being worked on, and of course personal taste. This article simply aims to introduce Unity programmers to an alternative architecture. Perhaps some will find it useful for a project where its particular set of pros and cons outweighs those of the default ECS paradigm.
#131 by Ryan on September 14th, 2016 ·
Writing unit tests for a monobehaviour doesn’t require one to follow MVC.
Unity has a good blog post about it.
https://blogs.unity3d.com/2014/06/03/unit-testing-part-2-unit-testing-monobehaviours/
Although they do use the word ‘Controller’ to describe internal logic classes not dependent on a MonoBehaviour. But it isn’t MVC. They are just borrowing the name which is somewhat appropriate.
I’d actually argue in their context of ‘SpaceShipMotor’, their interfaces would be better named. IShipDrive and IShipGun. To append the name ‘Controller’ onto everything is strange, of course it’s controlling something. It’s not as bad as appending ‘Script’ onto the end of all your scripts, but it’s in the same direction of irrelevance.
#132 by jackson on September 14th, 2016 ·
It’s true that MVC isn’t required in order to write unit tests for your code. I only said that it makes it easier than a pure ECS approach since the model and controller are separated from the Unity engine by the view. For example, you can mock the view interface and easily fake the input and output.
I re-read the article you linked from Unity about unit testing. Their
SpaceshipController
is almost a controller in the MVC sense and theirSpaceshipMotor
class is almost a view. That’s because theSpaceshipMotor
deals with all the input (e.g.Input.GetButton</code.) and output (e.g. <code>Instantiate
) but none of the logic. The logic resides in the non-MonoBehaviour
classSpaceshipController
. That class is essentially the same as the MVC controllers described in the article: it’s not aMonoBehaviour
, it only uses the Unity engine indirectly via a view interface, and it performs all of the logic.There are differences, too. C# events aren’t used by the view/
SpaceshipMotor
class to communicate input to the controller/SpaceshipController
class. The terminology is different since the “view” is called a “motor”. There are no factories, though that was optionally in the article. Perhaps most importantly, there’s also no concept of a model, so Unity has effectively implemented a VC pattern: no “M”.So while Unity didn’t technically adapt MVC to make their code more amenable to unit testing, they do seem to have adapted the VC part. I think that lends some credence to at least the VC part of the architecture and perhaps represents a middle ground, a “third way” to design our code.
#133 by Ryan on September 15th, 2016 ·
I would agree putting code dependent on calls to Unity in one class with callbacks to another to do unit testing is a good design. I would even agree that the VC in MVC is an example of where a setup like that existed before. But you could find examples in any designs made for unity testing.
Where I diverge is in the statement of lets do MVC in unity, because I do not think MVC is a good system metaphor for 3D interactive environments, but will actually inhibit the codebase. For all the before mentioned reasons, it is a metaphor created for 2D GUI. A system metaphor needs to be decided upon for the specific problem domain. If you choose one that is not properly descriptive of the problem domain and then enforce it, it becomes a limitation as things increasingly must be contorted to fit into it.
This notion of a ‘Controller’ doing logic and a ‘View’ handling input is a good example when you have a single, or a few, classes. However if this then gets enforced as the design pattern on an extremely large codebase and everything that wants input must implement a View for input, this is then sidestepping what is potentially a better design pattern for this specific problem. That being a MockInput object. A view attached to a controller is a concept created to describe the drawing of 2D elements. But we want something to MockInput universally. Why then are we not just making a MockInput class? A game has input, we want mockinputs. Describe it as literally as possible. There is no merit in borrowing concepts from another problem domain which proved itself successful if the logic behind them is not exactly descriptive of the new problem domain. The role of the architect is to design the most specifically applicable system metaphor to the specific problem domain.
#134 by jackson on September 15th, 2016 ·
Naming is important, so I’d agree that choosing good names for the
MonoBehaviour
that does passes through input and output and the non-MonoBehaviour
that does logic on that input and output is something worthy of the architect’s thought and time. You could call them “view” and “controller” to match the MVC paradigm or something else to come up with your own naming convention. Here too there are pros and cons, namely familiarity versus specificity.You said earlier that you dislike adding “Script” to the names of
MonoBehaviour
classes, but that would be one alternative for the “view” to emphasize that it’s theMonoBehaviour
in the pair of classes. You could append “IO” or “Engine” or “Behaviour” or perhaps eschew a standardized name altogether and just name it a “Spaceship”, in the example you linked. Likewise there’s a decision to name the “controller” class. You could append “Logic” or something or, again, just name it “Spaceship” if you chose a different name for the “view”.Or, as you suggest, a standardized (“MockInput”) class could be used by various “controllers”. The obvious pros here are reusability and codebase size reduction. The obvious cons are a lack of flexibility to specific input and output tasks. Perhaps a toolbox full of “Input” and “Output” classes could be used by “controllers” depending on their needs. For example, a controller for a gun might only care about the “fire” and “reload” keys but a “match three” game (e.g. Candy Crush) might want to use a “SwipeInput” class.
In the end this design goes in the direction of MVC’s “separation of concerns” and gains many of those benefits, including making code more testable and reusable. It stops short of adding an explicit “model” to the paradigm, but for some projects that may be the way to go.
#135 by Chom1k on September 16th, 2016 ·
Hello again Jackson :)
I’ve question about “time” consuming or continuous logic in model like movement, when I click somewhere, get position from mouse input etc. and want to move there until I get there. It would have been easy to do it in MonoBehaviour (MoveTowards in Update) but how can I accomplish it on model?
One idea on top of my head is to pass IEnumerator to View and StartCoroutine from there for example, second would be creating MovementRequest event and raising it in every FixedUpdate and updating new position from model, what do you think?
Thanks!
#136 by jackson on September 16th, 2016 ·
Consider the fixed progression of time as an input provided by the view to the controller via an
OnFixedUpdate
event. The controller can perform logic on it directly or by calling a function of the model (e.g.FixedUpdate()
) to have the logic performed there.Alternatively, you could make an iterator function on either the controller or the model and have the view execute it as a coroutine. That keeps the Unity engine part (e.g.
StartCoroutine
) isolated in the view and the iterator function (i.e. the one returningIEnumerator
) testable outside of coroutines and the Unity engine.