Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Losing reference when hitting Play. (Not a "use [SerializeField] to fix")

Discussion in 'Scripting' started by Dimlos, Jun 6, 2019.

  1. Dimlos

    Dimlos

    Joined:
    Aug 13, 2014
    Posts:
    51
    Hello, I'm having a problem with a custom class.
    First off I have to say that everything is working perfectly in Editor and it does not after hitting play.
    I can easily fix that problem by calling a function on Start to re-reference my variable but I want to understand what's happening:

    To explain I removed several variables and functions.

    My goal is to have a list of object of type "Cell" beeing linked by reference to GameObjects in the scene with a script of type "CellObject".
    What I wanted is if I turn off the boolean in the list, it should change in CellObject too.
    Code (CSharp):
    1.     [Serializable]
    2.     public class Cell
    3.     {
    4.         [SerializeField,HideInInspector]
    5.         private bool m_IsAccessible = true;
    6.         [ShowInInspector] //Sirenix Odin Inspector Plugin
    7.         public bool IsAccessible
    8.         {
    9.             get
    10.             {
    11.                 return m_IsAccessible;
    12.             }
    13.             set
    14.             {
    15.                 m_IsAccessible = value;
    16.             }
    17.         }
    18.     }
    Code (CSharp):
    1. public class CellObject : MonoBehaviour
    2. {
    3.     public Cell Cell;
    4.     public void Initialize(Cell cell)
    5.     {
    6.         Cell = cell;
    7.     }
    8. }
    9.  

    Code (CSharp):
    1. [Serializable]
    2. public class MapGrid
    3. {
    4.     public List <Cell> cells = new List<Cell>();
    5. }
    Code (CSharp):
    1. public class ListObject: MonoBehaviour
    2. {
    3.     public MapGrid m_Grid;
    4. }

    When I'm creating a CellObject I'm using Initialize to pass Cell as a reference since it's a class.
    It's working and I can enable/disable the boolean in both CellObject or the List and I can see it changing in one another.
    But when I hit play, it's loosing the reference but keeps all the values stored inside, like Unity created a copy of it.
    I have to re-Initializing on Start every CellObject to "re-link" them.
    I was wondering if there was a way of preventing that?

    Here is a gif showing my problem:
    2019-06-06_18-55-16.gif
     
  2. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Unity treats all objects that derive from UnityEngine.Object as references in the editor (thus the ability to drag and drop Components, MonoBehaviours, GameObjects, and ScriptableObjects into reference fields), while treating everything else essentially as structs (which is why they're created in-place in the inspector, and can't be dragged around). So, as values, not references, but it looks like you're trying to share references to specific Cell objects, which is not really possible in this manner.

    You need to serialize Cell fields in just one place if you want them to be shared, so instead, you could have your ListObject or MapGrid class store a list of CellObject references- these will be maintained when Play is pressed, because CellObject is a MonoBehaviour. Then, you can interact with the Cell information using an accessor in the CellObject component in Start/OnEnable for the ListObject. That puts the responsibility of generating the Cell data to the CellObject- that's the only place where the Cell fields need to be serialized.

    Alternatively, you can only serialize the Cell collection in the ListObject or MapGrid class, and then pass those Cell references to the CellObjects in Start / OnEnable instead. This puts the responsibility of generating the Cell data to the ListObject/MapGrid, so it wouldn't be a serialized field in the CellObject.

    In short, either the Cell field in CellObject, or the List<Cell> cells field in MapGrid, should not be serialized, and should instead accept references that are passed to it during initialization with Start/OnEnable, or through some other mechanism after Play is pressed. Does that make sense?
     
    Last edited: Jun 6, 2019
    Dimlos likes this.
  3. Dimlos

    Dimlos

    Joined:
    Aug 13, 2014
    Posts:
    51
    Thank you for your explanations.
    If I understood correctly, by serializing in multiple places it makes them independant of eachother.
    So it seems that the best solution for what I want to do is to recall Initialize() on Start since I want to be able to edit that boolean in both the object itself and the list inside the editor by clicking, requiring them both to be serialized.
    Now I understand a little bit more what's happening. :)