Daniel Lip
Daniel Lip

Reputation: 11341

How can I destroy a gameobject that is part of a prefab?

I want to use the ModifyPrefab method but not sure how. The problem is when I'm destroying the oldGameObject :

DestroyImmediate(oldGameObject);

I'm getting exception in the editor :

InvalidOperationException: Destroying a GameObject inside a Prefab instance is not allowed.

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class PrefabSwitch : MonoBehaviour
{
    /// <summary>The new object to instantiate in place of the old object.</summary>
    public GameObject newPrefab;
    /// <summary>The old objects, intended to be swapped out for iterations of 
    /// the new object.</summary>
    public GameObject[] oldGameObjects;
    /// <summary>The string tag to use when replacing objects by tag.</summary>
    public string searchByTag;

    /// <summary>Swaps all the game objects in oldGameObjects for 
    /// a new newPrefab.</summary>
    public void SwapAllByArray(GameObject[] objectsToSwap)
    {
        // Store a boolean to detect if we intend to swap this game object.
        bool swappingSelf = false;

        // For each game object in the oldGameObjects array, 
        for (int i = 0; i < oldGameObjects.Length; i++)
        {
            // If the current game object is this game object, 
            if (oldGameObjects[i] == gameObject)
            {
                // Enable the flag to swap this game object at the end, so we
                // do not destroy it before the script an complete its task.
                swappingSelf = true;
            }
            else
            {
                // Else, we are not dealing with the game object local to this 
                // script, so we can swap the prefabs, immediately. 
                SwapPrefabs(oldGameObjects[i]);
            }
        }

        // If we have flagged the local game object to be swapped, 
        if (swappingSelf)
        {
            // Swap the local game object.
            SwapPrefabs(gameObject);
        }
    }

    /// <summary>Swaps all the game objects that use the tag <code>searchByTag</code>.
    /// If empty, we will use the tag of the local game object.</summary>
    public void SwapAllByTag()
    {
        // If searchByTag is null, 
        if (searchByTag == "")
        {
            // Set searchByTag to the tag of the local game object.
            searchByTag = gameObject.tag;
        }

        // Find all the game objects using the tag searchByTag, 
        // store them in our array, and proceed to swapping them.
        oldGameObjects = GameObject.FindGameObjectsWithTag(searchByTag);
        SwapAllByArray(oldGameObjects);
    }

    /// <summary>Swaps the desired oldGameObject for a newPrefab.</summary>
    /// <param name="oldGameObject">The old game object.</param>
    void SwapPrefabs(GameObject oldGameObject)
    {
        // Determine the rotation and position values of the old game object.
        // Replace rotation with Quaternion.identity if you do not wish to keep rotation.
        Quaternion rotation = oldGameObject.transform.rotation;
        Vector3 position = oldGameObject.transform.position;

        // Instantiate the new game object at the old game objects position and rotation.
        GameObject newGameObject = Instantiate(newPrefab, position, rotation);

        // If the old game object has a valid parent transform,
        // (You can remove this entire if statement if you do not wish to ensure your
        // new game object does not keep the parent of the old game object.
        if (oldGameObject.transform.parent != null)
        {
            // Set the new game object parent as the old game objects parent.
            newGameObject.transform.SetParent(oldGameObject.transform.parent);
        }

        // Destroy the old game object, immediately, so it takes effect in the editor.
        DestroyImmediate(oldGameObject);
    }

    private void ModifyPrefab()
    {
        // Get the Prefab Asset root GameObject and its asset path.
        GameObject assetRoot = Selection.activeObject as GameObject;
        string assetPath = AssetDatabase.GetAssetPath(assetRoot);

        // Load the contents of the Prefab Asset.
        GameObject contentsRoot = PrefabUtility.LoadPrefabContents(assetPath);

        // Modify Prefab contents.
        contentsRoot.AddComponent<BoxCollider>();

        // Save contents back to Prefab Asset and unload contents.
        PrefabUtility.SaveAsPrefabAsset(contentsRoot, assetPath);
        PrefabUtility.UnloadPrefabContents(contentsRoot);
    }
}

Upvotes: 2

Views: 3032

Answers (1)

Athanasios Kataras
Athanasios Kataras

Reputation: 26460

This is heavily discussed here: https://forum.unity.com/threads/destroying-a-gameobject-inside-a-prefab-instance-is-not-allowed.555868/

It is not exactly a bug, but a side effect from the implementation of the PreFab system.

Top solutions are:

if (UnityEditor.PrefabUtility.IsPartOfPrefabInstance(transform))

   UnityEditor.PrefabUtility.UnpackPrefabInstance(gameObject,
         UnityEditor.PrefabUnpackMode.Completely,
         UnityEditor.InteractionMode.AutomatedAction);

// Then I do DestroyImmediate();

And this:

var rootGO = PrefabUtility.LoadPrefabContents(pathToMyPrefab);
// Destroy child objects or components on rootGO
PrefabUtility.SaveAsPrefabAsset(rootGO, pathToMyPrefab);
prefabUtility.UnloadPrefabContents(rootGO);

Upvotes: 3

Related Questions