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
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
class PrefabBrush : EditorWindow {
List<GameObject> prefabs = new List<GameObject>() {null};
Transform parent = null;
int selectedToolbar = 0;
bool alignNormals = false;
float scaleMin = 1, scaleMax = 1, scaleXMin = 1, scaleXMax = 1, scaleYMin = 1, scaleYMax = 1, scaleZMin = 1, scaleZMax = 1;
bool rotationX = false, rotationY = true, rotationZ = false;
bool painting = false;
bool selectObject = false;
int amount = 1;
float minRange = 1, maxRange = 1.5f;
bool checkOccipied = true;
List<GameObject> ignoreObjects = new List<GameObject>() {null};
Vector2 ignoreObjectsScrollPosition, prefabsScrollPosition;
float offsetX, offsetY, offsetZ;
public enum ScaleType {Individual, Uniform};
public ScaleType scaleType = ScaleType.Uniform;
[MenuItem ("Window/Prefab Brush")]
public static void ShowWindow () {
EditorWindow.GetWindow (typeof(PrefabBrush));
}
void OnEnable() {
SceneView.onSceneGUIDelegate += OnSceneGUI;
}
void OnDisable() {
SceneView.onSceneGUIDelegate -= OnSceneGUI;
}
void OnGUI () {
EditorGUILayout.LabelField ("Prefabs");
prefabsScrollPosition = GUILayout.BeginScrollView (prefabsScrollPosition, false, true, GUILayout.MinHeight(10), GUILayout.MaxHeight (100), GUILayout.ExpandHeight(true));
for (int i = 0; i < prefabs.Count; i++) {
prefabs[i] = EditorGUILayout.ObjectField (prefabs[i], typeof(GameObject), false) as GameObject;
}
GUILayout.EndScrollView ();
if (GUILayout.Button ("Add Item")) {
prefabs.Add (null);
}
if (GUILayout.Button ("Remove Item")) {
if (prefabs.Count > 1) {
prefabs.RemoveAt (prefabs.Count - 1);
}
}
parent = EditorGUILayout.ObjectField ("Parent", parent, typeof(Transform), true) as Transform;
selectedToolbar = GUILayout.Toolbar (selectedToolbar, new string[] {"Options", "Transformations"});
if (selectedToolbar == 0) {
EditorGUILayout.Space ();
EditorGUILayout.LabelField ("Multi", EditorStyles.boldLabel);
amount = EditorGUILayout.IntField ("Amount", amount);
amount = Mathf.Clamp (amount, 1, int.MaxValue);
if (amount > 1) {
minRange = EditorGUILayout.FloatField ("Min Range", minRange);
maxRange = EditorGUILayout.FloatField ("Max Range", maxRange);
}
EditorGUILayout.Space ();
EditorGUILayout.LabelField ("Collision", EditorStyles.boldLabel);
checkOccipied = EditorGUILayout.Toggle ("Check Occipied", checkOccipied);
if (checkOccipied == true) {
EditorGUILayout.LabelField ("Ignore Objects:");
ignoreObjectsScrollPosition = GUILayout.BeginScrollView (ignoreObjectsScrollPosition, false, true, GUILayout.MaxHeight (100));
for (int i = 0; i < ignoreObjects.Count; i++) {
ignoreObjects [i] = EditorGUILayout.ObjectField (ignoreObjects [i], typeof(GameObject), true) as GameObject;
}
GUILayout.EndScrollView ();
if (GUILayout.Button ("Add Item")) {
ignoreObjects.Add (null);
}
if (GUILayout.Button ("Remove Item")) {
if (ignoreObjects.Count > 1) {
ignoreObjects.RemoveAt (ignoreObjects.Count - 1);
}
}
}
EditorGUILayout.Space ();
selectObject = EditorGUILayout.Toggle ("Select Object", selectObject);
if (!painting) {
if (GUILayout.Button ("Start Painting")) {
painting = true;
}
} else {
if (GUILayout.Button ("Stop Painting")) {
painting = false;
}
}
} else {
EditorGUILayout.Space ();
EditorGUILayout.LabelField ("Scale", EditorStyles.boldLabel);
scaleType = (ScaleType)EditorGUILayout.EnumPopup ("Scale Type", scaleType);
if (scaleType == ScaleType.Uniform) {
scaleMin = EditorGUILayout.FloatField ("Min Scale", scaleMin);
scaleMax = EditorGUILayout.FloatField ("Max Scale", scaleMax);
if (GUILayout.Button ("Reset Scale")) {
scaleMin = 1;
scaleMax = 1;
}
} else {
scaleXMin = EditorGUILayout.FloatField ("Min X Scale", scaleXMin);
scaleXMax = EditorGUILayout.FloatField ("Max X Scale", scaleXMax);
scaleYMin = EditorGUILayout.FloatField ("Min Y Scale", scaleYMin);
scaleYMax = EditorGUILayout.FloatField ("Max Y Scale", scaleYMax);
scaleZMin = EditorGUILayout.FloatField ("Min Z Scale", scaleZMin);
scaleZMax = EditorGUILayout.FloatField ("Max Z Scale", scaleZMax);
if (GUILayout.Button ("Reset X Scale")) {
scaleXMin = 1;
scaleXMax = 1;
}
if (GUILayout.Button ("Reset Y Scale")) {
scaleYMin = 1;
scaleYMax = 1;
}
if (GUILayout.Button ("Reset Z Scale")) {
scaleZMin = 1;
scaleZMax = 1;
}
}
EditorGUILayout.Space ();
EditorGUILayout.LabelField ("Rotation", EditorStyles.boldLabel);
alignNormals = EditorGUILayout.Toggle ("Align Normals", alignNormals);
if (alignNormals == false) {
rotationX = EditorGUILayout.Toggle ("Random Rotation X", rotationX);
rotationY = EditorGUILayout.Toggle ("Random Rotation Y", rotationY);
rotationZ = EditorGUILayout.Toggle ("Random Rotation Z", rotationZ);
}
EditorGUILayout.Space ();
EditorGUILayout.LabelField ("Offset", EditorStyles.boldLabel);
offsetX = EditorGUILayout.FloatField ("Offset X", offsetX);
offsetY = EditorGUILayout.FloatField ("Offset Y", offsetY);
offsetZ = EditorGUILayout.FloatField ("Offset Z", offsetZ);
}
}
void OnSceneGUI(SceneView sv) {
Event currentEvent = Event.current;
if (painting && currentEvent.type == EventType.KeyDown && currentEvent.keyCode == KeyCode.P) {
Ray ray = Camera.current.ScreenPointToRay (new Vector3 (currentEvent.mousePosition.x, -currentEvent.mousePosition.y + Camera.current.pixelHeight));
RaycastHit raycastHit;
Vector3 originalPosition = Vector3.zero;
for (int i = 0; i < amount; i++) {
int tries = 0;
while (tries < 10) {
tries += 1;
if (i > 0) {
ray = new Ray (originalPosition + randomCircle (minRange, maxRange, 100), Vector3.down);
}
if (Physics.Raycast (ray, out raycastHit)) {
bool canContinue = true;
if (checkOccipied == true) {
for (int j = 0; j < ignoreObjects.Count; j++) {
if (ignoreObjects[j].Equals(raycastHit.transform.gameObject)) {
canContinue = false;
}
}
}
if (canContinue == true) {
GameObject prefab = prefabs[Random.Range(0, prefabs.Count)];
if (prefab != null) {
var o = PrefabUtility.InstantiatePrefab (prefab) as GameObject;
o.transform.SetParent (parent);
o.transform.position = raycastHit.point;
if (i == 0) {
originalPosition = raycastHit.point;
}
if (alignNormals == true) {
o.transform.rotation = Quaternion.FromToRotation (Vector3.up, raycastHit.normal) * Quaternion.identity;
} else {
int x = 0, y = 0, z = 0;
if (rotationX == true) {
x = Random.Range (0, 360);
}
if (rotationY == true) {
y = Random.Range (0, 360);
}
if (rotationZ == true) {
z = Random.Range (0, 360);
}
o.transform.Rotate (x, y, z);
}
if (scaleType == ScaleType.Uniform) {
float scale = Random.Range (scaleMin, scaleMax);
o.transform.localScale = new Vector3 (scale, scale, scale);
} else {
o.transform.localScale = new Vector3 (Random.Range (scaleXMin, scaleXMax), Random.Range (scaleYMin, scaleYMax), Random.Range (scaleZMin, scaleZMax));
}
if (selectObject) {
GameObject[] go = { o };
Selection.objects = go;
}
Undo.RegisterCreatedObjectUndo (o, "Paint Prefab " + o.name);
tries = 0;
break;
}
}
}
}
}
}
}
public Vector3 randomCircle(float minRange, float maxRange, float y) {
Vector3 position;
float angle = Random.value * 360;
position.x = Random.Range (minRange, maxRange) * Mathf.Sin (angle * Mathf.Deg2Rad);
position.y = y;
position.z = Random.Range (minRange, maxRange) * Mathf.Cos (angle * Mathf.Deg2Rad);
return position;
}
}