How to prevent from duplicating procedurally instantiated GameObject in Editor?

Hi guys,

Several times, I need to procedurally create a GameObject and to put it as a child of my MonoBehaviour GameObject (in both Editor and Playtime). Something like this:

[ExecuteInEditMode]
public class MasterObject: MonoBehaviour
{
    GameObject m_Slave; // this field is NOT serialized because procedurally instantiated

    void Start()
    {
        m_Slave = new GameObject("Procedural Slave");
        m_Slave.transform.SetParent(transform, false);
        m_Slave.hideFlags = HideFlags.NotEditable | HideFlags.DontSave; // the user cannot manually modify the procedural object
    }
}

3303340--256289--Screenshot_17.png

This works very well, except when I duplicate my MasterObject in the Editor. When I do so, the Slave object is also duplicated, and another one is created from Start:
3303340--256293--Screenshot_18.png

I tried to change the hideFlags of my slave object, but couldn’t find an option which prevent it from being duplicated with his parent. This slave object is also duplicated if I put it as HideFlags.HideAndDontSave.

To fix this issue, I implemented this workaround:
I attach a special MonoBehaviour to my Slave object and manually call a function on it just after being created to let it know that it is properly procedurally generated and not simply duplicated. After that, in Slave.Start function, I check if the function has been called: if not, this means that the GAO has been duplicated, so I destroy it.
Here is the code:

[ExecuteInEditMode]
public class MasterObject : MonoBehaviour
{
    GameObject m_Slave;

    void Start()
    {
        m_Slave = new GameObject("Procedural Slave");
        m_Slave.AddComponent<Slave>().OnProceduralInstantiate(); // attach Slave behaviour and manually call a function to let it know that it's properly instantiated
        m_Slave.transform.SetParent(transform, false);
        m_Slave.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
    }
}

[ExecuteInEditMode]
public class Slave : MonoBehaviour
{
    bool m_HasBeenInstantiated = false;

    public void OnProceduralInstantiate()
    {
        m_HasBeenInstantiated = true;
    }
 
    void Start()
    {
        if (!m_HasBeenInstantiated)
        {
            // This means the GAO has been duplicated from Editor, so we delete it
            DestroyImmediate(gameObject);
        }
    }
}

And this works. However, this feels a bit ugly to me, I would like to know if there is a better way to solve this issue.

Thanks

You could look for the component in Master’s children

Beyond that there are methods to check child count and enumerate children but that would depend on what the rest of your hierarchy looks like.

Thank you for your suggestion. Indeed, I can use GetComponentsInChildren to destroy every Slaves just before creating the proper one:

[ExecuteInEditMode]
public class MasterObject : MonoBehaviour
{
    GameObject m_Slave;
    void Start()
    {      
        // if some slaves already exist, it's because they have been duplicated. So destroy them all!
        var slaves = GetComponentsInChildren<Slave>(true);
        for (int i = slaves.Length - 1; i >= 0; --i)
            DestroyImmediate(slaves[i].gameObject);

        m_Slave = new GameObject("Procedural Slave");
        m_Slave.AddComponent<Slave>();
        m_Slave.transform.SetParent(transform, false);
        m_Slave.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
    }
}

It works very fine. However, I am still not sure which solution is better, since GetComponentsInChildren is far from being free performance-wise.

Stupid question - how does duplication play with ExecuteInEditMode? IE - can you just null check m_Slave?

When MasterObject is duplicated, a new MasterObject and Slave objects (since Slave is child of MasterObject) are instantiated by the Editor. So it calls Start() on both objects.

No I cannot null check m_Slave: m_Slave is not serialized, so it always equals null from Start(). Obviously I could serialize m_Slave, but this solution seems super ugly to me, because all m_Slave properties serialized into my Scene file would reference invalid data then…

Yeah I was just curious if duplicating was smart enough.

Again, if your parent always starts out empty you can check transform.childCount. I think you could also simplify the component getting

void Start()
{
    m_Slave = GetComponentInChildren<Slave>();
    if (m_Slave == null)
    {
        // create new
    }
}

No, duplicating is not really smart…
Thanks again for your suggestions :slight_smile:

Sorry for bumping this; did you ever find a more reliable solution than your custom script and variable on the procedural GameObject?