How to not allow other script to modify the value of static variables

Hello! I would like to have a couple of asset references to be available statically, but I also noticed that you can modify the static variable values at run-time from other scripts, which I don’t want. I cannot use const because otherwise I’d have to hard code their path and I’m pretty sure that readonly isn’t helpful in this case. How do I solve this issue?

using UnityEngine;

public class AssetReferences : MonoBehaviour {

    [SerializeField]
    private string asteroidDataPath;
    static public AsteroidData asteroidData;

    [SerializeField]
    private string blackHoleDataPath;
    static public BlackHoleData blackHoleData;

    [SerializeField]
    private string difficultyConfigDataPath;
    static public DifficultyConfigurationData difficultyConfigData;

    [SerializeField]
    private string gameConfigDataPath;
    public static GameConfigurationData gameConfigData;

    void Awake()
    {
        blackHoleData = Resources.Load(blackHoleDataPath) as BlackHoleData;
        gameConfigData = Resources.Load(gameConfigDataPath) as GameConfigurationData;
        asteroidData = Resources.Load(asteroidDataPath) as AsteroidData;
        difficultyConfigData = Resources.Load(difficultyConfigDataPath) as DifficultyConfigurationData;
    }

}

That’s kind of the deal with static, they are public for every script to read and write.
This is the only why I can think of. Create getter and setters. the set is private to stop other scripts from changing it.

    [SerializeField]
    private string blackHoleDataPath;
    private BlackHoleData _blackHoleData;
   
    static public BlackHoleData blackHoleData {
    get {
        return _blackHoleData;
    }
    private set {
        _blackHoleData = value;
    }
}

This is more of a general problem, not only with statics. Any field that is public (and neither const nor readonly) can be assigned from wherever you want.

@johne5 has pretty much nailed it.

However, if you don’t mind, I’d suggest to try reducing static fields as much as possible and try to come up with a way that you can model this on an instance level.
The first (yet not the best approach) would be some kind of singleton.
Another approach involves scriptable objects, as they’re often used as data containers.
This offers more flexibility as well, as you could create different instances for play-testing and various setups that you may want to try in the future. :slight_smile:
Statics kind of limit these advances in many cases, the less you have, the better it is (in most cases).

@johne5

@johne5 This sounds awesome. I tend to avoid C# properties because my C# coding skills aren’t great. But anyway… then I set BlackHoleData in Awake()?

@Suddoha Actually this class didn’t contain all those static variables before. In fact, I used to use instances instead, which were public, and I got them using GetComponent. Not the best approach IMHO, but I would like this class to be anyway a container for all my asset references, globally. I did this to avoid drag-n-dropping the asset reference to each GameObject, every time. This is why in the end I decided to use static fields. What do think?

blackHoleData should work in Awake(). It’s local to the script so i can use the private getter.

Couple things. Static properties have to be backed by static fields. So _blackHoleData would also have to be static in this case. Also, properties are generally PascalCase not camelCase so it should be BlackHoleData (which of course wouldn’t work because that’s also the name of your type - so you’d have to come up with something else - I’d probably not put Data in the type name). Lastly, you can get rid of the explicit private backing field entirely if you want to.

public static BlackHoleData BlackHole { get; private set; }

@johne5 Thanks again for the code snippet. By the way, there was a very small error on your code, but I fixed it. This is the final version for those interested:

    [SerializeField]
    private string blackHoleDataPath;
    private static BlackHoleData _blackHoleData;

void Awake()
    {
        _blackHoleData = Resources.Load(blackHoleDataPath) as BlackHoleData;
        ...
    }

static public BlackHoleData blackHoleData
    {
        get
        {
            return _blackHoleData;
        }
        private set
        {
            _blackHoleData = value;
        }
    }

@KelsoMRK Thanks again for the clarification! One little question: that private set; means that I can set the static field only inside the script and not externally, right?

Correct. All your access modifiers work there. So if you had a non-static property that you wanted to expose to derivative classes you could do this

public string Foo { get; protected set; }

You can also omit the set entirely if your property is derived from other values

private int damage;
private int bonusDamage;
public int TotalDamage { get { return damage + bonusDamage; } }

Also, if you’re feeling saucy (and targeting the 4.6 runtime) you can use expression bodied properties for read-only stuff.

private int damage;
private int bonusDamage;

// this
public int TotalDamage => damage + bonusDamage;
// is the same as this
public int TotalDamage { get { return damage + bonusDamage; } }

@KelsoMRK Thanks for the awesome code!

Do you mind to adapt my source code to implement your last snippet? Somehow I cannot make it work… So I can understand properties once and for all :smile:

Why go through all of this work just to reference assets for Resources.Load() when you could just link to the prefabs instead?

All I want to share are asset references across the whole project to calculate stuff around. My prefab references belong to the factories.

Given what I think you’re trying to do I’d probably go with something like this

private BlackHoleData data;

public BlackHoleData BlackHole
{
    get
    {
        if (data == null)
        {
            data = Resources.Load(path);
        }
        return data;
    }
}

Thanks you all for the help :slight_smile: