2
\$\begingroup\$

I have a list of object references inside a serializable class, like this:

[Serializable]
public class Test : MonoBehavior{
    public List<SomeSerializableObject> list;
}

When I instatiate a copy (with Instatiate()), the objects inside the list get copied and are no longer references to the original elements in the list. Using [SerializeReference] only works for the list itself, but how do I do it for the items inside the list?

\$\endgroup\$
6
  • \$\begingroup\$ Can you help us understand what this list of references is pointing to? Are these items serialized anywhere else, or do some entries in the list point to the same item? \$\endgroup\$
    – DMGregory
    Dec 28, 2021 at 16:53
  • \$\begingroup\$ From my understanding the items in the list have to be proper Unity assets (with GUIDs) to be able to support serializable references. Otherwise a copy-by-value is the only thing that is possible via serialization. \$\endgroup\$
    – Charly
    Dec 28, 2021 at 16:56
  • \$\begingroup\$ @DMGregory Every item is unique. The objects are custom serializable classes, so non-unity objects. \$\endgroup\$
    – RenX
    Dec 28, 2021 at 22:10
  • \$\begingroup\$ @Charly So they need to inherit from ScriptableObject or MonoBehavior? \$\endgroup\$
    – RenX
    Dec 28, 2021 at 22:12
  • \$\begingroup\$ @DMGregory Each item is unique but not only referenced by this list, this is my problem. Sorry for articulating myself so poorly. \$\endgroup\$
    – RenX
    Dec 29, 2021 at 7:31

1 Answer 1

2
\$\begingroup\$

The Problem

In Unity's Docs :

  • [Serialize] Referenced values cannot be shared between UnityEngine.Object instances. For example, two MonoBehaviours cannot share an object that is serialized by reference. Use ScriptableObjects instead to share data.

As Instantiate creates a new Unity Object, its data cannot be shared by reference by just decorating the fields with a [SerializeReference] Attributes.

The Solution

Instead, each member of the list that you want to be copied by reference during instantiation must be it's own Unity Asset. This means each member has to be inherit from UnityEngine.Object and be an actual Asset on disk with a .meta file with a GUID. It's these GUIDs which are the links which Unity's serialization system uses to stitch all the references back together at edit or runtime.

You can create an Unity asset via script by going AssetDatabase.CreateAsset([...]) with anything which inherits from UnityEngine.Object. But instead what you probably want to do for convenient sake is inherit from ScriptableObject and then create your assets via the asset menu (which you can open by right clicking in the project window). This way you can create new assets in the editor and don't have to create custom inspectors to edit their values.

Here are the two scripts which get the job done:

[CreateAssetMenu(menuName = "Custom/test so", order = 0)]
public class MySO : ScriptableObject
{
    public int Value;
}

And:

public class Test : MonoBehaviour
{
    public List<MySO> scriptableObjectAssets;
}

Opening the asset menue to create the scriptableObject:

Opening the asset menu to create the SO

The inspector for the the Monobehavior with the members which will be copied by reference during instantiations:

The inspector for the Monobehavior with list members which will be copied by reference during Instantiates

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for the detailed answer! I made my list items inherit from ScriptableObject and use Object.CreateInstance to instantiate them. \$\endgroup\$
    – RenX
    Dec 30, 2021 at 2:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .