Search Unity

[Released] Created a free prefab brush

Discussion in 'Assets and Asset Store' started by MCrafterzz, Nov 5, 2017.

  1. MCrafterzz

    MCrafterzz

    Joined:
    Jun 3, 2017
    Posts:
    354
    I didn't want to buy one for my game so I created my own and decided to share it. It isn't the most pretty editor but it works well and has a lot of options. You can use it however you want as long as you don't reuppload it to for example the asset store. I hope you enjoy!

    Paint by hovering over the position you want to paint on and press P. You can only paint on objects with colliders.

    Features:
    - Place a random prefab from a list
    - Place multiple prefabs at the same time
    - Random scale (same for all axis or chosen specificly for each)
    - Random rotation or align rotation to the ground's normals
    - Check if position is empty or if an object already has been placed there
    - Offset object to place objects sunken into the ground
    - Toggle painting on and off

    Note: File must be named PrefabBrush for it to work
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. [System.Serializable]
    6. class PrefabBrush : EditorWindow {
    7.     List<GameObject> prefabs = new List<GameObject>() {null};
    8.     Transform parent = null;
    9.     int selectedToolbar = 0;
    10.     bool alignNormals = false;
    11.     float scaleMin = 1, scaleMax = 1, scaleXMin = 1, scaleXMax = 1, scaleYMin = 1, scaleYMax = 1, scaleZMin = 1, scaleZMax = 1;
    12.     bool rotationX = false, rotationY = true, rotationZ = false;
    13.     bool painting = false;
    14.     bool selectObject = false;
    15.     int amount = 1;
    16.     float minRange = 1, maxRange = 1.5f;
    17.     bool checkOccipied = true;
    18.     List<GameObject> ignoreObjects = new List<GameObject>() {null};
    19.     Vector2 ignoreObjectsScrollPosition, prefabsScrollPosition;
    20.     float offsetX, offsetY, offsetZ;
    21.  
    22.     public enum ScaleType {Individual, Uniform};
    23.     public ScaleType scaleType = ScaleType.Uniform;
    24.  
    25.     [MenuItem ("Window/Prefab Brush")]
    26.     public static void ShowWindow () {
    27.         EditorWindow.GetWindow (typeof(PrefabBrush));
    28.     }
    29.  
    30.     void OnEnable() {
    31.         SceneView.onSceneGUIDelegate += OnSceneGUI;
    32.     }
    33.  
    34.     void OnDisable() {
    35.         SceneView.onSceneGUIDelegate -= OnSceneGUI;
    36.     }
    37.  
    38.     void OnGUI () {
    39.         EditorGUILayout.LabelField ("Prefabs");
    40.         prefabsScrollPosition = GUILayout.BeginScrollView (prefabsScrollPosition, false, true, GUILayout.MinHeight(10), GUILayout.MaxHeight (100), GUILayout.ExpandHeight(true));
    41.         for (int i = 0; i < prefabs.Count; i++) {
    42.             prefabs[i] = EditorGUILayout.ObjectField (prefabs[i], typeof(GameObject), false) as GameObject;
    43.         }
    44.         GUILayout.EndScrollView ();
    45.         if (GUILayout.Button ("Add Item")) {
    46.             prefabs.Add (null);
    47.         }
    48.  
    49.         if (GUILayout.Button ("Remove Item")) {
    50.             if (prefabs.Count > 1) {
    51.                 prefabs.RemoveAt (prefabs.Count - 1);
    52.             }
    53.         }
    54.  
    55.         parent = EditorGUILayout.ObjectField ("Parent", parent, typeof(Transform), true) as Transform;
    56.  
    57.         selectedToolbar = GUILayout.Toolbar (selectedToolbar, new string[] {"Options", "Transformations"});
    58.  
    59.         if (selectedToolbar == 0) {
    60.             EditorGUILayout.Space ();
    61.             EditorGUILayout.LabelField ("Multi", EditorStyles.boldLabel);
    62.             amount = EditorGUILayout.IntField ("Amount", amount);
    63.             amount = Mathf.Clamp (amount, 1, int.MaxValue);
    64.  
    65.             if (amount > 1) {
    66.                 minRange = EditorGUILayout.FloatField ("Min Range", minRange);
    67.                 maxRange = EditorGUILayout.FloatField ("Max Range", maxRange);
    68.             }
    69.  
    70.             EditorGUILayout.Space ();
    71.             EditorGUILayout.LabelField ("Collision", EditorStyles.boldLabel);
    72.             checkOccipied = EditorGUILayout.Toggle ("Check Occipied", checkOccipied);
    73.  
    74.             if (checkOccipied == true) {
    75.                 EditorGUILayout.LabelField ("Ignore Objects:");
    76.                 ignoreObjectsScrollPosition = GUILayout.BeginScrollView (ignoreObjectsScrollPosition, false, true, GUILayout.MaxHeight (100));
    77.  
    78.                 for (int i = 0; i < ignoreObjects.Count; i++) {
    79.                     ignoreObjects [i] = EditorGUILayout.ObjectField (ignoreObjects [i], typeof(GameObject), true) as GameObject;
    80.                 }
    81.  
    82.                 GUILayout.EndScrollView ();
    83.                 if (GUILayout.Button ("Add Item")) {
    84.                     ignoreObjects.Add (null);
    85.                 }
    86.  
    87.                 if (GUILayout.Button ("Remove Item")) {
    88.                     if (ignoreObjects.Count > 1) {
    89.                         ignoreObjects.RemoveAt (ignoreObjects.Count - 1);
    90.                     }
    91.                 }
    92.             }
    93.  
    94.             EditorGUILayout.Space ();
    95.             selectObject = EditorGUILayout.Toggle ("Select Object", selectObject);
    96.  
    97.             if (!painting) {
    98.                 if (GUILayout.Button ("Start Painting")) {
    99.                     painting = true;
    100.                 }
    101.             } else {
    102.                 if (GUILayout.Button ("Stop Painting")) {
    103.                     painting = false;
    104.                 }
    105.             }
    106.         } else {
    107.             EditorGUILayout.Space ();
    108.             EditorGUILayout.LabelField ("Scale", EditorStyles.boldLabel);
    109.  
    110.             scaleType = (ScaleType)EditorGUILayout.EnumPopup ("Scale Type", scaleType);
    111.  
    112.             if (scaleType == ScaleType.Uniform) {
    113.                 scaleMin = EditorGUILayout.FloatField ("Min Scale", scaleMin);
    114.                 scaleMax = EditorGUILayout.FloatField ("Max Scale", scaleMax);
    115.  
    116.                 if (GUILayout.Button ("Reset Scale")) {
    117.                     scaleMin = 1;
    118.                     scaleMax = 1;
    119.                 }
    120.             } else {
    121.                 scaleXMin = EditorGUILayout.FloatField ("Min X Scale", scaleXMin);
    122.                 scaleXMax = EditorGUILayout.FloatField ("Max X Scale", scaleXMax);
    123.                 scaleYMin = EditorGUILayout.FloatField ("Min Y Scale", scaleYMin);
    124.                 scaleYMax = EditorGUILayout.FloatField ("Max Y Scale", scaleYMax);
    125.                 scaleZMin = EditorGUILayout.FloatField ("Min Z Scale", scaleZMin);
    126.                 scaleZMax = EditorGUILayout.FloatField ("Max Z Scale", scaleZMax);
    127.  
    128.                 if (GUILayout.Button ("Reset X Scale")) {
    129.                     scaleXMin = 1;
    130.                     scaleXMax = 1;
    131.                 }
    132.  
    133.                 if (GUILayout.Button ("Reset Y Scale")) {
    134.                     scaleYMin = 1;
    135.                     scaleYMax = 1;
    136.                 }
    137.  
    138.                 if (GUILayout.Button ("Reset Z Scale")) {
    139.                     scaleZMin = 1;
    140.                     scaleZMax = 1;
    141.                 }
    142.             }
    143.  
    144.             EditorGUILayout.Space ();
    145.             EditorGUILayout.LabelField ("Rotation", EditorStyles.boldLabel);
    146.             alignNormals = EditorGUILayout.Toggle ("Align Normals", alignNormals);
    147.  
    148.             if (alignNormals == false) {
    149.                 rotationX = EditorGUILayout.Toggle ("Random Rotation X", rotationX);
    150.                 rotationY = EditorGUILayout.Toggle ("Random Rotation Y", rotationY);
    151.                 rotationZ = EditorGUILayout.Toggle ("Random Rotation Z", rotationZ);
    152.             }
    153.  
    154.             EditorGUILayout.Space ();
    155.             EditorGUILayout.LabelField ("Offset", EditorStyles.boldLabel);
    156.  
    157.             offsetX = EditorGUILayout.FloatField ("Offset X", offsetX);
    158.             offsetY = EditorGUILayout.FloatField ("Offset Y", offsetY);
    159.             offsetZ = EditorGUILayout.FloatField ("Offset Z", offsetZ);
    160.         }
    161.     }
    162.        
    163.     void OnSceneGUI(SceneView sv) {
    164.         Event currentEvent = Event.current;
    165.         if (painting && currentEvent.type == EventType.KeyDown && currentEvent.keyCode == KeyCode.P) {
    166.             Ray ray = Camera.current.ScreenPointToRay (new Vector3 (currentEvent.mousePosition.x, -currentEvent.mousePosition.y + Camera.current.pixelHeight));
    167.             RaycastHit raycastHit;
    168.             Vector3 originalPosition = Vector3.zero;
    169.  
    170.             for (int i = 0; i < amount; i++) {
    171.                 int tries = 0;
    172.  
    173.                 while (tries < 10) {
    174.                     tries += 1;
    175.                     if (i > 0) {
    176.                         ray = new Ray (originalPosition + randomCircle (minRange, maxRange, 100), Vector3.down);
    177.                     }
    178.  
    179.                     if (Physics.Raycast (ray, out raycastHit)) {
    180.                         bool canContinue = true;
    181.  
    182.                         if (checkOccipied == true) {
    183.                             for (int j = 0; j < ignoreObjects.Count; j++) {
    184.                                 if (ignoreObjects[j].Equals(raycastHit.transform.gameObject)) {
    185.                                     canContinue = false;
    186.                                 }
    187.                             }
    188.                         }
    189.  
    190.  
    191.                         if (canContinue == true) {
    192.                             GameObject prefab = prefabs[Random.Range(0, prefabs.Count)];
    193.                             if (prefab != null) {
    194.                                 var o = PrefabUtility.InstantiatePrefab (prefab) as GameObject;
    195.                                 o.transform.SetParent (parent);
    196.                                 o.transform.position = raycastHit.point;
    197.                                 if (i == 0) {
    198.                                     originalPosition = raycastHit.point;
    199.                                 }
    200.  
    201.                                 if (alignNormals == true) {
    202.                                     o.transform.rotation = Quaternion.FromToRotation (Vector3.up, raycastHit.normal) * Quaternion.identity;
    203.                                 } else {
    204.                                     int x = 0, y = 0, z = 0;
    205.                                     if (rotationX == true) {
    206.                                         x = Random.Range (0, 360);
    207.                                     }
    208.  
    209.                                     if (rotationY == true) {
    210.                                         y = Random.Range (0, 360);
    211.                                     }
    212.  
    213.                                     if (rotationZ == true) {
    214.                                         z = Random.Range (0, 360);
    215.                                     }
    216.  
    217.                                     o.transform.Rotate (x, y, z);
    218.                                 }
    219.  
    220.                                 if (scaleType == ScaleType.Uniform) {
    221.                                     float scale = Random.Range (scaleMin, scaleMax);
    222.                                     o.transform.localScale = new Vector3 (scale, scale, scale);
    223.                                 } else {
    224.                                     o.transform.localScale = new Vector3 (Random.Range (scaleXMin, scaleXMax), Random.Range (scaleYMin, scaleYMax), Random.Range (scaleZMin, scaleZMax));
    225.                                 }
    226.  
    227.                                 if (selectObject) {
    228.                                     GameObject[] go = { o };
    229.                                     Selection.objects = go;
    230.                                 }
    231.                
    232.                                 Undo.RegisterCreatedObjectUndo (o, "Paint Prefab " + o.name);
    233.                                 tries = 0;
    234.                                 break;
    235.                             }
    236.                         }
    237.                     }
    238.                 }
    239.             }
    240.         }
    241.     }
    242.  
    243.     public Vector3 randomCircle(float minRange, float maxRange, float y) {
    244.         Vector3 position;
    245.         float angle = Random.value * 360;
    246.         position.x = Random.Range (minRange, maxRange) * Mathf.Sin (angle * Mathf.Deg2Rad);
    247.         position.y = y;
    248.         position.z = Random.Range (minRange, maxRange) * Mathf.Cos (angle * Mathf.Deg2Rad);
    249.         return position;
    250.     }
    251. }
     
    Last edited: Nov 5, 2017
    Mike891, blockhead and rvictorcardozo like this.
  2. blockhead

    blockhead

    Joined:
    Mar 23, 2013
    Posts:
    1
    This is great! So helpful to me. I had to edit it a bit to work with 2D and for my needs but thank you so much for posting this! I was having trouble finding many resources for creating such a tool.

    Thanks again!
     
    MCrafterzz likes this.
  3. Mike891

    Mike891

    Joined:
    May 18, 2019
    Posts:
    23
    Thanks for your efforts
     
    MCrafterzz likes this.
  4. Grenadine_

    Grenadine_

    Joined:
    Mar 9, 2020
    Posts:
    1
    Thanks for the script Mcrafterzz!

    In case someone uses this as a starting point and finds that the prefab is instanced way off, it might be that your display's resolution needs to be taken into account for the ray to cast from the correct place, like so:

    Code (CSharp):
    1.  Vector3 mousePos = Event.current.mousePosition;
    2.             float ppp = EditorGUIUtility.pixelsPerPoint;
    3.             mousePos.y = sv.camera.pixelHeight - mousePos.y*ppp;
    4.             mousePos.x *= ppp;
    5.             Ray ray = sv.camera.ScreenPointToRay(mousePos);
    (you can replace original posts' code line 166 with this)
    I found out thanks to Fauvent's post on this thread:
    https://forum.unity.com/threads/world-mouse-position-in-editorwindow.460917/