Search Unity

How to intentionally break a prefab connection?

Discussion in 'Editor & General Support' started by ArachnidAnimal, Jan 27, 2016.

  1. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,651
    Working with prefabs, I don't quite understand how to purposely break a prefab connection in Unity.

    I have this situation:
    I have a gameobject. I create a prefab from it. Now I decide I need to change the source mesh.
    I go into the source mesh and change it, but I want the prefab to be updated with the updates to the source mesh.
    I have to force Unity to break the pre-fab by doing something like deleting a gameobject from the prefab. This then forces the gameobject to be updated with changes to the source mesh. This is what I want to happen.
    The problem is I cannot figure out how to break the prefab connection without having to delete something from the prefab instance to force Unity to point back to the source mesh.
    Is there some easier way to do this?
    In the inspector I see the following buttons: Select, Revert, Open.
    There is no button to "break" the prefab connection.
    This is my gripe with Prefabs, it does not seem to easily support this workflow

    I would expect to be able to hit a button called "Break" and then create the prefab again
     
    Last edited: Jan 27, 2016
    dogmirian likes this.
  2. fffMalzbier

    fffMalzbier

    Joined:
    Jun 14, 2011
    Posts:
    2,899
    To beak the gameobjects connection to the prefab you can select the gameobject and go into the menu Gameobject -> Beak Prefab Instance.
    I hope i did understand you correctly. If not would be nice to hear back what you mean instead.
     
  3. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,651
    Thanks, that is what I was looking for.
    I assumed there must have been a way to do it.

    That solves half of the problem.
    Now I want the broken prefab to point back to the source mesh. It still points to the prefab if you hit the "select button", even if you "break" the prefab

    brokenPrefab.png

    The arrow points to the source mesh. Since I created the pre-fab, i update the source meshes.
    Maybe there's no way to do this other than creating a brand-new gameobject, or manually adding the additional meshs and mesh renderers.
    It seems to be tedious when you have complex pre-fabs with a deep hierarchy. I keep shooting myself in the foot everytime I create a pre-fab and then decide I need to change the source meshs or add additional meshes to the original 3d model

    j
     
  4. s_guy

    s_guy

    Joined:
    Feb 27, 2013
    Posts:
    102
    I'm not sure why, but the "break prefab" option doesn't fully sever the connection. The easiest way to do that is to use the object instance to create a new prefab (drag to the project folder) and delete the new prefab, presuming you don't want it.

    There is no built-in way to get a prefab instance to point to it's previous association.

    I think the functionality you're looking for, where the model part of an object instance retains its connection to the source, as a prefab, would be covered under the "nested prefabs" feature discussed here. As far as I know, that feature hasn't been implemented yet and is still being debated on how it should work.

    Alternatively, you could create a script with a public field that references a model, link it up, and then instantiate the model at runtime. That way, when your model changes you automatically get the changes in your object instance. One obvious downside is that you don't see your model in the scene instance. Also, you don't get to parent instance-specific stuff directly to the model's hierarchy in your object instance, but that'd need a script solution even with nested prefabs.
     
    nratcliff and ArachnidAnimal like this.
  5. Big-Ed

    Big-Ed

    Joined:
    Mar 28, 2015
    Posts:
    14
    1. Break the prefab. Seems to me this should break the link, but it doesn't.
    2. Next break the link to the original prefab by creating a new one to link to - In your Prefab folder in the project window, create a new empty prefab from the menu Assets[Menu]->Create->Prefab and name it something different.
    3. Drag the existing component from the Hierarchy window onto the new empty prefab - the link to the old one is gone.
    Now the component will be linked to the new Prefab and you will still have the existing
     
  6. TwoHeadedDog

    TwoHeadedDog

    Joined:
    Jul 13, 2015
    Posts:
    5
    To completly destroy the connection you have to instantiate the gameobject. i wrote a littlescript for this.

    #if UNITY_EDITOR
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    using System.Linq;

    public static class BreakPrefab
    {
    [MenuItem ("GameObject/Disconnect Prefab Instance", false, 0)]
    public static void BREAK ()
    {
    List<GameObject> news = new List<GameObject> ();
    while (Selection.gameObjects.Length > 0) {
    GameObject old = Selection.gameObjects [0];

    string name = old.name;
    int index = old.transform.GetSiblingIndex ();

    GameObject _new = MonoBehaviour.Instantiate (old) as GameObject;
    MonoBehaviour.DestroyImmediate (old);

    _new.name = name;
    _new.transform.SetSiblingIndex (index);
    news.Add (_new);
    }
    Selection.objects = news.ToArray ();
    }
    }
    #endif
     
  7. mairendecarmena

    mairendecarmena

    Joined:
    Nov 7, 2013
    Posts:
    3
    Watch out!
    Breaking the prefab connection, then destroying the prefab from the Project Tab (Unity Editor), will effectively destroy the prefab connection. In your current scene it all will be good, all the instantiated prefab hierarchy will stand still...

    BUT in other scenes it will delete/break the hierarchy, thus you will have to recreate your missing scene gameobjects an their script connections AGAIN... :_(
     
  8. gungnir

    gungnir

    Joined:
    Nov 27, 2009
    Posts:
    16
    Modified version of NoGame0life's script. Use at your own risk!
    Code (CSharp):
    1.     public static class BreakPrefab
    2.     {
    3.         [MenuItem ("GameObject/Break Prefab Instance Completely", false, 0)]
    4.         public static void BreakPrefabConnectionStrong()
    5.         {
    6.             List<GameObject> news = new List<GameObject>();
    7.  
    8.             if (Selection.gameObjects.Length > 0)
    9.             {
    10.                 if (EditorUtility.DisplayDialog("Breaking prefab connection", "This is dangerous and can lead to broken objects and broken links to this object because it will be destroyed and recreated. Are you sure?", "I'm Sure", "Cancel"))
    11.                 {
    12.                     while (Selection.gameObjects.Length > 0)
    13.                     {
    14.                         GameObject old = Selection.gameObjects[0];
    15.                        
    16.                         if (PrefabUtility.GetPrefabType(old) != PrefabType.None)
    17.                         {
    18.                             Scene oldScene = old.scene;
    19.                             Transform oldParent = old.transform.parent;
    20.                             string oldName = old.name;
    21.                             int oldSiblingIndex = old.transform.GetSiblingIndex();
    22.  
    23.                             GameObject replacementObject = MonoBehaviour.Instantiate(old) as GameObject;
    24.                             Undo.RegisterCreatedObjectUndo(replacementObject, "Created object to break prefab.");
    25.                             Undo.DestroyObjectImmediate(old);
    26.  
    27.                             if (replacementObject.scene != oldScene)
    28.                             {
    29.                                 Undo.MoveGameObjectToScene(replacementObject, oldScene, "Move new object to scene");
    30.                             }
    31.  
    32.                             if (oldParent != null)
    33.                             {
    34.                                 Undo.SetTransformParent(replacementObject.transform, oldParent, "Set correct parent");
    35.                             }
    36.  
    37.                             replacementObject.name = oldName;
    38.                             replacementObject.transform.SetSiblingIndex(oldSiblingIndex);
    39.                             news.Add(replacementObject);
    40.                         }
    41.                         else
    42.                         {
    43.                             Debug.LogWarning("Object: " + old.name + " is not a prefab and will be ignored.");
    44.                         }
    45.                     }
    46.  
    47.                     Selection.objects = news.ToArray();
    48.                 }
    49.             }
    50.         }
    51.     }
     
    Last edited: Sep 4, 2017
  9. rad1c

    rad1c

    Joined:
    Feb 26, 2016
    Posts:
    21
    Modified version of gungnir's modified version of NoGame0life's script. Added object position and rotation info so the new GO will be moved in the place of the deleted one + added all mandatory 'using' to make the code a simple CTRL+A copy-paste, as well as moved text from the code into consts for an easier edit + better code readability.

    What it still doesn't do is, making a copy of the mesh (filter & renderer) itself.
    So if you want to "extract" something from a prefab, i.e. put parts of it on the scene, "detach" from the source prefab to make it 100% standalone, then delete the original prefab from your project, BUT that prefab stores all the mesh info, well, you are doomed.
    As soon as you delete the original prefab, the meshes will go away with it and your ?detached? GOs (even if you created a new prefab from them) will lose mesh data, as that will remain a reference to the original prefab, no matter what you do.
    I tried several things to make a clone of the original mesh data but failed (Instantiate, create a brand new mesh and copy over values from the original then assign this mesh to the newly created GO, etc).
    It seems virtually impossible to "extract" this from the "original" prefab, i.e. to make a standalone, cloned mesh. Hope someone finds a way to do this.

    Still use at your own risk...
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3. using UnityEngine.SceneManagement;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. public static class BreakPrefab {
    8.    private const string k_WARNING_MSG_HEADER = "Breaking prefab connection";
    9.    private const string k_WARNING_MSG_TEXT = "This is dangerous and can lead to broken objects and broken links to this object because it will be destroyed and recreated. Are you sure?";
    10.    private const string k_WARNING_MSG_POS_ANSWER = "I'm Sure";
    11.    private const string k_WARNING_MSG_NEG_ANSWER = "Cancel";
    12.    private const string k_UNDO_LOG_CREATED = "Created object to break prefab.";
    13.    private const string k_UNDO_LOG_MOVED = "Move new object to scene";
    14.    private const string k_UNDO_LOG_PARENTED = "Set correct parent";
    15.    private const string k_WARNING_LOG_NOT_PREFAB = "Object: {0} is not a prefab and will be ignored.";
    16.  
    17.    [MenuItem ("GameObject/Break Prefab Instance Completely", false, 0)]
    18.    public static void BreakPrefabConnectionStrong() {
    19.        List<GameObject> news = new List<GameObject>();
    20.  
    21.        if (Selection.gameObjects.Length > 0) {
    22.            if (EditorUtility.DisplayDialog(k_WARNING_MSG_HEADER, k_WARNING_MSG_TEXT, k_WARNING_MSG_POS_ANSWER, k_WARNING_MSG_NEG_ANSWER)) {
    23.                while (Selection.gameObjects.Length > 0) {
    24.                    GameObject old = Selection.gameObjects[0];
    25.  
    26.                    if (PrefabUtility.GetPrefabType(old) != PrefabType.None) {
    27.                        Scene oldScene = old.scene;
    28.                        Transform oldParent = old.transform.parent;
    29.                        Quaternion oldRot = old.transform.rotation;
    30.                        Vector3 oldPos = old.transform.position;
    31.                        string oldName = old.name;
    32.                        int oldSiblingIndex = old.transform.GetSiblingIndex();
    33.  
    34.                        GameObject replacementObject = MonoBehaviour.Instantiate(old) as GameObject;
    35.                        Undo.RegisterCreatedObjectUndo(replacementObject, k_UNDO_LOG_CREATED);
    36.                        Undo.DestroyObjectImmediate(old);
    37.  
    38.                        if (replacementObject.scene != oldScene) {
    39.                            Undo.MoveGameObjectToScene(replacementObject, oldScene, k_UNDO_LOG_MOVED);
    40.                        }
    41.  
    42.                        if (oldParent != null) {
    43.                            Undo.SetTransformParent(replacementObject.transform, oldParent, k_UNDO_LOG_PARENTED);
    44.                        }
    45.  
    46.                        replacementObject.name = oldName;
    47.                        replacementObject.transform.rotation = oldRot;
    48.                        replacementObject.transform.position = oldPos;
    49.                        replacementObject.transform.SetSiblingIndex(oldSiblingIndex);
    50.                        news.Add(replacementObject);
    51.                    } else {
    52.                        Debug.LogWarning(string.Format(k_WARNING_LOG_NOT_PREFAB, old.name));
    53.                    }
    54.                }
    55.                Selection.objects = news.ToArray();
    56.            }
    57.        }
    58.    }
    59. }
     
  10. gmxtian

    gmxtian

    Joined:
    Jul 10, 2012
    Posts:
    8
    2018.2 and above updated method. Right click on the game object and select "unpack prefab".
     
unityunity