{"id":355,"date":"2021-05-08T20:13:12","date_gmt":"2021-05-08T20:13:12","guid":{"rendered":"https:\/\/bronsonzgeb.com\/?p=355"},"modified":"2021-05-08T20:13:13","modified_gmt":"2021-05-08T20:13:13","slug":"model-view-controller-pattern-for-in-game-ui","status":"publish","type":"post","link":"https:\/\/bronsonzgeb.com\/index.php\/2021\/05\/08\/model-view-controller-pattern-for-in-game-ui\/","title":{"rendered":"Model View Controller Pattern for in-game UI"},"content":{"rendered":"\n<p>This article describes an approach to structuring UI in Unity. This approach is based on the Model View Controller (MVC) pattern, but it&#8217;s slightly adapted for Unity. This isn&#8217;t the only way to structure your UI, but it has worked well for all my previous Unity games.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Model View Controller Pattern<\/h2>\n\n\n\n<p>In case you&#8217;re unfamiliar with the MVC pattern, I&#8217;ll summarize it. MVC is a pattern used to separate UI, Data, and functionality. The Model holds the data to display, the View manages the layout, and the Controller handles the functionality. This separation (theoretically) makes it easier to change each part independently. For example, you could quickly redesign the layout without touching the data or the functionality. That&#8217;s MVC in a nutshell, so let&#8217;s look at how we can adapt this pattern for Unity UI.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">MVC for Unity<\/h2>\n\n\n\n<p>First off, let&#8217;s talk about the Model. The Model is nothing but a big data container. There are several different ways you could achieve this, and to be honest, they&#8217;re probably all valid. It depends on your use case. However, I prefer to use a Scriptable Object.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Scriptable Objects as a Model<\/h2>\n\n\n\n<p>The advantage of using a Scriptable Object (SO) as your Model comes down to flexibility, testability, and ease of use.<\/p>\n\n\n\n<p>Scriptable Objects can either be created at runtime or in the editor. This feature allows you to test different scenarios with very little code. For example, if you wanted to try several different UI scenarios, you could create a new Scriptable Object to represent each one. Then, it&#8217;s just a matter of switching between them and viewing the result. Additionally, if you need to pull data from a spreadsheet or database, you could accomplish that by creating an SO at runtime and filling it with the retrieved data. In other words, using an SO as your Model won&#8217;t force you into an awkward place down the line. It&#8217;s adaptable to any scenario. <\/p>\n\n\n\n<p>Finally, Scriptable Objects are first-class citizens in the Unity Editor. What I mean by that is that they interact well with the Editor ecosystem, especially the Inspector. As a result, it&#8217;s easy to manipulate your data, which isn&#8217;t the case for databases or raw JSON. By the way, your Model could be as simple as an object with a list of variables:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class DataModel : ScriptableObject\n{\n    public int Coins;\n    public int Gems;\n    public int Tickets;\n    public int Friends;\n    \/\/...etc\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">The View Controller<\/h2>\n\n\n\n<p>As for the view controller, it&#8217;s a <code>MonoBehaviour<\/code> at the root of our prefab. If you haven&#8217;t read my previous two articles on the Main Loop and Structured Prefabs, I recommend you do so. The way I structure View Controllers is a Structured Prefab. The view controller is in charge of binding all your UI pieces to their corresponding variables. By the way, there are essentially two strategies for hooking up UI, polling or event-based. Both approaches are valid so let&#8217;s look at the pros and cons.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Polling-based UI<\/h2>\n\n\n\n<p>If you have a simple UI, using a polling-based method is much simpler. This works by simply setting the UI to the most recent data every frame. For example, if you have a coin counter in your in-game UI, just set the current number of coins in <code>Update<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class GameViewController : MonoBehaviour\n{\n    &#91;SerializeField] CoinCounterPanel _coinCounterPanel;\n    &#91;SerializeField] GameViewModel _model;\n    \n    public void Update()\n    {\n        _coinCounterPanel.SetText(_model.coins);\n    }\n}<\/code><\/pre>\n\n\n\n<p>This type of polling-based UI system is easy to read and understand. Additionally, you don&#8217;t have to worry about managing state, and your UI will always be in sync. There are some disadvantages though. For one, you may be wasting CPU cycles to set data that hasn&#8217;t changed. Also, depending on the UI, the code might become very lengthy. However, given that this code tends to be straightforward, this may not be an issue.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Event-based UI<\/h2>\n\n\n\n<p>In an event-based system, the UI updates whenever an event occurs that changes your data.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class CoinCounterPanel : MonoBehaviour\n{\n    GameViewModel _model;\n    &#91;SerializeField] Text _text;\n\n    public void OnCreated(GameViewModel model)\n    {\n        _model = model;\n        _model.OnCoinsValueChanged += OnCoinCountChanged;\n    }\n\n    void OnDestroy()\n    {\n        _model.OnCoinsValueChanged -= OnCoinCountChanged;\n    }\n\n    void OnCoinCountChanged(int previous, int current)\n    {\n        _text.text = $\"Coins: {current}\";\n    }\n}<\/code><\/pre>\n\n\n\n<p>This system is a little more complicated because you have to bind the data to the UI. In other words, you need to make sure that a specific method executes every time the bound variable changes. In the above example, we need <code>OnCoinCountChanged<\/code> to run every time the <code>coins<\/code> value changes. To do that, you need to keep track of every time the <code>coins<\/code> value changes. In my example, I did this by wrapping <code>coins<\/code> in a Setter that calls an event. So the Model code changes from this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class GameViewModel : ScriptableObject\n{\n    public int Coins;\n}<\/code><\/pre>\n\n\n\n<p>To this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class GameViewModel : ScriptableObject\n{\n    &#91;SerializeField] int _coins;\n\n    public int Coins\n    {\n        get =&gt; _coins;\n        set\n        {\n            int previous = _coins;\n            _coins = value;\n            OnCoinsValueChanged?.Invoke(previous, _coins);\n        }\n    }\n\n    public event Action&lt;int, int&gt; OnCoinsValueChanged;\n}<\/code><\/pre>\n\n\n\n<p>Hopefully, this example demonstrates the added complexity of the event-based approach. Not to mention, using this code, you could still modify the <code>_coins<\/code> value in the Inspector, and it wouldn&#8217;t notify the UI, so it&#8217;s not even a comprehensive solution. This is a lot of code to manage just one variable, so this won&#8217;t scale. Let&#8217;s build a better system.<\/p>\n\n\n\n<p>I created an ObservableVariable type, named after the Observer pattern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;Serializable]\npublic class ObservableVariable&lt;T&gt;\n{\n    &#91;SerializeField] T _value;\n\n    public event Action&lt;T, T&gt; OnValueChanged;\n\n    public T Value\n    {\n        get =&gt; _value;\n        set\n        {\n            T previous = _value;\n            _value = value;\n            OnValueChanged?.Invoke(previous, _value);\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>It&#8217;s a generic version of the same code we used to make our <code>coins<\/code> variable observable earlier. Now we can modify our Model to look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class GameViewModel : ScriptableObject\n{\n    public ObservableVariable&lt;int&gt; _coins = new ObservableVariable&lt;int&gt;();\n}<\/code><\/pre>\n\n\n\n<p>By the way, you may have noticed that I haven&#8217;t shown the <code>GameViewController<\/code> code for our event-based system. That&#8217;s because it no longer does any work beyond initialization. Once the UI is bound to the data, it&#8217;s completely passive. In other words, it just waits for changes to occur rather than actively looking for modifications. So it ends up looking like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class GameViewController : MonoBehaviour\n{\n    &#91;SerializeField] HealthPanel _playerHealthPanel;\n    &#91;SerializeField] CoinCounterPanel _coinCounterPanel;\n\n    public void OnCreated(Health playerHealth, GameViewModel model)\n    {\n        _playerHealthPanel.OnCreated(playerHealth);\n        _coinCounterPanel.OnCreated(model);\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Relationship Inversion<\/h2>\n\n\n\n<p>Right now, <code>GameMain<\/code> initializes <code>GameViewController<\/code> through a direct reference. As a result, if <code>GameViewController<\/code> is missing, then initializing the game will fail. However, depending on the situation, you may find that you want something more flexible. Let&#8217;s modify the <code>GameViewController<\/code> to register itself with <code>GameMain<\/code> rather than the other way around.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class GameViewController : MonoBehaviour\n{\n    &#91;SerializeField] HealthPanel _playerHealthPanel;\n    &#91;SerializeField] CoinCounterPanel _coinCounterPanel;\n\n    public void Start()\n    {\n\tGameMain.NotifyGameViewControllerWasCreated(this);\n    }\n    \n    public void OnCreated(Health playerHealth, GameViewModel model)\n    {\n        _playerHealthPanel.OnCreated(playerHealth);\n        _coinCounterPanel.OnCreated(model);\n    }\n}<\/code><\/pre>\n\n\n\n<p>And here&#8217;s a stripped-down version of <code>GameMain<\/code> to illustrate how this works:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class GameMain : MonoBehaviour\n{\n    public static event Action&lt;GameViewController&gt; OnGameViewControllerCreated;\n\n    void Awake()\n    {\n        OnGameViewControllerCreated += InitializeGameViewController;\n    }\n\n    void OnDestroy()\n    {\n        OnGameViewControllerCreated -= InitializeGameViewController;\n    }\n\n    public static void NotifyGameViewControllerWasCreated(GameViewController gameViewController)\n    {\n        OnGameViewControllerCreated?.Invoke(gameViewController);\n    }\n\n    void InitializeGameViewController(GameViewController gameViewController)\n    {\n        gameViewController.OnCreated(_player.Health, _model);\n    }\n}<\/code><\/pre>\n\n\n\n<p>The idea is that when the <code>GameViewController<\/code> awakes, it&#8217;ll notify <code>GameMain<\/code> who will then initialize it through the <code>OnCreated<\/code> method. I chose to orchestrate this through static events, but you may have a solution that&#8217;s more suited to your game. Now, no matter when <code>GameViewController<\/code> gets added to the scene, it&#8217;ll be initialized correctly. However, it&#8217;s worth mentioning that this flexibility leaves room for mistakes because the game won&#8217;t throw an error if the <code>GameViewController<\/code> is missing.<\/p>\n\n\n\n<p>I brought up the idea of inverting the initialization relationship to demonstrate that when designing software, or anything for that matter, every decision has benefits and drawbacks. There&#8217;s never a universally perfect design decision; there are only decisions that best suit your given constraints. In this case, we gain flexibility but introduce the possibility of human error.<\/p>\n\n\n\n<p>One last thing before I go, you may be wondering what happened to the View part of MVC. Well, given that the View is in charge of layout, the View is your UI Canvas and all the accompanying UI elements. In my case, I usually make this a child of my View Controller prefab.<\/p>\n\n\n\n<p><strong>If you&#8217;re interested, check out the&nbsp;<\/strong><a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/github.com\/bzgeb\/UnityUIDataBindingExample\"><strong>accompanying project<\/strong><\/a><strong>&nbsp;on Github. If you&nbsp;<\/strong><a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/bronsonzgeb.com\/index.php\/join-my-mailing-list\/\"><strong>join my mailing list<\/strong><\/a><strong>, I&#8217;ll notify you whenever I post a new article. If you&#8217;d like to see more ways to architect events, this time using Scriptable Objects, check out&nbsp;<\/strong><a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/www.youtube.com\/watch?v=WLDgtRNK2VE\"><strong>this Devlog<\/strong><\/a><strong>&nbsp;from Unity&#8217;s Open Projects initiative.<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This article describes an approach to structuring UI in Unity. This approach is based on the Model View Controller (MVC) pattern, but it&#8217;s slightly adapted for Unity. This isn&#8217;t the only way to structure your UI, but it has worked well for all my previous Unity games. Model View Controller Pattern In case you&#8217;re unfamiliar [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":359,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,32,1],"tags":[30,29,33,5],"class_list":["post-355","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-architecture","category-ui","category-unity-programming","tag-architecture","tag-scriptable-objects","tag-ui","tag-unity"],"_links":{"self":[{"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/posts\/355","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/comments?post=355"}],"version-history":[{"count":3,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/posts\/355\/revisions"}],"predecessor-version":[{"id":361,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/posts\/355\/revisions\/361"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/media\/359"}],"wp:attachment":[{"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/media?parent=355"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/categories?post=355"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bronsonzgeb.com\/index.php\/wp-json\/wp\/v2\/tags?post=355"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}