Default values for public int / float variables

There is one thing, which has been the bane of my Unity experience lately and this has to do with the behaviour of public int / float values for classes derived from MonoBehaviour.

Lets assume i have a simple (saved) script:

public class Enemy: MonoBehaviour {
public int speed;
}

Let us further assume this Script is attached to alot of objects (e.g. one for each base enemy type i will be spawning in a game).
Most of the enemies will have the same speed, but some are slower / faster so the variable ā€œneedsā€ to be editable in the inspector.

Now i suddenly remember that i wanted to give an default value, so i change the script to

public class Enemy: MonoBehaviour {
public int speed = 2;
}

As result nothing will happen, because all my enemies had already the value 0 filled in for speed in the inspector. This is also true, if i decide the variable doesnt need to be visible in the inspector:

public class Enemy: MonoBehaviour {
[HideInInspector] public int speed = 2;
}

The only way, which found around this is to rename / comment out the variable (at every place where it is used). After that one has to manually fill in the exception values.
This is also a problem, if i want to change the default value later for any reason.

I have spent serious amout of time looking for bugs where all i did was having a variable use ā€œoldā€ values from the inspector.

In short:
Is there a good approach for handling the Situation of variable

  • Are editable in the inspector

  • In a class attched to alot of GameObjects

  • Needs a default value

  • But there are some special class-instance, which need other values

The best solution i can think of is, that unity override values, which were never touched in the inspector, i.e. remebers that the field in the inspector was filled as default value from a script.

The inline initialiser for serialised fields is only used when an asset (component, scriptable object or otherwise) is initially serialised. After that, changing it wonā€™t do anything to fields already serialised.

Itā€™s no bug, itā€™s just how Unity and serialisation works. This is the point of serialising fields: so that you can both view and edit values in the inspector.

It would be a terrible idea for them to constantly change their values should someone in a dev team alter the value of these initialisers.

In short, just edit the values in the inspector.

1 Like

I was about to suggest slapping [System.NonSerialized] on the field, reloading, removing it, and reloading againā€¦but that doesnā€™t work! I guess that, even if you mark a field as non-serialized (or completely remove it), any instances of the component will keep their values.

Well the values will remain serialised until the assets are specifically re-serialised (aside from a few instances where this is bugged, namely when dealing with SerializeReference).

1 Like

Ah, so I just need to find a bigger stick to whack it with :smile:

Fortunately Unity made the stick for you: https://docs.unity3d.com/ScriptReference/AssetDatabase.ForceReserializeAssets.html

1 Like

Serialized properties in Unity are initialized as a cascade of possible values, each subsequent value (if present) overwriting the previous value:

  • what the class constructor makes (either default(T) or else field initializers)

  • what is saved with the prefab

  • what is saved with the prefab override(s)/variant(s)

  • what is saved in the scene and not applied to the prefab

  • what is changed in Awake(), Start(), or even later etc.

Make sure you only initialize things at ONE of the above levels, or if necessary, at levels that you specifically understand in your use case. Otherwise errors will seem very mysterious.

Field initializers versus using Reset() function and Unity serialization:

From the replies i get the feeling i didnt make my question clear enough.

  • I am not asking, what the execution order is. I know at that the inspector comes after the constructor
  • I am talking specifically about situations where editing all values in the inspector is not feasible

Let me try to make the question more precise. Let us assume

  • I have the following script:
public class Enemy: MonoBehaviour {
public int speed, damage;
}
  • Since i didnt specify any values the default value for speed / damage is 0. So 0 is shown / serialised in the inspector

  • This script is attached to infinitly many GameObjects g1,g2,ā€¦,gn (finitely many) and h1,h2,ā€¦ (infinitely many)

  • I have overwritte the value for speed of g1,g2,ā€¦,gn in the inspector to values different from 0

  • I never touched the values for speed of h1, h2, ā€¦ in the inspector

  • I realise, that i need a new default value for speed, say 1

What is the best way to change the (serialised) value for speed for h1, h2, ā€¦ to 1, while

  • Not touching the (serialised) value for speed for g1, g2, ā€¦, gn

  • Not touching any value for damage for either g1, g2, ā€¦, gn or h1, h2, ā€¦

  • Allowing that the values of h1, h2, ā€¦ can be overwritten in the inspector if needed in the future

  • Optional

  • The solution does not depend on the previous default value for speed (0)

From an abstact view point this should be easy, because i never touched the values for speed of h1, h2, ā€¦
So all that needed to happen during serializing is, that values not changed int the inspector are overwritteable by the constructor (since this it where these values came from originally).

No I understood your question, and my response answered it. Once the values are serialisedā€¦ thatā€™s it. Unity at that point only cares about the serialised value, not the ā€˜default valueā€™ in C#.

You either serialise the value or you donā€™t.

If you want to have both a ā€˜defaultā€™ value and a potentially alternate serialised value, then you need to make a wrapper class to handle that.

If you want to edit a large number of these objects for whatever reason, thatā€™s what editor scripts are for.

This is not really an answer to my question, i am not asking what Unity does while serialisation. I am asking how i can solve a specific problem

This might be the start of an answer, but seems a bit vague to be helpful (for me).

To be honest your initial post felt like a lack of understanding as to how Unity works (particularly itā€™s serialisation). More often than not, understanding how something works lets you figure out how to work around it.

Also worth noting that something like int someInt = 2; isnā€™t a ā€˜default valueā€™, itā€™s a field initialiser. Which is just syntax sugar for how the value is initialised, which only ever happens once.

As always the devil is in the details.

Donā€™t see how itā€™s vague. A wrapper class is well understood coding concept you can look up. So is the concept of writing editor scripts for Unity.

Doesnā€™t have to be anything special either:

using UnityEditor;
using UnityEngine;

public static class TempEditorMethods
{
    [MenuItem("Editing/Update All X GameObject")]
    private static void UpdateAllXGameObjects()
    {
        var components = UnityEngine.Object.FindAllObjectsOfType<SomeType>();
       
        foreach (var component in components)
        {
            if (component.SomeValue == 0) //lets pretent zero was our old default value
            {
                component.SomeValue = 2; //and lets make 2 our new 'default' value
            }
        }
    }
}
1 Like

While i think i got that concept, i agree that using the word default-value was maybe not optimal.

Much clearer answer for someone who has never seen an editor script before, thank you. This should solve the problem except the little flaw, that it indeed depends on the previous value from the initialiser.

You can right-click the script to reset it instead of recompiling.

Also, you would often have this script in a prefab so the default value can easily be changed.

Actual games would probably load the value from some constant in a config such as a scriptableobject, instead of relying on the inspector.

I agree with Stardogā€¦ these types of values should be in a config file. There can be as many different speeds as you like, they can be conditional, etc. Set them at runtime.

Also worth noting, if all these game objects were a prefab, then you could easily change the value of the prefab and any instances in scenes that havenā€™t said value overridden would update to the new value.