Search Unity

object reference breaks becomes copy after i close and reopen scene

Discussion in 'Scripting' started by elseforty, Feb 1, 2020.

  1. elseforty

    elseforty

    Joined:
    Apr 3, 2018
    Posts:
    290
    Hi,
    i have a list of objects like below, when i change [b1] all the [b1] of the other list objects changes as well

    List
    [a1]
    =>[b1]
    [a2]
    =>[b1]
    [a3]
    =>[b1]
    .
    .
    .

    when i close and reopen the scene the list becomes like below, the [b1] changes are not reflected anymore, it seams like the objects of type b are now copies instead of object references pointing toward one object of type b

    List
    [a1]
    =>[b1]
    [a2]
    =>[b2]
    [a3]
    =>[b3]
    .
    .
    .

    can you guys help me understand what is going on
     
    Last edited: Feb 1, 2020
  2. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    Do you change the scene, or quit the application and reopen it? Either way, how are you saving the lists? Do they persist, or do you recreate them, or do you load them in some way?

    Taking what you said at face value, if you close a scene and open it again, everything in that scene should be loaded as if it was the first time. So unless you manually do something to these lists, they should be the save every time you start the scene. So what exactly do you mean when you say you are closing and reopening a scene?
     
  3. elseforty

    elseforty

    Joined:
    Apr 3, 2018
    Posts:
    290
    Hi Yoreki,

    justing jumping between scenes will replicate the issue,
    concerning the way i'm using to save the list, i'm setting the object class holding the list to dirty like this EditorUtility.SetDirty(object class holding the list); since this is is an editor plugin
    the list object persists and i'm not using any special way to load it
     
  4. elseforty

    elseforty

    Joined:
    Apr 3, 2018
    Posts:
    290
    i made i script to replicate the issue , can someone please help with this

    when i edit the last data element "list" by changing the size, the change is reflected on the other data objects list


    if i open a new scene and go back to this one, the changes are not reflected anymore as previously, it behaves like copies instead of references pointing toward one object in memory.
    any thoughts about this ?


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEditor;
    4. using UnityEngine;
    5.  
    6. public class test : MonoBehaviour
    7. {
    8.  
    9.     public List<data> Data = new List<data>();
    10. }
    11. [System.Serializable]
    12. public class data
    13. {
    14.  
    15.     public List<int> list = new List<int>();
    16. }
    17. [CustomEditor(typeof(test))]
    18. public class testEditor:Editor
    19. {
    20.     test test;
    21.     public void OnEnable()
    22.     {
    23.         test = (test)target;
    24.     }
    25.     public override void OnInspectorGUI()
    26.     {
    27.         DrawDefaultInspector();
    28.  
    29.         if (GUILayout.Button("Add"))
    30.         {
    31.             data data = new data();
    32.             if (test.Data.Count >= 1) data.list = test.Data[test.Data.Count - 1].list;
    33.             test.Data.Add(data);
    34.  
    35.             EditorUtility.SetDirty(test);
    36.         }
    37.         if (GUILayout.Button("Clear"))
    38.         {
    39.             test.Data.Clear();
    40.  
    41.             EditorUtility.SetDirty(test);
    42.         }
    43.     }
    44. }
     
    Last edited: Feb 2, 2020
  5. elseforty

    elseforty

    Joined:
    Apr 3, 2018
    Posts:
    290
    can someone please take a look at this, i can't figure out why unity is behaving like this, it is really weird
     
  6. Adrien-TA

    Adrien-TA

    Joined:
    Jan 11, 2019
    Posts:
    7
    Hi o/

    On line 32 :
    if (test.Data.Count >= 1) data.list = test.Data[test.Data.Count - 1].list;

    You are explicitly copying your list from one element to the other.
    Since C# lists are copied by reference, your list is now shared between the two elements.

    You should either explicitly instantiate a new list, or use a copy instruction, something like :
    List<YourType> newList = new List<YourType>(oldList);
     
    Joe-Censored and SonicBloomEric like this.
  7. elseforty

    elseforty

    Joined:
    Apr 3, 2018
    Posts:
    290
    @Adrien-TA
    thanx but are you sure that i'm copying the array
    a little test i made to verify this
    Code (CSharp):
    1.     public List<int> a = new List<int>();
    2.     public List<int> b= new List<int>();
    3.     void Start()
    4.     {
    5.         // b will be just a ref for a , if i change a, b will change as well, if i change b, a will change as well
    6.        // b = a;
    7.        // a.Add(2);
    8.  
    9.         // b will be a copy of a , if i change a, b will not change, if i change b, a will not change
    10.         b = new List<int>(a);
    11.         b.Add(2);
    12.     }
     
  8. Adrien-TA

    Adrien-TA

    Joined:
    Jan 11, 2019
    Posts:
    7
    Well, the line you wrote in your original code :
    data.list = test.Data[test.Data.Count - 1].list;

    is exactly the same case as your first test :
    b = a;

    So yes, you are copying your list by reference in those cases, instead of copying the content into a new list.
    Maybe my wording was not very clear about that.
    You need the "b = new List<int>(a);" approach to ensure the two lists are independents.
     
  9. elseforty

    elseforty

    Joined:
    Apr 3, 2018
    Posts:
    290
    Hi Adrien,

    That was may intention, i want to copy the reference of a list and not copy the whole list content and when i reload the scene i want those reference to still be functional, but it is not the case cause i found that unity does not serialize references of custom classes that does not derive from UnityEngine.Object
    i guess i have to write something that will reconstruct references when deserialization myself
    here is the part from the docs that says this

    "With custom classes that are not derived from UnityEngine.Object Unity serializes them inline by value, similar to the way it serializes structs. If you store a reference to an instance of a custom class in several different fields, they become separate objects when serialized. Then, when Unity deserializes the fields, they contain different distinct objects with identical data.
    "
     
  10. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    No, you do not copy the array, you only pass references around.

    And that's what Unity doesn't deal with by default.
    When you pass references of serializable types and assign them to serialized fields, Unity treats those as you'd expect as long as the serialization / deserialization does not need to persist / recover the serialized objects.

    When serialization kicks in, Unity serializes the objects that are being referenced by serialized fields as if they were value types. That is, it serializes the data for each field separately, no matter if an object is assigned to multiple fields.
    As a consequence, the objects will be recovered as separate instances during deserialization, no matter if one object happened to be referenced multiple times.

    That case is commonly worked around by using ScriptableObjects. For SOs and all other types derived from UnityEngine.Objects, other mechanism apply and references etc will be recovered correctly.

    Recent versions also introduced a new attribute: SerializeReference. In some cases this can be used to actually let Unity share objects and recover references to objects, rather than creating one instance for each field that used to reference a shared object. It's got some limitations as well though, and it might simply be better to workaround the issue usnig an SO.
     
    Adrien-TA, elseforty and Joe-Censored like this.