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.
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.
Statics kind of limit these advances in many cases, the less you have, the better it is (in most cases).
@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?
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; } }