Why my code isn't working as I'd like to?

Hello all,

I have a question about scriptable objects variables. I have demonstrated my program to a simpler one that pin points exactly my problem.

I have created a Scriptable Object of the ScriptObject class and set numberInteger to 10 in the editor. I have attached the Scriptable Object to the Test field. When I run it, the int number that is being passed to Number constructor is 0, as the default in the definition of numberInteger.

What change shall I do to pass the reference as I have it in the editor?

Thank you,

John

public class Test : MonoBehaviour
{

    public ScriptObject scriptObject;

}
[CreateAssetMenu]
public class ScriptObject : ScriptableObject {

    public int numberInteger;
    public Number number;

    public ScriptObject()
    {
        this.number = new Number(this.numberInteger);
    }

}
public class Number
{

    public Number(int number)
    {
        Debug.Log(number);
    }

}

The values you set in the editor are applied after the object’s constructor is called.

You usually should not do things inside the constructor of a Unity object. Perhaps you could do this in the Awake function of your MonoBehaviour instead?

2 Likes

Yes Antistone, you are absolutely right. I tested it using a Log and it works exactly as you said. Seems like these are small traps I step into coming from Java.

I reworked it using the MonoBehaviour as suggested.

Thank you

Yeah, Unity has a lot going on shall we say when it stands up (loads, activates, whatever) a scene or prefab.

All the GameObjects are loaded and the Components (everything else) is slotted into them, which fires the constructors.

After the constructor, the loader looks for any data to jam into those fields. This is broadly called the deserialization step, when it takes what you might have set in the editor (saved into the scene, asset, or prefab) and populates public fields in the obect with that data.

Finally it calls Awake() on each added component.

If you put code in Awake() it should not interoperate with anything but yourself.

If you need to “get at” other components, do so in Start().

Here’s some more brain-expanding insights, actually one of my favorite Unity pictures:

https://docs.unity3d.com/Manual/ExecutionOrder.html

1 Like

Kurt, your post and the link you provided are very informative and helped me immensely understand the process of the lifespan of the script.

Thank you

2 Likes

Glad to hear it, and of course you’re welcome.

As always with something complicated, there’s lots of ways for it to go wrong.

For instance, if you make a public GameObject MyThingy; and save a reference into it, then later decide “Hmm, I’m going to make that a Transform to save myself one extra .transform dereference,” and all you do is make your variable public Transform MyThingy;, your code won’t work because the deserializer still has saved a GameObject reference, and it cannot jam that into a Transform variable.

You must re-drag the GameObject in, even if it’s the same exact one as before. That will create a Transform reference, which is different than a GameObject reference.

Another confusion comes from field initializers:

// try NOT to do this in Unity:
public int MaxLives = 4;

The reason is, once serialized data is stored, it does not matter what you change that “4” to, it will get wiped out by the saved data.

Instead, to make predefined values for MonoBehaviors, implement a void Reset() method:

// instead do this:
public int MaxLives;

void Reset()
{
 MaxLives = 4;
}

That ONLY works when using the editor. When you first drag the script on, it will inject 4 into that field. That way you know that function won’t run unless you either re-drag it in afresh, or right-click the hamburger on the inspector and select Reset.

1 Like

Ok Kurt, I think that providing these two paradigms you made me pass over two big traps that I’d probably lose much time to figure them out. All this is starting to make sense.

Can’t thank you more

1 Like