Search Unity

Creating GameObject at runtime from child prefab

Discussion in 'Prefabs' started by MatthieuPr, Nov 13, 2018.

  1. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    I did my best to find a suitable title, but after 5min I just went with this one :D

    What I would like to do and so far I have looked at the PrefabUtility and seems to be not possible:

    I have a simple script on in the hierarchy of a GameObject, let's call it ParentScript. I would like to be able to dynamically create a certain amount of children for ParentGameObject, based on how many children that prefab already has and if all children are of the same prefab.

    Code (CSharp):
    1. public void SetChildrenCount(int amount)
    2. {
    3.     foreach(Transform child in transform)
    4.     {
    5.         //need to figure out this part
    6.     }
    7. }
    8.  
    That last part is what causes the most problems:
    - How can I know at runtime if the gameObject I am looking at is a nested prefab?
    - How can I find the prefab resource of the children present?
    - How can I get the GameObject linked to the nested prefab so I can instantiate more if amount > childCount?

    The purpose is fairly simple, a dynamic system like an inventory would benefit from this greatly. I need x amount of items at runtime, I can't predict the amount correctly when the game is not running, I wish to be able to visualise in editor mode how it would look like with different amount of items (1,3,7,22,...).

    I wish to grab the prefab from the child object as this would allow me to reuse the ParentScript in different places.

    If anyone knows of a way to do this or part of this would be great. So far things I have looked at in the PrefabUtility:

    string path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(child.gameObject); (empty)
    GameObject obj = PrefabUtility.GetCorrespondingObjectFromOriginalSource(child.gameObject); (null)
    PrefabAssetType type = PrefabUtility.GetPrefabAssetType(child.gameObject); (notAPrefab)
    bool isPrefab = PrefabUtility.IsAnyPrefabInstanceRoot(child.gameObject); (false)

    I could use a reference in the ParentScript to the prefab, but I would still need to check if each child is of that specific prefab...
     
  2. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Hi. Prefabs as a feature does not exist at runtime. There are no Prefab instances, no nested Prefabs or Variants etc. at runtime; only GameObjects that get cloned when you call Instantiate. As such, the information you are looking for doesn't exist. That's also why Instantiated "Prefabs" in Play mode don't look like Prefabs have say "(Clone)" in their name.
     
  3. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    That is what I assumed after reading as much as I can in the forums, just asked to make sure...

    @runevision I hope I don't bother too much with all my questions, but was wondering if following is possible:

    Can I delete every child nested prefab/gameObject from a specific parent (ParentGameObject with ParentScript) every time I leave prefab mode? And create them back when entering PrefabMode (this to allow UI artist to more easily see how it looks like).

    I have found
    "prefabStageClosing Callback that's invoked whenever a Prefab stage is about to be opened."
    from the documentation based on SteenLund's recommendation, but that is called before opening a prefab stage, I would like to have one when I actually leave a prefab stage to allow me to do customisation (like cleaning up the prefab) and save it with the new cleaned up state.

    In the samples I have found a way already to discover what stage editor is (main stage or prefab stage) in the GameObjectType.cs but there is no clear example related to knowing in what stage the editor is.
     
  4. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    So I started to experiment with checking if I can use some standard methods to achieve the above mentioned problem, and this has given me some very strange results:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5. using UnityEditor.SceneManagement;
    6.  
    7. [ExecuteAlways]
    8. public class ParentScript : MonoBehaviour
    9. {
    10.     /*
    11.     public void Start()
    12.     {
    13.         SetChildAmount(5);
    14.     }
    15.  
    16.     public void SetChildAmount(int amount)
    17.     {
    18.         int inactiveChild = 0;
    19.         string path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(gameObject);
    20.  
    21.         foreach(Transform child in transform)
    22.         {
    23.             if(!child.gameObject.activeSelf)
    24.                 inactiveChild +=1;
    25.             GameObject obj = PrefabUtility.GetCorrespondingObjectFromOriginalSource(child.gameObject);
    26.             PrefabAssetType type = PrefabUtility.GetPrefabAssetType(child.gameObject);
    27.             bool isPrefab = PrefabUtility.IsAnyPrefabInstanceRoot(child.gameObject);
    28.            
    29.             Debug.LogError("path: "+path);
    30.             Debug.LogError("object: "+obj);
    31.             Debug.LogError("asset type: "+ type);
    32.             Debug.LogError("isPrefab: "+ isPrefab);
    33.         }
    34.     }
    35.     /**/
    36.     void Awake()
    37.     {
    38.         Debug.LogError("Awake");
    39.         var mainStage = StageUtility.GetMainStageHandle();
    40.         var currentStage = StageUtility.GetStageHandle(gameObject);
    41.         if(currentStage == mainStage)
    42.         {
    43.             Debug.LogError("GameObject is in the MainStage");
    44.         }
    45.         else
    46.         {
    47.             Debug.LogError("GameObject is in the PrefabStage");
    48.         }
    49.     }
    50.     void Start()
    51.     {
    52.         Debug.LogError("Start");
    53.         var mainStage = StageUtility.GetMainStageHandle();
    54.         var currentStage = StageUtility.GetStageHandle(gameObject);
    55.         if(currentStage == mainStage)
    56.         {
    57.             Debug.LogError("GameObject is in the MainStage");
    58.         }
    59.         else
    60.         {
    61.             Debug.LogError("GameObject is in the PrefabStage");
    62.         }
    63.     }
    64.     void OnDestroy()
    65.     {
    66.         Debug.LogError("OnDestroy");
    67.         var mainStage = StageUtility.GetMainStageHandle();
    68.         var currentStage = StageUtility.GetStageHandle(gameObject);
    69.         if(currentStage == mainStage)
    70.         {
    71.             Debug.LogError("GameObject is in the MainStage");
    72.         }
    73.         else
    74.         {
    75.             Debug.LogError("GameObject is in the PrefabStage");
    76.         }
    77.     }
    78.     void OnDisable()
    79.     {
    80.         Debug.LogError("OnDisable");
    81.         var mainStage = StageUtility.GetMainStageHandle();
    82.         var currentStage = StageUtility.GetStageHandle(gameObject);
    83.         if(currentStage == mainStage)
    84.         {
    85.             Debug.LogError("GameObject is in the MainStage");
    86.         }
    87.         else
    88.         {
    89.             Debug.LogError("GameObject is in the PrefabStage");
    90.         }
    91.     }
    92.  
    93.     void OnEnable()
    94.     {
    95.         Debug.LogError("OnEnable");
    96.         var mainStage = StageUtility.GetMainStageHandle();
    97.         var currentStage = StageUtility.GetStageHandle(gameObject);
    98.         if(currentStage == mainStage)
    99.         {
    100.             Debug.LogError("GameObject is in the MainStage");
    101.         }
    102.         else
    103.         {
    104.             Debug.LogError("GameObject is in the PrefabStage");
    105.         }
    106.     }
    107. }
    108.  

    when going into prefab mode I get following call order:
    - Awake (in PrefabStage)
    - OnEnable (in PrefabStage)
    - OnDisable (in PrefabStage)
    - OnEnable (in PrefabStage)
    - Start (in PrefabStage)

    when returning to editor mode
    - OnDisable (in PrefabStage)
    - OnDestroy (in PrefabStage)
     
  5. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    Also found some inconsistent behaviour when running my tests and made bug report about it as well ;)

    (Case 1100740) Editor wrongly things he is in editor mode when changing a Serializefield value in prefab mode

    It feels that while I am trying to make something very simple, the new prefab system just chews me up and spits me back out at every turn :D
     
  6. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    It's very different from what Prefab Mode was designed for, so there may not be anything specifically supporting this at the moment.

    Also, the objects can be saved into the file at many different points while in Prefab Mode (whenever you click Save, or almost constantly if Auto Save is on). I'm not sure what you would gain from deleting objects from the Prefab specifically when closing Prefab Mode only.

    Right, the extra OnDisable+OnEnable is due to how we internally first load the Prefab in a temporary scene before moving it over into the final preview scene. As far as I understand, this was not originally the case, but was added to fix some physics issues. We want to look into whether this can be avoided.
     
    Last edited: Nov 14, 2018
  7. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    The reason for deleting when exiting prefab mode is that I can't do this in editor mode :D If I try to delete nested prefabs in editor mode I get error message that can't do this, that is why I need to do this while in prefab mode.

    The purpose here is performance for mobile devices. When the prefabs become very heavy due to a large amount of children with UI elements (inventory items) we do not wish to Instantiate the children that will be deleted as first action and recreated dynamically to have the correct amount. We have seen several severe performance drops in our project.

    The first attempt to solve this was to maintain a reference to the children create in prefab mode, but this doesn't work.
    reported: 1089690 (Open) nested prefab loses reference to original if created from editor script
    Our script who generates the nested prefabs/gameobjects loses the reference to it when exiting prefab mode.
    We maintain 2 lists for Object pooling and integer to know how many we wish to have visible in editor/prefab mode for UI artist to work with. If reference would be kept in the lists we could just reuse the children already present, but that doesn't work.

    Next solution I found was based on examples, check in what stage I am (prefab or editor stage) and do all functionality in the OnEnable or OnDisable calls with [ExecuteAlways] tag. This is just testing script to see if that works and after that can look into cleaning up the script more and turning it into an Editor only script.

    But that doesn't work either due to different (imo) bug.
    1100740 (Open) Editor wrongly things he is in editor mode when changing a Serializefield value in prefab mode


    In short: I tried different ways to delete the children of the prefab for performance reasons and so far not been able to do that due to running into what looks to me like bugs.
     
  8. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    You could try spawning the items and set it's HideFlags to DontSave. That should work - unless prefab mode doesn't respect HideFlags properly.
     
  9. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    Ideally I would like them to be saved and referenced, that way I can reuse them at runtime as well. (first bug)
    But good idea, will try to check on that option :D If prefab modes doesn't respect flag would just be another bug to report ;)
     
  10. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    219
    The logs you see here are correct since it is the instance in your scene that are being updated after the Prefab Mode changes are saved. Auto save will save to the Prefab Asset as soon as you have changed the value and all instances in the scene are updated to reflect the changes of the Prefab Asset.
    You can also check by adding more info to your logging:
    Debug.LogError("GameObject is in the MainStage " + GetInstanceID());


    If you change the Inspector to debug mode (upper right dropdown in the Inspector) you can see the instanceID of your PLRParent component on the instance in the scene and see they match what is being logged.

    Also if you remove the instance in the scene and open the Prefab in Prefab Mode you will not see these logs.
     
  11. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    What I am missing there is the identical logs for the prefab mode as well. If I change the amount of nested prefabs in prefab mode, my prefab should be updated with the auto save option and that doesn't happen.
    Result of that not happening is that I might get the correct logs that the prefab is modified in the main stage, but when I actually go into the main stage the prefab has different child amount than I have in the prefab mode (generated by the script). Update in main stage makes sense, but what is wrong is that the prefab itself wasn't properly updated so the instance in the main scene no longer reflects what I get from opening prefab mode and executing my script...
     
  12. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Maybe I'm missing what you mean, but the Quantity property on the script on your Prefab instance in the Main Stage is overridden, meaning it won't receive updates from the Prefab Asset.
     
  13. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    219
    I have made a couple of changes to your CollectionEditor.cs script, specifically the OnInspectorGUI() method, the two ways I see you can handle dynamically controlling child objects: Either by unpacking the Prefab Instance first or disallowing changes for instances.

    Code (CSharp):
    1.     public override void OnInspectorGUI()
    2.     {
    3.         bool isPrefabInstance = PrefabUtility.IsPartOfPrefabInstance(collection);
    4.         if (isPrefabInstance)
    5.         {
    6.             EditorGUILayout.HelpBox("Cannot restructure a Prefab Instance directly. This script is dynamically changing the structure of the Prefab so open Prefab Mode and change the number of children there.", MessageType.Info);
    7.         }
    8.  
    9.         using (new EditorGUI.DisabledScope(isPrefabInstance))
    10.         {
    11.             DrawDefaultInspector();
    12.    
    13.             if (Application.isPlaying)
    14.                 return;
    15.    
    16.             if (collection == null)
    17.                 collection = (PLRCollection)target;
    18.    
    19.             if (collection.prefab != null &&
    20.                 (collection.GetCount() != collection.quantity || GUILayout.Button("Update Clones")))
    21.             {
    22.                 /* Alternatively to the disabled scope you could unpack the Prefab instance before restructuring:
    23.                 if (PrefabUtility.IsPartOfPrefabInstance(collection))
    24.                 {
    25.                     var root = PrefabUtility.GetOutermostPrefabInstanceRoot(collection.gameObject);
    26.                     PrefabUtility.UnpackPrefabInstance(root, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
    27.                 }
    28.                 */
    29.  
    30.                 collection.Create<MonoBehaviour>(collection.quantity, true);
    31.             }
    32.         }  
    33.     }
    Let me know if this works for you or not.
     
    Last edited: Nov 15, 2018
  14. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    219
    In Prefab Mode the opened Prefab Asset is not a Prefab Instance; it is the contents of the Prefab you are editing directly (notice the root is a plain GameObject (no blue icon) so there is no updating/merging of Prefab instances going on there, except if you have nested Prefabs.
     
  15. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    This is becoming complicated cause multiple projects now reference here. The project with Quantity isn't the one we have been discussing, project is the one from bug 1100740.

    Property there is Amount (good thing I called them differently, so easier to differentiate between scripts). The Amount is not in bold nor is it ever overwritten by script, as script only works in prefab mode.

    Code (CSharp):
    1. void OnDisable()
    2.     {
    3.         Debug.LogError("OnDisable");
    4.  
    5.         var mainStage = StageUtility.GetMainStageHandle();
    6.         var currentStage = StageUtility.GetStageHandle(gameObject);
    7.         if(currentStage == mainStage)
    8.         {
    9.             Debug.LogError("GameObject is in the MainStage");
    10.         }
    11.         else
    12.         {
    13.             Debug.LogError("GameObject is in the PrefabStage");
    14.             while(transform.childCount > 0)
    15.             {
    16.                 DestroyImmediate(transform.GetChild(0).gameObject);
    17.             }
    18.         }
    19.     }
    20.  
    21.     void OnEnable()
    22.     {
    23.         Debug.LogError("OnEnable");
    24.  
    25.         if(prefab == null)
    26.             return;
    27.  
    28.         var mainStage = StageUtility.GetMainStageHandle();
    29.         var currentStage = StageUtility.GetStageHandle(gameObject);
    30.         if(currentStage == mainStage)
    31.         {
    32.             Debug.LogError("GameObject is in the MainStage");
    33.         }
    34.         else
    35.         {
    36.             Debug.LogError("GameObject is in the PrefabStage");
    37.            
    38.             for(int i = 0; i < amount; i++)
    39.             {
    40.                 GameObject res = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
    41.                 if(res)
    42.                 {
    43.                     res.transform.SetParent(transform);
    44.                 }
    45.             }
    46.         }
    47.     }
    48. }
    When I go into prefab mode I have the correct amount of nested prefabs added to my gameObject, in this case 6. Auto-save is on, when I turn it off the save button is greyed out so I assume that when I use this prefab in my scene it would have 6 nested prefab in there. When I drag the prefab into the scene it has only 1 child nested prefab.

    What I would like:
    1) Amount creates the correct amount of children in prefab mode
    2) Same amount of nested prefabs are present in the scene when I drag prefab there
    3) Script keeping a reference to the nested prefabs I have created so at runtime I can actually use them in a pooling system (hide those I don't need and use the amount the game requires in that particular screen).

    This is not reflected in the current script cause of above mentioned bug reports. So my current solution is to create when going into prefab mode and destroying after leaving prefab mode.
     
  16. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    I have nested prefabs, that is the entire purpose of the script I am trying to write. Having a reference to a prefab combined with an integer and having a script creating the correct amount of nested prefabs as child of the object that has the script. After that, I try to find workarounds cause I do not keep a reference to those children (see post right above on what I want to accomplish).
     
  17. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    Thanks for the editor script, got some time this morning to test is and partially works. I still have the issue that when in prefab mode I change the amount of nested prefabs using editor script it doesn't get saved in the prefab.

    Editor script part:
    Code (CSharp):
    1.  
    2.             if(GUILayout.Button("Destoy Inactives"))
    3.             {
    4.                 if(!Application.isPlaying)
    5.                 {
    6.                     collection.Clear(true);
    7.                 }
    8.             }
    Method that is being called from the editor script:
    Code (CSharp):
    1. public void Clear(bool destroy = false)
    2.     {
    3.         for(int i = quantity; i < activeObjects.Count; )
    4.         {
    5.             unactiveObjects.Add(activeObjects[i]);
    6.             activeObjects.RemoveAt(i);
    7.         }
    8.  
    9.         foreach (GameObject obj in unactiveObjects)
    10.         {
    11.             if(destroy)
    12.             {
    13.                 if (!Application.isPlaying && Application.isEditor)
    14.                     DestroyImmediate(obj);
    15.             }
    16.             else
    17.             {
    18.                 obj.SetActive(false);
    19.             }
    20.         }
    21.  
    22.         if (destroy)
    23.         {
    24.             unactiveObjects.Clear();
    25.         }
    26.     }
    Should I submit a new bug report for this as well? I don't think I have an open bug for this behaviour yet...
     
  18. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    219
    Hi Matthieu,
    You need to make sure the PrefabStage's scene is dirtied (so saving will kick in) when modifying the PrefabStage objects without using SerializedProperties. To dirty the PrefabStage's scene try the following:
    Code (CSharp):
    1. var prefabStage = PrefabStageUtility.GetPrefabStage(yourGameObject);
    2. if (prefabStage != null)
    3. {
    4.      EditorSceneManager.MarkSceneDirty(prefabStage.scene);
    5. }
     
  19. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    small question on this, the "yourGameObject" needs to be the root object (so the opened prefab) or can it be any nested gameObject of that prefab. cause I can not guarantee where UI artists places the script in hierarchy...
     
  20. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    219
    It can be any GameObject that are in the PrefabStage
     
  21. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    Thanks, got it to work and modified slightly to be disabled as well at runtime. For others that have been reading here I will post code asap, nicely structured in case you wish to do similar out of the box UI tools ;)
     
  22. MatthieuPr

    MatthieuPr

    Joined:
    May 4, 2017
    Posts:
    56
    Editor script:

    Code (CSharp):
    1.  
    2. using UnityEditor;
    3. using UnityEditor.Experimental.SceneManagement;
    4. using UnityEditor.SceneManagement;
    5. using UnityEngine;
    6.  
    7. [CustomEditor(typeof(Collection))]
    8. public class CollectionEditor : Editor {
    9.     Collection collection;
    10.    
    11.     void Awake()
    12.     {
    13.         if (collection == null)
    14.             collection = (Collection)target;
    15.     }
    16.     public override void OnInspectorGUI()
    17.     {
    18.  
    19.         bool isPrefabInstance = PrefabUtility.IsPartOfPrefabInstance(collection);
    20.         if (isPrefabInstance)
    21.         {
    22.             EditorGUILayout.HelpBox("Cannot restructure a Prefab Instance directly. This script is dynamically changing the structure of the Prefab so open Prefab Mode and change the number of children there.", MessageType.Info);
    23.         }
    24.         using (new EditorGUI.DisabledScope(isPrefabInstance))
    25.         {
    26.             DrawDefaultInspector();
    27.  
    28.             if(GUILayout.Button("Destoy Inactives"))
    29.             {
    30.                 if(!Application.isPlaying)
    31.                 {
    32.                     collection.Clear(true);
    33.                     var prefabStage = PrefabStageUtility.GetPrefabStage(collection.gameObject);
    34.                     if (prefabStage != null)
    35.                     {
    36.                         EditorSceneManager.MarkSceneDirty(prefabStage.scene);
    37.                     }
    38.                     else
    39.                     {
    40.                         Debug.LogError("Prefab stage is null");
    41.                     }
    42.                 }
    43.             }
    44.  
    45.             if (Application.isPlaying)
    46.                 return;
    47.  
    48.             if (collection == null)
    49.                 collection = (Collection)target;
    50.  
    51.             if (collection.Prefab != null && collection.GetCount() != collection.Quantity)
    52.             {
    53.                 /* Alternatively to the disabled scope you could unpack the Prefab instance before restructuring:
    54.                 if (PrefabUtility.IsPartOfPrefabInstance(collection))
    55.                 {
    56.                     var root = PrefabUtility.GetOutermostPrefabInstanceRoot(collection.gameObject);
    57.                     PrefabUtility.UnpackPrefabInstance(root, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
    58.                 }
    59.                 */
    60.                 collection.Create<MonoBehaviour>(collection.Quantity, true);
    61.             }
    62.         }
    63.     }
    64. }
    65.  
    Collection class:

    Code (CSharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. public class Collection : MonoBehaviour
    8. {
    9.     [SerializeField] private GameObject _prefab;
    10.     [Range(0, 20)][SerializeField] private int _quantity = 1;
    11.     [HideInInspector][SerializeField] private List<GameObject> _activeObjects;
    12.     [HideInInspector][SerializeField] private List<GameObject> _unactiveObjects;
    13.  
    14.     public GameObject Prefab {get{return _prefab;}}
    15.     public int Quantity {get{return _quantity;}}
    16.     public List<GameObject> ActiveObjects
    17.     {
    18.         get
    19.         {
    20.            if(_activeObjects == null)
    21.                 _activeObjects = new List<GameObject>();
    22.             return _activeObjects;
    23.         }
    24.     }
    25.     public List<GameObject> UnactiveObjects
    26.     {
    27.         get
    28.         {
    29.             if(_unactiveObjects == null)
    30.                 _unactiveObjects = new List<GameObject>();
    31.             return _unactiveObjects;
    32.         }
    33.     }
    34.     public int GetCount()
    35.     {
    36.         if (_activeObjects == null)
    37.             return 0;
    38.        
    39.         if (!Application.isPlaying && Application.isEditor)
    40.             foreach (var go in _activeObjects)
    41.                 if (go == null)
    42.                     return 0;
    43.        
    44.         return _activeObjects.Count;
    45.     }
    46.  
    47.     public void Create<T>(int n, bool destroy = false) where T : Object
    48.     {
    49.         if (_prefab == null) {
    50.             DebugUtils.Assert(false, "No prefab defined for collection "+PLRUnityUtils.GetGameObjectPath(gameObject));
    51.             return;
    52.         }
    53.  
    54.         Clear();
    55.  
    56.         for (int i = 0; i < n-_activeObjects.Count; i++)
    57.         {
    58.             ActivateNewObject();
    59.         }
    60.  
    61.         return;
    62.     }
    63.     GameObject ActivateNewObject()
    64.     {
    65.         GameObject go;
    66.         if (_unactiveObjects.Count > 0)
    67.         {
    68.             go = _unactiveObjects[0];
    69.             _unactiveObjects.Remove(go);
    70.             go.SetActive(true);
    71. }
    72.         else
    73.         {
    74.             go = PrefabUtility.InstantiatePrefab(_prefab) as GameObject;
    75.             go.transform.SetParent(transform);
    76.             go.transform.localScale = Vector3.one;
    77.             go.transform.localRotation = Quaternion.identity;
    78.             go.transform.localPosition = Vector3.zero;
    79.         }
    80.  
    81.         go.transform.SetAsLastSibling();
    82.         _activeObjects.Add(go);
    83.         return go;
    84.     }
    85.     public void Clear(bool destroy = false)
    86.     {
    87.         for(int i = _quantity; i < _activeObjects.Count; )
    88.         {
    89.             _unactiveObjects.Add(_activeObjects[i]);
    90.             _activeObjects.RemoveAt(i);
    91.         }
    92.  
    93.         foreach (GameObject obj in _unactiveObjects)
    94.         {
    95.             if(destroy)
    96.             {
    97.                 if (!Application.isPlaying && Application.isEditor)
    98.                     DestroyImmediate(obj);
    99.             }
    100.             else
    101.             {
    102.                 obj.SetActive(false);
    103.             }
    104.         }
    105.  
    106.         if (destroy)
    107.         {
    108.             _unactiveObjects.Clear();
    109.         }
    110.     }
    111. }
    What does this do:
    in prefab mode
    - It allows you to easily change the amount of nested prefabs in a prefab (just by moving the slider around).
    - You can easily clean up the prefab from all the inactive ones (press of Button in inspector)
    in editor mode
    - inspector is disabled cause shouldn't interact with it.
    at runtime:
    - inspector is disabled cause shouldn't interact with it
    - can access the active and inactive lists of nested prefabs, this allows you to call scripts on them as you want
    - can create more nested prefabs as you need them
     
  23. All_American

    All_American

    Joined:
    Oct 14, 2011
    Posts:
    1,528
    I am getting an error when trying to access the gameObject after it instantiates. Am I getting an error saying I cannot do anything to it because it is a prefab and for security purposes.
     
  24. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
  25. All_American

    All_American

    Joined:
    Oct 14, 2011
    Posts:
    1,528
    play mode...runtime.
     
  26. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    It's generally a good idea to copy paste the exact error message instead of just a description of it. Is this the error you get?

    Destroying assets is not permitted to avoid data loss.
    If you really want to remove an asset use DestroyImmediate (theObject, true);​

    If so, your code is trying to modify the Prefab Asset itself rather than the instantiated Prefab instance. This is indeed not supported in Play Mode in order to prevent mistakes that modify your project, which runtime code generally shouldn't do.
     
  27. All_American

    All_American

    Joined:
    Oct 14, 2011
    Posts:
    1,528

    It's this one, and another one similar.

    "Setting the parent of a transform which resides in a Prefab Asset is disabled to prevent data corruption"


    Sorry it was late...
     
  28. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Right. You can't add a parent to the root of a Prefab Asset. You probably want to modify the Prefab instance instead.
     
  29. All_American

    All_American

    Joined:
    Oct 14, 2011
    Posts:
    1,528
    I think what I was doing was just trying to do it too soon.