Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

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

Discussion in 'Scripting' started by ProperNovice, Apr 13, 2021.

  1. ProperNovice

    ProperNovice

    Joined:
    Nov 1, 2016
    Posts:
    15
    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

    Code (CSharp):
    1. public class Test : MonoBehaviour
    2. {
    3.  
    4.     public ScriptObject scriptObject;
    5.  
    6. }
    Code (CSharp):
    1. [CreateAssetMenu]
    2. public class ScriptObject : ScriptableObject {
    3.  
    4.     public int numberInteger;
    5.     public Number number;
    6.  
    7.     public ScriptObject()
    8.     {
    9.         this.number = new Number(this.numberInteger);
    10.     }
    11.  
    12. }
    Code (CSharp):
    1. public class Number
    2. {
    3.  
    4.     public Number(int number)
    5.     {
    6.         Debug.Log(number);
    7.     }
    8.  
    9. }
     
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    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?
     
    angrypenguin and ProperNovice like this.
  3. ProperNovice

    ProperNovice

    Joined:
    Nov 1, 2016
    Posts:
    15
    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
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,762
    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
     
    ProperNovice likes this.
  5. ProperNovice

    ProperNovice

    Joined:
    Nov 1, 2016
    Posts:
    15
    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
     
    angrypenguin and Kurt-Dekker like this.
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,762
    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:

    Code (csharp):
    1. // try NOT to do this in Unity:
    2. 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:

    Code (csharp):
    1. // instead do this:
    2. public int MaxLives;
    3.  
    4. void Reset()
    5. {
    6.  MaxLives = 4;
    7. }
    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.
     
    ProperNovice likes this.
  7. ProperNovice

    ProperNovice

    Joined:
    Nov 1, 2016
    Posts:
    15
    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
     
    Kurt-Dekker likes this.