Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Design Patterns When Scripting

Discussion in 'Scripting' started by Shadowsurge, Jan 25, 2016.

  1. Shadowsurge

    Shadowsurge

    Joined:
    Nov 26, 2015
    Posts:
    34
    This is a re-post from the "Teaching" section. I figured it might be better here.

    I have a project for University, in which we've to use MVC and other design patterns. Up until now, I've been using Unity just to program on my own, so I haven't followed any patterns whatsoever. Not to mention that Unity already has it's own system where everything is Entity-Component.

    I'm a bit lost, I can program in C#, which is what I use for Unity, but I have no idea how to set things out into an MVC pattern, could anyone link some useful tutorials, webpages or even take the time to explain how I'd do MVC in Unity?

    I created an Arkanoid clone from a course I've been taking, could someone describe how I'd use the Model-View-Controller layout on a simple game such as that?
     
  2. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    The MVC pattern (model view controller) is described here http://www.tutorialspoint.com/design_pattern/mvc_pattern.htm. There's a good tutorial on how to make one here http://www.toptal.com/unity-unity3d/unity-with-mvc-how-to-level-up-your-game-development.

    Also there's a list of frameworks on the asset store. Mainly they come in the form of a IoC container, however many of those follow the MVC pattern:

    https://www.assetstore.unity3d.com/en/#!/content/9267
    https://www.assetstore.unity3d.com/en/#!/content/17758

    I wouldn't get too lost in the frameworks though. They contain A LOT more than just an IoC Container. Go for the tutorial.
     
  3. Shadowsurge

    Shadowsurge

    Joined:
    Nov 26, 2015
    Posts:
    34
    I'm reading the tutorials now, thanks. As helpful as a framework would be, I'd rather do it all from scratch as much as possible, I'll most likely learn more that way but I'll keep those in mind as a last resort.

    The second tutorial link you provided is clearing things up partially. Still a bit confused as I'm unsure what script should contain which code in this design pattern. What sort of code should the model have, or the controller? The view, just holds all code related to updating the UI for the user, which is pretty simple really, sprite changes etc.

    The reason I'm confused is I hear a saying frequently "fat models, skinny controllers" but in this second tutorial, the guy teaching has fairly 'skinny models' and 'fat controllers'.
     
  4. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Well first I need to point out that I'm not the master of the MVC, however I'll share what I know. Basically if you think of an entity, as a GameObject with a feature that works fully on it's own. So lets say a character that has health, can shoot, and can die. The MVC would break down the entity into several parts:

    It's all about segmenting communication and information.

    The model stores all the data of an entity (for example a player). So it'll hold their name, health, ammo, ect. This is just the core data of the object. So it's not the UI, or how to handle that data, or it's effects. It's just the data. The model has no knowledge of the 'view' component. It will only have knowledge of the controller, and that's only to feed any updates about any progress (if saving/loading data), if need be.

    The view has two roles. 1) it's holds all the physical representations of the entity. So in the instance of a player, it'd show in the UI the player's health, ammo, ect. Any physical representation of data needed for the user to see. Like the 3D models, and collisions. It can read it's own data and display it, from the model, but it may read only. It may not change anything on the model itself. So it's able to read the data from the model and show it in the game. 2) The view is a entry point for the outside world to affect the entity. It doesn't handle the logic of HOW the object will be affected. It only passes the events to the controller. So for example the view could have an 'OnCollision' event, or a 'TappedInteractiveObject' event. These would be then passed to the controller to be handled.

    Controllers don't store any player data, like the model, no do they show any data in the scene, like the view. They purely handle the entities logic. So an event would be passed from the view to the controller. So for example, the player's entered the state of 'falling', or there's been a collision with a projectile. The controller would then process the event. So on being hit by a bullet, the view would pass the controller the event, the controller would then handle the event, the controller would update the models core data from the effects of the event, and then the view would show the updated core data from the model, after the event has hapenned.

    So it would work a little like this:

    model:
    Code (CSharp):
    1. public int health = 10;
    view
    Code (CSharp):
    1. public void OnCollisionEnter(Collider collision)
    2. {
    3.       controller.Collided();
    4. }
    controller
    Code (CSharp):
    1. public void Collided(Collider _collision)
    2. {
    3.      if(_collision.collider.tag == "Bullet")
    4.        {
    5.               model.health -= 1
    6.        }
    7.  
    8. }
    view again
    Code (CSharp):
    1. private void Update()
    2. {
    3.      healthText.text = model.health.ToString();
    4. }
    Really though this is just my interpretation of it. The only way to truly understand is to google the hell out of it. Read all the blogs, cross reference, and and when you're ready, practice to make it yourself and learn.
     
  5. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
  6. Shadowsurge

    Shadowsurge

    Joined:
    Nov 26, 2015
    Posts:
    34
    Ok, that was actually a fantastic explanation. It made things a lot clearer for me. One final thing, I think it's the final thing anyway ( I do appreciate you taking the time to go over this with me) does every new object require a model, view and controller script unique to itself? I'm assuming it can be done in one huge Model script or one huge View script etc but it's probably better practice to give each object in the game it's own MVC scripts right?

    Anyway, I get it, mostly, so thank you. Before, I was doing things like this

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Brick : MonoBehaviour
    6. {
    7.  
    8.     private LevelManager levelManager;
    9.     private int timesHit;
    10.     bool isBreakable;
    11.  
    12.     public GameObject smoke;
    13.  
    14.     public static int breakableCount = 0;
    15.     public Sprite[] hitSprites;
    16.  
    17.     // Use this for initialization
    18.     void Start ()
    19.     {
    20.         isBreakable = (this.tag == "Breakable");
    21.      
    22.         // tracks and sets the amount of bricks at the start of each level
    23.         // if it is breakable, it is added to the total count for that level
    24.         if (isBreakable)
    25.         {
    26.             breakableCount ++;
    27.         }
    28.      
    29.         levelManager = GameObject.FindObjectOfType<LevelManager>();
    30.         timesHit = 0;
    31.     }
    32.  
    33.     // Update is called once per frame
    34.     void Update ()
    35.     {
    36.      
    37.     }
    38.  
    39.     void OnCollisionEnter2D (Collision2D collision)
    40.     {
    41.         if(isBreakable)
    42.         {
    43.             HandleHits ();
    44.         }
    45.     }
    46.  
    47.     void HandleHits()
    48.     {
    49.         timesHit ++;
    50.      
    51.         int maxHits = hitSprites.Length + 1;
    52.      
    53.         if( timesHit >= maxHits)
    54.         {
    55.             breakableCount --;
    56.             Debug.Log(breakableCount);
    57.          
    58.             // tells level manager that a brick was destroyed
    59.             // level manager then checks if its the last brick, if it is, it loads
    60.             //next level, otherwise play continues
    61.             levelManager.BrickDestroyed();
    62.          
    63.             // instantiate does not return a game object, so you must specify.
    64.             GameObject smokePuff = Instantiate (smoke, transform.position, Quaternion.identity) as GameObject;
    65.          
    66.             // sets the smoke puffs particle system start color
    67.             // to be the same as the current game objects sprite renderer color
    68.             // current game object is the object attached to this script, so the brick.
    69.             smokePuff.particleSystem.startColor = gameObject.GetComponent<SpriteRenderer>().color;
    70.          
    71.             // destroys the brick
    72.             Destroy(gameObject);
    73.         }
    74.         else
    75.         {
    76.             LoadSprites();
    77.         }
    78.     }
    79.  
    80.     void LoadSprites()
    81.     {
    82.         int spriteIndex = timesHit - 1;
    83.      
    84.         if (hitSprites[spriteIndex])
    85.         {
    86.             // gets the component of this, as in this brick object.
    87.             // this, refers to the current object attached to the script
    88.             this.GetComponent<SpriteRenderer>().sprite = hitSprites[spriteIndex];
    89.         }
    90.         else
    91.         {
    92.             Debug.LogError("Sprite Missing");
    93.         }
    94.     }
    95.  
    96.     //TODO remove this method once we can actually win
    97.     void SimulateWin()
    98.     {  
    99.         levelManager.LoadNextLevel();
    100.     }
    101. }
    102.  
    But like I said in my OP, I've never used a design pattern so for things like this ( this is a script for a brick being hit, which is from a course I'm doing) I have simply been putting the class variables, the view updates and the collisions etc in one file.

    I'm assuming to correct this into MVC it'd be

    Model - Taking the bricks core data, such as timesHit, maxHP etc etc. This should contain only the data needed to instantiate/create the object at runtime, correct?

    View - This would handle all sprite updates and the data to controller view "OnCollisonEnter2D" but would this also handle the function called void handleHits or would the controller handle that and feed the data back to the model?
     
    Last edited: Jan 25, 2016
  7. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
  8. Shadowsurge

    Shadowsurge

    Joined:
    Nov 26, 2015
    Posts:
    34
  9. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Model: Yes. It'd have timesHit, and maxHP. When you say create/instantiate the object at runtime. The model wouldn't actually perform this operation. You'd probably want to create a prefab, which contains all 3, and instantiate that. Or create some factory pattern to create them. Looking at the example in this link here: http://www.tutorialspoint.com/design_pattern/mvc_pattern.htm, an external class creates the model, and the view, and then instantiates the controller, passing in the references of the model and the view. For an example in Unity, you wouldn't need to do this. I'd probably create a prefab, which includes a model (a class holding all the data), a view (a class passing all model data to the 3d model, and any UI), and a controller (another class, which handles logic).

    As for each object having an MVC, or having a massive one. I'm not totally sure at this point, as I havn't done very much with them. If it was me I'd probably try whichever I prefer and read more into it as I go.
     
  10. Shadowsurge

    Shadowsurge

    Joined:
    Nov 26, 2015
    Posts:
    34
    Yeah creating prefabs in Unity is something I already do so I'd probably go for that. Something just clicked, which should have been obvious. I assumed that each part of the MVC would require it's own gameObject to be attached to.

    When in reality, if I had a brick, like in my script example earlier. I'd simply attach 3 scripts to it. One for the Model, one for the Controller and one for the View, then pass data around as required, right? Anyway you've been a lot of help, seriously, thanks a lot.
     
    Nigey likes this.
  11. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    You got it :) and no problem