Follow by Email

Friday, 11 April 2014

Single Responsability Principle. Disassembling Singlenton in Unity


If you ask me what is the most important of the SOLID principles that you should follow when developing for Unity, I would say the first. The Single Responsibility Principle (SRP) states that "Every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class", also that "All its services should be narrowly aligned with that responsibility". In other words, don't mix beer and wine.

This can be sometimes complicated in different programming languages, but looks like Unity was created to specifically follow this rule. Because most of the logic of a game object is something you do not have to care about, you can create different behaviours that will do very simple things.

Singleton UML diagram. The simplest design pattern I could think of... xD

The example I will work with is the Singleton Pattern. The singleton is used a lot in Unity to have items that have to survive different game scenes. At the same time, you don't want that game object to duplicate while navigating though the game. The script below ensure that already, you just need to add in the Start or Update your functionality. For example: Game Controlling or Background Music.



public class SingletonExample: MonoBehaviour {

protected static SingletonExample instance;

public static SingletonExample GetInstance(){
     return instance;
}

// Singlenton pattern for in between scenes
void Awake() {
     if (instance != null && instance != this) {
         Destroy(this.gameObject);
     return;
     } else {
         instance = this;
     }
     DontDestroyOnLoad(this.gameObject);
}

void Update(){
    DoThings();
}


The script looks quite nice, but it goes against the SRP. While building scripts in Unity, you should always think the best way of creating assets that you can use later on with flexibility. So what can we do with the script above? The main problem is does two independent things. So we will divide it.

The first script will be in charge of ensuring the uniqueness of the script. It will be called "Unique". We will also add extra functionality to decide what to do when there is a duplicated script. We also replaced the reference of the instance from the script to the game object. We do this because the script is not so important anymore.

public class Unique: MonoBehaviour {

protected static GameObject instance;

public static GameObject GetInstance(){
     return instance;
}

// Delete existing instance?
public bool shouldReplace = false;

void Awake() {
     if (instance != null && instance != this) {
         if (!shouldReplace){
             Destroy(this.gameObject);
             return;
         } else {
             Destroy(instance);
         }
     }
     instance = gameObject;
}


Now we will create another script that will keep the object alive between scene. We will call it "DontDestroyOnLoad". We also added an attribute to know in which scene was the script created.

public class DontDestroyOnLoad: MonoBehaviour {

// The scene when this script was created
public int originalScene;

void Awake() {
     originalScene = Application.loadedLevel;
     DontDestroyOnLoad();
}


Now that we have divided the functionality of our Singleton pattern, we can add this scripts to any game object in many different ways:
- Only Unique: we will have a game object that you cannot instantiate more than once. I used it recently while creating a Unity component for a different person.
- Only DontDestroyOnLoad: we will have a game object that will survive scene loadings until we kill it. Additionally we will be able to know if the script is going to be destroyed in load checking if it has this script. This cannot be done in any other way.
- Both scripts: we will have the previous Singleton pattern with some extra functionality and flexibility.

The only thing left is add the functionality of your Game Controller or Background music in a different script or component. In this way, you will not have to write over and over again the same functions. It will save your time and a lot of code errors.
 
Success! You can now enjoy the sweet taste of Code Reusability :)


In conclusion, it is super easy to create re-usable scripts for Unity. Just think twice the next time you will be copy-pasting your code. And don't be scared of create a lot of different scripts :)

4 comments:

  1. I personally don't like this approach, I simply added a boolean flag on our abstract "MonoSingleton" class which marks if a singleton should live across scenes or not.

    ReplyDelete
    Replies
    1. I agree with you. The good news is that with Unity there are many ways of doing the same thing.

      If the reasons in my post didn't convince you about the virtues of SRP, think that the Singleton is just the simples example I could think of.

      Imagine having a series of character controllers developed with SRP, so you can later conbine them however you want (input, camera, comunication, collisions... everything is separate scripts)

      Delete
  2. Great idea on level of instantiated gameobjects or just dropped on stage but if I want reference game object in code it is not possible to have two game object with assigned sigleton script.

    ReplyDelete
    Replies
    1. You are totally right! :)
      This solution is is incomplete and it doesn't work with more than one item at the same time.

      In order to make it work with more than one, it would require certain modifications:
      - Singleton has another script (Class) that is taking care of it's uniqueness
      - Singleton has a method to retreive an instance of a particular class.
      - Singleton is also a singleton itself (not sure if this is neccesary, though).

      Thanks for reading though my blog! :)

      Delete