Search Unity

Is a 2D solution for FrameBunker's "Poor man's nested prefab" possible?

Discussion in 'Scripting' started by Rienhl, Apr 7, 2015.

  1. Rienhl

    Rienhl

    Joined:
    Nov 14, 2013
    Posts:
    42
    http://framebunker.com/blog/poor-mans-nested-prefabs/

    This script which enables users to work with "nested prefabs" seems to be a saver. I read a lot of good comments on it around the forums and it's for FREE.
    Now, my problem is that I want to use it for a 2D game, while this is written for 3D (meshes and stuff). I don't have the knowledge of Unity's internal Graphics system in order to make the neccesary changes to the script (if possible) but maybe somebody has already done it, or might be able to check it out and share some thoughts about it.

    Code (CSharp):
    1. using UnityEngine;
    2. #if UNITY_EDITOR
    3. using UnityEditor;
    4. using UnityEditor.Callbacks;
    5. #endif
    6. using System.Collections.Generic;
    7.  
    8. [ExecuteInEditMode]
    9. public class PrefabInstance : MonoBehaviour
    10. {
    11.     public GameObject prefab;
    12.  
    13. #if UNITY_EDITOR  
    14.     // Struct of all components. Used for edit-time visualization and gizmo drawing
    15.     public struct Thingy {
    16.         public Mesh mesh;
    17.         public Matrix4x4 matrix;
    18.         public List<Material> materials;
    19.     }
    20.  
    21.     [System.NonSerializedAttribute] public List<Thingy> things = new List<Thingy> ();
    22.  
    23.     void OnValidate () {
    24.         things.Clear();
    25.         if (enabled)
    26.             Rebuild (prefab, Matrix4x4.identity);
    27.     }
    28.  
    29.     void OnEnable () {
    30.         things.Clear();
    31.         if (enabled)
    32.             Rebuild (prefab, Matrix4x4.identity);
    33.     }
    34.  
    35.     void Rebuild (GameObject source, Matrix4x4 inMatrix) {
    36.         if (!source)
    37.             return;
    38.  
    39.         Matrix4x4 baseMat = inMatrix * Matrix4x4.TRS (-source.transform.position, Quaternion.identity, Vector3.one);
    40.        
    41.         foreach (Renderer mr in source.GetComponentsInChildren(typeof (Renderer), true))
    42.         {
    43.             things.Add(new Thingy () {
    44.                 mesh = mr.GetComponent<MeshFilter>().sharedMesh,
    45.                 matrix = baseMat * mr.transform.localToWorldMatrix,
    46.                 materials = new List<Material> (mr.sharedMaterials)
    47.             });
    48.         }
    49.  
    50.         foreach (PrefabInstance pi in source.GetComponentsInChildren(typeof (PrefabInstance), true))
    51.         {
    52.             if (pi.enabled && pi.gameObject.activeSelf)
    53.                 Rebuild (pi.prefab, baseMat * pi.transform.localToWorldMatrix);
    54.         }      
    55.     }
    56.  
    57.     // Editor-time-only update: Draw the meshes so we can see the objects in the scene view
    58.     void Update () {
    59.         if (EditorApplication.isPlaying)
    60.             return;
    61.         Matrix4x4 mat = transform.localToWorldMatrix;
    62.         foreach (Thingy t in things)
    63.             for (int i = 0; i < t.materials.Count; i++)
    64.                 Graphics.DrawMesh (t.mesh, mat * t.matrix, t.materials[i], gameObject.layer, null, i);
    65.     }
    66.  
    67.     // Picking logic: Since we don't have gizmos.drawmesh, draw a bounding cube around each thingy
    68.     void OnDrawGizmos () { DrawGizmos (new Color (0,0,0,0)); }
    69.     void OnDrawGizmosSelected () { DrawGizmos (new Color (0,0,1,.2f)); }
    70.     void DrawGizmos (Color col) {
    71.         if (EditorApplication.isPlaying)
    72.             return;
    73.         Gizmos.color = col;
    74.         Matrix4x4 mat = transform.localToWorldMatrix;
    75.         foreach (Thingy t in things)
    76.         {
    77.             Gizmos.matrix = mat * t.matrix;
    78.             Gizmos.DrawCube(t.mesh.bounds.center, t.mesh.bounds.size);
    79.         }      
    80.     }
    81.  
    82.     // Baking stuff: Copy in all the referenced objects into the scene on play or build
    83.     [PostProcessScene(-2)]
    84.     public static void OnPostprocessScene() {
    85.         foreach (PrefabInstance pi in UnityEngine.Object.FindObjectsOfType (typeof (PrefabInstance)))
    86.             BakeInstance (pi);
    87.     }
    88.  
    89.     public static void BakeInstance (PrefabInstance pi) {
    90.         if(!pi.prefab || !pi.enabled)
    91.             return;
    92.         pi.enabled = false;
    93.         GameObject go = PrefabUtility.InstantiatePrefab(pi.prefab) as GameObject;
    94.         Quaternion rot = go.transform.localRotation;
    95.         Vector3 scale = go.transform.localScale;
    96.         go.transform.parent = pi.transform;
    97.         go.transform.localPosition = Vector3.zero;
    98.         go.transform.localScale = scale;
    99.         go.transform.localRotation = rot;
    100.         pi.prefab = null;
    101.         foreach (PrefabInstance childPi in go.GetComponentsInChildren<PrefabInstance>())
    102.             BakeInstance (childPi);
    103.     }
    104.  
    105. #endif
    106. }
    I know there are assets on the store for doing this, but I don't believe I should pay for something that Unity is currently trying to solve. This one works and should be easily adapted for 2D.
     
  2. igarcez

    igarcez

    Joined:
    Sep 19, 2015
    Posts:
    7
    I have this 2D project, where I randomly pick a prefab and Instantiate it on runtime, so I managed to make it work with these (crude) changes:

    (please, bear in mind that I'm a total noob with unity yet, and with c# for that matter)
    Code (CSharp):
    1. using UnityEngine;
    2. #if UNITY_EDITOR
    3. using UnityEditor;
    4. using UnityEditor.Callbacks;
    5. #endif
    6. using System.Collections.Generic;
    7.  
    8. [ExecuteInEditMode]
    9. public class PrefabInstance2D : MonoBehaviour
    10. {
    11.     public GameObject prefab;
    12.  
    13. #if UNITY_EDITOR  
    14.     // Struct of all components. Used for edit-time visualization and gizmo drawing
    15.     public struct Thingy {
    16.         public Sprite sprite;
    17.         public Matrix4x4 matrix;
    18.         public List<Material> materials;
    19.     }
    20.  
    21.     [System.NonSerializedAttribute] public List<Thingy> things = new List<Thingy> ();
    22.  
    23.     void OnValidate () {
    24.         things.Clear();
    25.         if (enabled)
    26.             Rebuild (prefab, Matrix4x4.identity);
    27.     }
    28.  
    29.     void OnEnable () {
    30.         things.Clear();
    31.         if (enabled)
    32.             Rebuild (prefab, Matrix4x4.identity);
    33.     }
    34.  
    35.     void Rebuild (GameObject source, Matrix4x4 inMatrix) {
    36.         if (!source)
    37.             return;
    38.  
    39.         Matrix4x4 baseMat = inMatrix * Matrix4x4.TRS (-source.transform.position, Quaternion.identity, Vector3.one);
    40.      
    41.         foreach (SpriteRenderer mr in source.GetComponentsInChildren(typeof (SpriteRenderer), true))
    42.         {
    43.             things.Add(new Thingy () {
    44.                 sprite = mr.GetComponent<Sprite>(),
    45.                 matrix = baseMat * mr.transform.localToWorldMatrix,
    46.                 materials = new List<Material> (mr.sharedMaterials)
    47.             });
    48.         }
    49.  
    50.         foreach (PrefabInstance2D pi in source.GetComponentsInChildren(typeof (PrefabInstance2D), true))
    51.         {
    52.             if (pi.enabled && pi.gameObject.activeSelf)
    53.                 Rebuild (pi.prefab, baseMat * pi.transform.localToWorldMatrix);
    54.         }      
    55.     }
    56.     // I don't need editor scene view rendering of the prefabs, so I just commented this out
    57.     // Editor-time-only update: Draw the meshes so we can see the objects in the scene view
    58.     // void Update () {
    59.         // ...
    60.     // }
    61.  
    62.     // Picking logic: Since we don't have gizmos.drawmesh, draw a bounding cube around each thingy
    63.     //  ...
    64.  
    65.     // Baking stuff: Copy in all the referenced objects into the scene on play or build
    66.     [PostProcessScene(-2)]
    67.     public static void OnPostprocessScene() {
    68.         foreach (PrefabInstance2D pi in UnityEngine.Object.FindObjectsOfType (typeof (PrefabInstance2D)))
    69.             BakeInstance (pi);
    70.     }
    71.  
    72.     public static void BakeInstance (PrefabInstance2D pi) {
    73.         if(!pi.prefab || !pi.enabled)
    74.             return;
    75.         pi.enabled = false;
    76.         GameObject go = PrefabUtility.InstantiatePrefab(pi.prefab) as GameObject;
    77.         Quaternion rot = go.transform.localRotation;
    78.         Vector3 scale = go.transform.localScale;
    79.         go.transform.parent = pi.transform;
    80.         go.transform.localPosition = Vector3.zero;
    81.         go.transform.localScale = scale;
    82.         go.transform.localRotation = rot;
    83.         pi.prefab = null;
    84.         foreach (PrefabInstance2D childPi in go.GetComponentsInChildren<PrefabInstance2D>())
    85.             BakeInstance (childPi);
    86.     }
    87.  
    88. #endif
    89. }
    and on a script Instantiated on the randomly picked prefab, I call this on the start method (so I can do stuff to the nested prefab)
    Code (CSharp):
    1. foreach (PrefabInstance2D pi in UnityEngine.Object.FindObjectsOfType (typeof (PrefabInstance2D)))
    2.         PrefabInstance2D.BakeInstance (pi);
    3. #endif
    this works when I play on editor mode, but not when I build the project on android
     
  3. igarcez

    igarcez

    Joined:
    Sep 19, 2015
    Posts:
    7
    Okay, now I could make it work and build on android with the following very simplified version, I removed everything used to execute it on editor edit mode

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PrefabInstance2D : MonoBehaviour
    4. {
    5.     public GameObject prefab;
    6.  
    7.     public static void BakeInstance (PrefabInstance2D pi) {
    8.         if(!pi.prefab || !pi.enabled)
    9.             return;
    10.         pi.enabled = false;
    11.         GameObject go = Instantiate(pi.prefab);
    12.         Quaternion rot = go.transform.localRotation;
    13.         Vector3 scale = go.transform.localScale;
    14.         go.transform.parent = pi.transform;
    15.         go.transform.localPosition = Vector3.zero;
    16.         go.transform.localScale = scale;
    17.         go.transform.localRotation = rot;
    18.         pi.prefab = null;
    19.         foreach (PrefabInstance2D childPi in go.GetComponentsInChildren<PrefabInstance2D>())
    20.             BakeInstance (childPi);
    21.     }
    22. }
    it makes it harder to work without a preview, but maybe we can start implementing it from here...
     
  4. igarcez

    igarcez

    Joined:
    Sep 19, 2015
    Posts:
    7
    I could make it show up the sprite on the scene view with Gizmos.DrawGUITexture, you need to set the sprite Texture type to Advanced and check the read/write enabled

    then, you need to update the script to:
    Code (CSharp):
    1. using UnityEngine;
    2. #if UNITY_EDITOR
    3. using UnityEditor;
    4. using System.Collections.Generic;
    5. #endif
    6.  
    7. [ExecuteInEditMode]
    8. public class PrefabInstance2D : MonoBehaviour
    9. {
    10.     public GameObject prefab;
    11.  
    12.     public static void BakeInstance (PrefabInstance2D pi) {
    13.         if(!pi.prefab || !pi.enabled)
    14.             return;
    15.         pi.enabled = false;
    16.         GameObject go = Instantiate(pi.prefab);
    17.         Quaternion rot = go.transform.localRotation;
    18.         Vector3 scale = go.transform.localScale;
    19.         go.transform.parent = pi.transform;
    20.         go.transform.localPosition = Vector3.zero;
    21.         go.transform.localScale = scale;
    22.         go.transform.localRotation = rot;
    23.         pi.prefab = null;
    24.         foreach (PrefabInstance2D childPi in go.GetComponentsInChildren<PrefabInstance2D>())
    25.             BakeInstance (childPi);
    26.     }
    27.    
    28.     #if UNITY_EDITOR
    29.     public struct Thingy {
    30.         public Sprite sprite;
    31.         public Transform thingyTransform;
    32.     }
    33.    
    34.     [System.NonSerializedAttribute] public List<Thingy> things = new List<Thingy> ();
    35.    
    36.     void OnValidate () {
    37.         things.Clear();
    38.         if (enabled)
    39.             Rebuild (prefab);
    40.     }
    41.  
    42.     void OnEnable () {
    43.         things.Clear();
    44.         if (enabled)
    45.             Rebuild (prefab);
    46.     }
    47.    
    48.     void Rebuild (GameObject source) {
    49.         if (!source)
    50.             return;
    51.        
    52.         foreach (SpriteRenderer mr in source.GetComponentsInChildren(typeof (SpriteRenderer), true))
    53.         {
    54.             things.Add(new Thingy () {
    55.                 sprite = mr.sprite,
    56.                 thingyTransform = transform
    57.             });
    58.         }
    59.  
    60.         foreach (PrefabInstance2D pi in source.GetComponentsInChildren(typeof (PrefabInstance2D), true))
    61.         {
    62.             if (pi.enabled && pi.gameObject.activeSelf)
    63.                 Rebuild (pi.prefab);
    64.         }      
    65.     }
    66.    
    67.     void OnDrawGizmos () { DrawGizmos (new Color (0,0,0,0)); }
    68.     void OnDrawGizmosSelected () { DrawGizmos (new Color (0,0,1,.2f)); }
    69.     void DrawGizmos (Color col) {
    70.         if (EditorApplication.isPlaying)
    71.             return;
    72.         Gizmos.color = col;
    73.         foreach (Thingy t in things){
    74.            
    75.             var spriteTexture = new Texture2D ((int) t.sprite.rect.width, (int) t.sprite.rect.height);
    76.             var pixels = t.sprite.texture.GetPixels(
    77.                 (int) t.sprite.textureRect.x,
    78.                 (int) t.sprite.textureRect.y,
    79.                 (int) t.sprite.textureRect.width,
    80.                 (int) t.sprite.textureRect.height
    81.             );
    82.             spriteTexture.SetPixels(pixels);
    83.             spriteTexture.Apply();
    84.             Gizmos.DrawGUITexture(
    85.                 new Rect(
    86.                     t.thingyTransform.position.x,
    87.                     t.thingyTransform.position.y,
    88.                     t.thingyTransform.localScale.x,
    89.                     t.thingyTransform.localScale.y
    90.                 ),
    91.                 spriteTexture                      
    92.             );
    93.         }      
    94.     }
    95.     #endif
    96. }
     
  5. pertsa

    pertsa

    Joined:
    Jul 3, 2013
    Posts:
    15
    I wanted a simpler way to achieve this, so I wrote this:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEditor;
    4.  
    5. public class PrefabCreator : MonoBehaviour {
    6.  
    7.     public GameObject prefab;
    8.     public GameObject go;
    9.  
    10.     // Use this for initialization
    11.     void Start ()
    12.     {
    13.         go = Instantiate<GameObject>(prefab);
    14.         go.transform.position = transform.position;
    15.         go.transform.parent = transform.parent;
    16.     }
    17.  
    18.     void OnDrawGizmos()
    19.     {
    20.         if (gameObject.transform.Find(prefab.name + "(Clone)") == null)
    21.         {
    22.             go = Instantiate<GameObject>(prefab);
    23.             go.transform.parent = gameObject.transform;
    24.         }
    25.         else if (null != go)
    26.         {
    27.             go.transform.position = transform.position;
    28.         }
    29.     }
    30.  
    31.     // Update is called once per frame
    32.     void Update ()
    33.     {
    34.         Destroy (gameObject);
    35.     }
    36. }