Use to Singleton

hello everyone. I have 3 different scripts. Let's call them the 1st, 2nd and 3rd. I need to access the data in the 3rd script from the 1st script through the 2nd script and the 2nd script should be a singleton. how can I do it. I would be very happy if you can help.

Hello, mmm... If I don't misunderstood anything... Something like that could do the trick.

public class FirstScript
{
    private readonly ThirdScript _thirdScript;

    public FirstScript(ThirdScript thirdScript)
    {
        _thirdScript = thirdScript;
    }

    private void MethodUsingThirdScript()
    {
        Debug.Log(_thirdScript.SomeIntProperty);
    }
}

public class SecondScript
{
    public static SecondScript Instance;
    private readonly ThirdScript _thirdScript;
    public SecondScript(ThirdScript thirdScriptReference)
    {
        _thirdScript = thirdScriptReference;
        Instance = this;
    }

    private void MethodUsingThirdScript()
    {
        Debug.Log(_thirdScript.SomeIntProperty);
    }
}

public class ThirdScript
{
    public int SomeIntProperty;
}
1 Like

I like this pattern: Simple Singleton (UnitySingleton):

Some super-simple Singleton examples to take and modify:

Simple Unity3D Singleton (no predefined data):

https://gist.github.com/kurtdekker/775bb97614047072f7004d6fb9ccce30

Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

https://gist.github.com/kurtdekker/2f07be6f6a844cf82110fc42a774a625

These are pure-code solutions, do not put anything into any scene, just access it via .Instance!

If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

public void DestroyThyself()
{
   Destroy(gameObject);
   Instance = null;    // because destroy doesn't happen until end of frame
}

There are also lots of Youtube tutorials on the concepts involved in making a suitable GameManager, which obviously depends a lot on what your game might need.

1 Like

It matters whether your scripts are MonoBehaviour or not. @Franco_Voisard 's script is pure C# and if you want a MonoBehaviour singleton, check out Kurt's answer. But basically, when you want this kind of behavior with MonoBehaviours you must be mindful of the object's initialization states, because their execution order is now in Unity's hands.

For this check out the Script Execution Order in settings. For example your singletons are probably "the eldest" of the objects, in terms of importance, because it's likely the other objects will refer to them for some services. They should have a priority (i.e. their Script Execution Order should be lower than default).

This is just a heads up, if you ever stumble upon this problem.

2 Likes

When coding in Unity, a common mistake is to make every script as a MonoBehaviour. Often is better (To take advantage from constructors, dependency injection or avoid depending on the Unity lifecycle) using the “Humble Object” pattern, basically you encapsulate a common C# class inside a MonoBehaviour (This way you can take advantage of both things, Unity lifecycle and plain C# advantages)

Humble Object is an interesting pattern, I agree - but it's pretty advanced stuff, and pretty out of the norm, so I would not personally recommend it for somebody who's still getting to grips with the basics (also dependency injection, and even assigning to class members in the constructor are possible to pull off with MonoBehaviours as well... but that's getting off-topic :smile:).

Singleton is a simple, functional pattern to use starting off, but at least at some point I would recommend also looking into the service locator pattern. It has some benefits compared to the singleton pattern, such as being able to retrieve instances by an interface they implement.

using System;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;

public static class Get
{
    public static IPlayer Player => GetOrCreate<Player>();
    // TODO: Add more service getters here...

    private static T GetOrCreate<T>() where T : class
    {
        var instance = Cached<T>.instance;
        if(instance is null)
        {
            if(typeof(Component).IsAssignableFrom(typeof(T)))
            {
                instance = (T)(object)Object.FindObjectsOfType(typeof(T)).Single();
            }
            else
            {
                instance = Activator.CreateInstance<T>();
            }

            Cached<T>.instance = instance;
        }

        return instance;
    }

    private static class Cached<T> where T : class
    {
        public static T instance;
    }
}

Usage:

public class First : MonoBehaviour
{
    public void Example()
    {
        Second second = Get.Second;
        Third third = second.Third;
    }
}
1 Like

And while we're at it, let's not forget the time-honored process of just putting a bunch of shared data into a ScriptableObject instance and dragging it into everything that needs it.

https://docs.unity3d.com/Manual/class-ScriptableObject.html

Doesn't even need to have any predefined data... you can just make it all runtime data, mark everything as [NonSerialized] and get on with your game.

3 Likes

It is possible to copy data from the first script to the second one.