Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

"Poor mans nested prefabs" in object pool

Discussion in 'Scripting' started by michal_k, Nov 16, 2015.

  1. michal_k

    michal_k

    Joined:
    Jul 31, 2014
    Posts:
    33
    Hi,

    I was using Poor mans nested prefab for some time (because I am a poor man :p )
    http://framebunker.com/blog/poor-mans-nested-prefabs/

    It works well for static gameobjects but for dynamic / object pools not so well.
    Code (CSharp):
    1.     public static void BakeInstance (PrefabInstance pi) {
    2.         if(!pi.prefab || !pi.enabled)
    3.             return;
    4.         pi.enabled = false;
    5.         GameObject go = PrefabUtility.InstantiatePrefab(pi.prefab) as GameObject;
    6.         Quaternion rot = go.transform.localRotation;
    7.         Vector3 scale = go.transform.localScale;
    8.         go.transform.parent = pi.transform;
    9.         go.transform.localPosition = Vector3.zero;
    10.         go.transform.localScale = scale;
    11.         go.transform.localRotation = rot;
    12.         pi.prefab = null;
    13.         foreach (PrefabInstance childPi in go.GetComponentsInChildren<PrefabInstance>())
    14.             BakeInstance (childPi);
    15.     }
    PrefabUtility is Editor only. Is it possible to use this somehow on runtime (bake all prefabs before runtime?), or should I give up and use the recommended "best practice" I've found:
    use one God class with references to all prefabs and instantiate everything in code.
    (
    I was trying to implement easily-manageable solution for "God class" in this thread: http://forum.unity3d.com/threads/wo...t-a-good-idea-loading-prefabs-by-name.368042/)


    Thanks
     
  2. jmjd

    jmjd

    Joined:
    Nov 14, 2012
    Posts:
    48
    It's been awhile since I used this code, but at one point I did modify the "poor man's nested prefabs" to get it working at runtime.

    Below is my modified script, that has 3 main changes:
    1. Around line 55, I added some code because I was getting errors with skinned meshes.
    2. I've commented out OnPostprocessScene, and added a Start function so that BakeInstance gets called at runtime.
    3. BakeInstance just calls GameObject.Instantiate(), but this would be where you would spawn from your pool manager.

    Code (CSharp):
    1. //
    2. // This script is a modified version of "Poor Man's Nested Prefabs" (http://framebunker.com/blog/poor-mans-nested-prefabs/)
    3. // This script allows you to nest prefab objects and see them under the prefab
    4. //
    5.  
    6. using UnityEngine;
    7.  
    8. #if UNITY_EDITOR
    9. using UnityEditor;
    10. using UnityEditor.Callbacks;
    11. #endif
    12.  
    13. using System.Collections.Generic;
    14.  
    15. [ExecuteInEditMode]
    16. public class NestedPrefabInstance : MonoBehaviour
    17. {
    18.     public GameObject prefab;
    19.  
    20. #if UNITY_EDITOR
    21.     // Struct of all components. Used for edit-time visualization and gizmo drawing
    22.     public struct Thingy
    23.     {
    24.         public Mesh mesh;
    25.         public Matrix4x4 matrix;
    26.         public List<Material> materials;
    27.     }
    28.  
    29.     [System.NonSerializedAttribute]
    30.     public List<Thingy> things = new List<Thingy>();
    31.  
    32.     void OnValidate()
    33.     {
    34.         things.Clear();
    35.         if(enabled)
    36.             Rebuild(prefab, Matrix4x4.identity);
    37.     }
    38.  
    39.     void OnEnable()
    40.     {
    41.         things.Clear();
    42.         if(enabled)
    43.             Rebuild(prefab, Matrix4x4.identity);
    44.     }
    45.  
    46.     void Rebuild(GameObject source, Matrix4x4 inMatrix)
    47.     {
    48.         if(!source)
    49.             return;
    50.  
    51.         Matrix4x4 baseMat = inMatrix * Matrix4x4.TRS(-source.transform.position, Quaternion.identity, Vector3.one);
    52.  
    53.         foreach(Renderer mr in source.GetComponentsInChildren(typeof(Renderer), true))
    54.         {
    55.             Mesh m = null;
    56.             MeshRenderer meshRenderer = mr as MeshRenderer;
    57.             SkinnedMeshRenderer skinRenderer = mr as SkinnedMeshRenderer;
    58.             if(meshRenderer != null)
    59.                 m = mr.GetComponent<MeshFilter>().sharedMesh;
    60.             else if(skinRenderer != null)
    61.                 m = skinRenderer.sharedMesh;
    62.  
    63.             if(m != null)
    64.             {
    65.                 things.Add(new Thingy()
    66.                 {
    67.                     mesh = m,
    68.                     matrix = baseMat * mr.transform.localToWorldMatrix,
    69.                     materials = new List<Material>(mr.sharedMaterials)
    70.                 });
    71.             }
    72.         }
    73.  
    74.         foreach(NestedPrefabInstance pi in source.GetComponentsInChildren(typeof(NestedPrefabInstance), true))
    75.         {
    76.             if(pi.enabled && pi.gameObject.activeSelf)
    77.                 Rebuild(pi.prefab, baseMat * pi.transform.localToWorldMatrix);
    78.         }
    79.     }
    80.  
    81.     // Editor-time-only update: Draw the meshes so we can see the objects in the scene view
    82.     void Update()
    83.     {
    84.         if(EditorApplication.isPlaying)
    85.             return;
    86.         Matrix4x4 mat = transform.localToWorldMatrix;
    87.         foreach(Thingy t in things)
    88.             for(int i = 0; i < t.materials.Count; i++)
    89.                 Graphics.DrawMesh(t.mesh, mat * t.matrix, t.materials[i], gameObject.layer, null, i);
    90.     }
    91.  
    92.     // Picking logic: Since we don't have gizmos.drawmesh, draw a bounding cube around each thingy
    93.     void OnDrawGizmos() { DrawGizmos(new Color(0, 0, 0, 0)); }
    94.     void OnDrawGizmosSelected() { DrawGizmos(new Color(0, 0, 1, .2f)); }
    95.     void DrawGizmos(Color col)
    96.     {
    97.         if(EditorApplication.isPlaying)
    98.             return;
    99.         Gizmos.color = col;
    100.         Matrix4x4 mat = transform.localToWorldMatrix;
    101.         foreach(Thingy t in things)
    102.         {
    103.             Gizmos.matrix = mat * t.matrix;
    104.             Gizmos.DrawCube(t.mesh.bounds.center, t.mesh.bounds.size);
    105.         }
    106.     }
    107.  
    108. //    // Baking stuff: Copy in all the referenced objects into the scene on play or build
    109. //    [PostProcessScene(-2)]
    110. //    public static void OnPostprocessScene()
    111. //    {
    112. //        foreach(NestedPrefabInstance pi in UnityEngine.Object.FindObjectsOfType(typeof(NestedPrefabInstance)))
    113. //            BakeInstance(pi);
    114. //    }
    115.  
    116. #endif
    117.  
    118.     void Start()
    119.     {
    120.         if(Application.isPlaying)
    121.             BakeInstance(this);
    122.     }
    123.  
    124.     public static void BakeInstance(NestedPrefabInstance pi)
    125.     {
    126.         if(!pi.prefab || !pi.enabled)
    127.             return;
    128.         pi.enabled = false;
    129. //        GameObject go = PrefabUtility.InstantiatePrefab(pi.prefab) as GameObject;
    130.         GameObject go = null;
    131.         go = GameObject.Instantiate(pi.prefab) as GameObject;
    132.  
    133.         Quaternion rot = go.transform.localRotation;
    134.         Vector3 scale = go.transform.localScale;
    135.         go.transform.parent = pi.transform;
    136.         go.transform.localPosition = Vector3.zero;
    137.         go.transform.localScale = scale;
    138.         go.transform.localRotation = rot;
    139. //        pi.prefab = null;
    140.         foreach(NestedPrefabInstance childPi in go.GetComponentsInChildren<NestedPrefabInstance>())
    141.             BakeInstance(childPi);
    142.     }
    143. }
    144.  

    Hope this helps!
     
  3. michal_k

    michal_k

    Joined:
    Jul 31, 2014
    Posts:
    33
    Thanks!