How to reference a scene gameobject from a custom project asset?

Does anybody have any method for being able to have a serialized reference to a game object in a scene in a custom asset in the project?


To explain what I mean - I’ve written a tool for saving, loading & merging lightmap bakes to a custom asset in the project tab (i.e. something that derives from ScriptableObject, was created with ScriptableObject.CreateInstance and saved with AssetDatabase.CreateAsset). The one problem with it is that all the ways I could find to store which GameObject has what lightmap settings fails to keep track of the gameobject in interesting scenes over time.

One method I tried was saving the path to the gameobject in the scene’s hierarchy tab as a string - that works terrible because the paths are not unique, and because when the hierarchy is reordered the paths change, even though the game objects are the same

A second method was GameObject.GetInstanceID and EditorUtility.InstanceIDToObject - this fails because the instance ID’s change every time the scene is reloaded, so EditorUtility.InstanceIDToObject returns null after closing/opening or changing scenes

A third method is actually using a GameObject reference in the custom asset - which seems like it is the right way and seems like it ought to work great (can browse the properties of the custom asset and see the gameobjects are all set in the property inspector), and it does work for a while - unfortunately the moment any scene other the one containing the GameObject is loaded, the saved references are all killed in the asset, so that AssetDatabase.LoadAssetAtPath returns an object with all gameobject references set to null :frowning:


here’s some code bits that may help place what I’m talking about:

// got this class in "SavedLightmapData.cs"
public class SavedLightmapData : ScriptableObject 
{
    [System.Serializable]
    public class LightmapObjectAtlasPair
    {
        [SerializeField]
        public GameObject gameObject;  // this is what goes null
        public AtlasPosition atlasPosition;

        public LightmapObjectAtlasPair(GameObject go, AtlasPosition pos)
        {
            gameObject = go;
            atlasPosition = pos;
        }
    };

    [System.Serializable]
    public class LightmapCopiedData
    {
        public int activeLightmapIndex;
        public Texture2D farMap;
        public Texture2D nearMap;
        public LightmapObjectAtlasPair[] atlasPairs;
    }

    public LightmapHelpers.LightmapCopiedData[] lightmapData;
}

// and the code to save one of these looks like this:
SavedLightmapData savedData = ScriptableObject.CreateInstance<SavedLightmapData>();
savedData.lightmapData = data;
AssetDatabase.CreateAsset(savedData, outputName);

// and the code to load looks like this:
SavedLightmapData savedData = (SavedLightmapData)AssetDatabase.LoadAssetAtPath(inputName, typeof(SavedLightmapData));

Documentation says: “assets can’t store references to objects in a scene.”

I’d go with hidden child, then reference to parent.

string uniqueID = "generateUniqueID";
GameObject child = new GameObject(uniqueID);
child.transform.parent = targetedSceneObject;
child.hideFlags = HideFlags.HideInHierarchy;

and fetch like this GameObject.Find(uniqueID).transform.parent;

" the paths are not unique, " - What I did was make an Editor script to give unique names to all the objects. Not as pretty, but at least you can always find the uniquely-named object.

I don’t think Unity allows references from assets to scenes, only the other way round.

An option to work around this limitation could be storing a “entry point” game object in the scene with an unique name, so that you can easily locate it in every scene using GameObject.Find(). Then add a script that contains references to the game objects in the scene and reference them from your assets using either the array index or a unique identifier. Optionally you can also hide that object using HideFlags so it doesn’t clutter the hierarchy view.