Search Unity

"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:
    50
    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!