3
votes

Usually you would instantiate a clone of a prefab in a script using

var obj = (GameObject)Instantiate(....);

and than change it's parent using

var obj.transform.SetParent(...);

However I'm writing on a little editor script for instantiating prefabs over the menu entries that should keep their link to the prefab. I do it in a static class like:

public static class EditorMenu
{
    // This is a reference that I already have and is not null
    privtae static Transform exampleParent;

    [MenuItem("Example/Create Clone")]
    private static void CreateClone()
    {
        var prefab = AssetDatabase.LoadAssetAtPath("Example/Path/myObject.prefab", typeof(GameObject));
        var obj = (GameObject)PrefabUtility.InstantiatePrefab(prefab);

        obj.transform.position = Vector3.zero;
        obj.transform.rotation = Quaternion.identity;

        Selection.activeGameObject = obj;
    }
}

I had to use

(GameObject)PrefabUtility.InstantiatePrefab(prefab);

because Instantiate() doesn't keep the link to the prefab but creates a clone.

So the above code works great so far and the object is inserted to the scene as if I would drag and drop it.

Now I would like to change this newly instantiated objects parent to parent so I added the line

obj.transform.SetParent(exampleParent, true);

I also tried

obj.transform.parent = exampleParent;

But both throw an exception:

Setting the parent of a transform which resides in a prefab is disabled to prevent data corruption. UnityEngine.Transform:SetParent(Transform)

prefab and thereby obj are the topmost GameObject of the prefab so I thought I am setting the parent of the whole instantiated object not any transform inside of the prefab hierachy.

How can I change the parent of a GameObject instantiated via PrefabUtility.InstantiatePrefab?


Update

A workarround I just tried was to actually use Instantiate and do

var obj = Object.Instantiate(prefab, cancel.transform);

// remove the added "(clone)" suffix
obj.name = prefab.name;
obj.transform.SetParent(cancel.transform);

however as mentioned before this doesn't completely keep the prefb functionality intact .. so it only allows me to Revert but not to Apply changes. As workaround it might be good enough however since in my case I use it as a shortcut for instantiating a prefab which users are not really supposed to change later...

2
Are you sure that Instantiate() can be called only from a MonoBehaviour? This is a static call GameObject.Instantiate().z3nth10n
@z3nth10n no I just updated that part ;) I actually tried it again but it is not the same as using InstantiatePrefab since Instantiate brakes the prefab functionality like Apply changesderHugo
I found the issue see post below the error message is bad formulated and the actual issue lies in a code I didn't provide so I voted to close this question.derHugo

2 Answers

2
votes

Sorry I just found the issue:

I didn't add it since the error message was confusing/bad formulated.

I was using Resources.FindObjectsOfTypeAll for checking if the exampleParent of type SomeType was in the scene.

Following Unity's example which should exclude prefabs I used

// Validation for the Menu item
[MenuItem("Example/Create Clone", true]
private bool TargetAvailable()
{
    foreach (var target in (SomeType[])Resources.FindObjectsOfTypeAll(typeof(SomeType))
    { 
        if (target.hideFlags == HideFlags.NotEditable || target.hideFlags == HideFlags.HideAndDontSave)
            continue;

        if (!EditorUtility.IsPersistent(target.transform.root.gameObject))
            continue;

        exampleParent = target.transform;
        return true;
    }

    exampleParent = null;
    return false;
}

But this seems actually to be wrong and not working since it allways returned me the SomeType reference from the prefabs! (I already found it a bit strange that they do

!EditorUtility.IsPersistent(target.transform.root.gameObject))
    continue;

I'm not sure if that ! maybe is a type in their example code?!


So the error which sounds like setting the parent was not allowed actually means and should say

setting the parent to a Transform which resides in a prefab is not allowed ...

than I would have found the actual issue in the first place.


So again as a workaround until I can figure out that FindObjectsOfTypeAll thing I switched to Object.FindObjectOfType instead assuming my target will always be active in the scene. And using SetParent works now together with PrefabUtitlity.InstantiatePrefab.

1
votes

How can I change the parent of a GameObject instantiated via PrefabUtility.InstantiatePrefab?

This is right, but make sure cancel is also an instantiated object.

obj.transform.parent = cancel.transform;