Skip to content

Commit ab1eea6

Browse files
Change PrefabBrushes Erase behaviour with small doc change. (#200)
* fix PrefabBrush's name with naming rule * update Paint and Eease behaviour for Prefab Brushes * update doc comment * update README.md * changed method name * added m_EraseAnyObjects to erase not related any game objects see below conversation #200 (comment) * added #pragma warning disable 0649
1 parent 7bac29c commit ab1eea6

File tree

4 files changed

+154
-112
lines changed

4 files changed

+154
-112
lines changed

Editor/Brushes/PrefabBrushes/BasePrefabBrush.cs

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using UnityEngine;
1+
using System.Collections.Generic;
2+
using UnityEngine;
23

34
namespace UnityEditor.Tilemaps
45
{
@@ -11,51 +12,24 @@ public class BasePrefabBrush : GridBrush
1112
/// </summary>
1213
public Vector3 m_Anchor = new Vector3(0.5f, 0.5f, 0.5f);
1314

14-
/// <summary>
15-
/// GameObject to instantiating
16-
/// </summary>
17-
protected GameObject Prefab = null;
18-
19-
protected static Transform GetObjectInCell(GridLayout grid, Transform parent, Vector3Int position)
15+
protected List<GameObject> GetObjectsInCell(GridLayout grid, Transform parent, Vector3Int position)
2016
{
17+
var results = new List<GameObject>();
2118
var childCount = parent.childCount;
2219
for (var i = 0; i < childCount; i++)
2320
{
2421
var child = parent.GetChild(i);
2522
if (position == grid.WorldToCell(child.position))
2623
{
27-
return child;
24+
results.Add(child.gameObject);
2825
}
2926
}
30-
return null;
27+
return results;
3128
}
3229

33-
public override void Erase(GridLayout grid, GameObject brushTarget, Vector3Int position)
30+
protected void InstantiatePrefabInCell(GridLayout grid, GameObject brushTarget, Vector3Int position, GameObject prefab)
3431
{
35-
if (brushTarget.layer == 31)
36-
{
37-
return;
38-
}
39-
40-
var erased = GetObjectInCell(grid, brushTarget.transform, position);
41-
if (erased != null)
42-
{
43-
Undo.DestroyObjectImmediate(erased.gameObject);
44-
}
45-
}
46-
47-
public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
48-
{
49-
// Do not allow editing palettes
50-
if (brushTarget.layer == 31 || brushTarget == null)
51-
return;
52-
53-
var tileObject = GetObjectInCell(grid, brushTarget.transform, position);
54-
if (tileObject)
55-
{
56-
return;
57-
}
58-
var instance = (GameObject)PrefabUtility.InstantiatePrefab(Prefab);
32+
var instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
5933
if (instance != null)
6034
{
6135
Undo.MoveGameObjectToScene(instance, brushTarget.scene, "Paint Prefabs");
Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,103 @@
1+
using System.Linq;
12
using UnityEngine;
23

34
namespace UnityEditor.Tilemaps
45
{
56
/// <summary>
6-
/// This Brush instances and places a selected prefab onto the targeted location and parents the instanced object to the paint target.
7+
/// This Brush instances and places a containing prefab onto the targeted location and parents the instanced object to the paint target.
78
/// </summary>
8-
[CreateAssetMenu(fileName = "Prefab brush", menuName = "2D Extras/Brushes/Prefab brush", order = 359)]
9+
[CreateAssetMenu(fileName = "New Prefab Brush", menuName = "2D Extras/Brushes/Prefab Brush", order = 359)]
910
[CustomGridBrush(false, true, false, "Prefab Brush")]
1011
public class PrefabBrush : BasePrefabBrush
1112
{
13+
#pragma warning disable 0649
1214
/// <summary>
1315
/// The selection of Prefab to paint from
1416
/// </summary>
15-
public GameObject m_Prefab;
17+
[SerializeField] GameObject m_Prefab;
18+
#pragma warning restore 0649
19+
1620
/// <summary>
17-
/// Use to remove all prefabs in the cell or just the one that is currently selected in m_Prefab
21+
/// If true, erases any GameObjects that are in a given position within the selected layers with Erasing.
22+
/// Otherwise, erases only GameObjects that are created from owned Prefab in a given position within the selected layers with Erasing.
1823
/// </summary>
19-
public bool m_ForceDelete;
24+
bool m_EraseAnyObjects;
25+
2026
/// <summary>
21-
/// Paints Prefabs into a given position within the selected layers.
27+
/// Paints GameObject from containg Prefab into a given position within the selected layers.
2228
/// The PrefabBrush overrides this to provide Prefab painting functionality.
2329
/// </summary>
2430
/// <param name="grid">Grid used for layout.</param>
2531
/// <param name="brushTarget">Target of the paint operation. By default the currently selected GameObject.</param>
2632
/// <param name="position">The coordinates of the cell to paint data to.</param>
27-
2833
public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
2934
{
30-
31-
32-
Prefab = m_Prefab;
33-
var tileObject = GetObjectInCell(grid, brushTarget.transform, position);
34-
if (tileObject == null || tileObject.name != m_Prefab.name)
35+
// Do not allow editing palettes
36+
if (brushTarget.layer == 31 || brushTarget == null)
3537
{
36-
base.Paint(grid, brushTarget, position);
38+
return;
39+
}
40+
41+
var objectsInCell = GetObjectsInCell(grid, brushTarget.transform, position);
42+
var existPrefabObjectInCell = objectsInCell.Any(objectInCell => PrefabUtility.GetCorrespondingObjectFromSource(objectInCell) == m_Prefab);
43+
44+
if (!existPrefabObjectInCell)
45+
{
46+
base.InstantiatePrefabInCell(grid, brushTarget, position, m_Prefab);
3747
}
3848
}
3949

4050
/// <summary>
41-
/// Erases all Prefabs in a given position within the selected layers if ForceDelete is true.
42-
/// Erase only selected Prefabs in a given position within the selected layers if ForceDelete is false.
51+
/// If "Erase Any Objects" is true, erases any GameObjects that are in a given position within the selected layers.
52+
/// If "Erase Any Objects" is false, erases only GameObjects that are created from owned Prefab in a given position within the selected layers.
4353
/// The PrefabBrush overrides this to provide Prefab erasing functionality.
4454
/// </summary>
4555
/// <param name="grid">Grid used for layout.</param>
4656
/// <param name="brushTarget">Target of the erase operation. By default the currently selected GameObject.</param>
4757
/// <param name="position">The coordinates of the cell to erase data from.</param>
4858
public override void Erase(GridLayout grid, GameObject brushTarget, Vector3Int position)
4959
{
50-
var erased = GetObjectInCell(grid, brushTarget.transform, position);
51-
if (erased == null)
60+
if (brushTarget.layer == 31 || brushTarget.transform == null)
5261
{
5362
return;
5463
}
55-
if (m_ForceDelete || (!m_ForceDelete && erased.gameObject.name == m_Prefab.name))
64+
65+
foreach (var objectInCell in GetObjectsInCell(grid, brushTarget.transform, position))
5666
{
57-
base.Erase(grid, brushTarget, position);
67+
if (m_EraseAnyObjects || PrefabUtility.GetCorrespondingObjectFromSource(objectInCell) == m_Prefab)
68+
{
69+
Undo.DestroyObjectImmediate(objectInCell);
70+
}
5871
}
5972
}
60-
}
61-
62-
/// <summary>
63-
/// The Brush Editor for a Prefab Brush.
64-
/// </summary>
65-
[CustomEditor(typeof(PrefabBrush))]
66-
public class PrefabBrushEditor : BasePrefabBrushEditor
67-
{
68-
private SerializedProperty m_Prefab;
69-
private SerializedProperty m_ForceDelete;
70-
71-
protected override void OnEnable()
72-
{
73-
base.OnEnable();
74-
m_Prefab = m_SerializedObject.FindProperty("m_Prefab");
75-
m_ForceDelete = m_SerializedObject.FindProperty("m_ForceDelete");
76-
}
7773

7874
/// <summary>
79-
/// Callback for painting the inspector GUI for the PrefabBrush in the Tile Palette.
80-
/// The PrefabBrush Editor overrides this to have a custom inspector for this Brush.
75+
/// The Brush Editor for a Prefab Brush.
8176
/// </summary>
82-
public override void OnPaintInspectorGUI()
77+
[CustomEditor(typeof(PrefabBrush))]
78+
public class PrefabBrushEditor : BasePrefabBrushEditor
8379
{
84-
base.OnPaintInspectorGUI();
85-
m_SerializedObject.UpdateIfRequiredOrScript();
86-
EditorGUILayout.PropertyField(m_Prefab, true);
87-
EditorGUILayout.PropertyField(m_ForceDelete, true);
88-
m_SerializedObject.ApplyModifiedPropertiesWithoutUndo();
80+
private PrefabBrush prefabBrush => target as PrefabBrush;
81+
private SerializedProperty m_Prefab;
82+
83+
protected override void OnEnable()
84+
{
85+
base.OnEnable();
86+
m_Prefab = m_SerializedObject.FindProperty(nameof(m_Prefab));
87+
}
88+
89+
/// <summary>
90+
/// Callback for painting the inspector GUI for the PrefabBrush in the Tile Palette.
91+
/// The PrefabBrush Editor overrides this to have a custom inspector for this Brush.
92+
/// </summary>
93+
public override void OnPaintInspectorGUI()
94+
{
95+
base.OnPaintInspectorGUI();
96+
m_SerializedObject.UpdateIfRequiredOrScript();
97+
EditorGUILayout.PropertyField(m_Prefab, true);
98+
prefabBrush.m_EraseAnyObjects = EditorGUILayout.Toggle("Erase Any Objects", prefabBrush.m_EraseAnyObjects);
99+
m_SerializedObject.ApplyModifiedPropertiesWithoutUndo();
100+
}
89101
}
90102
}
91103
}
Lines changed: 88 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,125 @@
1+
using System.Linq;
12
using UnityEngine;
23

34
namespace UnityEditor.Tilemaps
45
{
56
/// <summary>
67
/// This Brush instances and places a randomly selected Prefabs onto the targeted location and parents the instanced object to the paint target. Use this as an example to quickly place an assorted type of GameObjects onto structured locations.
78
/// </summary>
8-
[CreateAssetMenu(fileName = "Prefab Random brush", menuName = "2D Extras/Brushes/Prefab Random brush", order = 359)]
9+
[CreateAssetMenu(fileName = "New Prefab Random Brush", menuName = "2D Extras/Brushes/Prefab Random Brush", order = 359)]
910
[CustomGridBrush(false, true, false, "Prefab Random Brush")]
1011
public class PrefabRandomBrush : BasePrefabBrush
1112
{
1213
private const float k_PerlinOffset = 100000f;
14+
15+
#pragma warning disable 0649
1316
/// <summary>
1417
/// The selection of Prefabs to paint from
1518
/// </summary>
16-
public GameObject[] m_Prefabs;
19+
[SerializeField] GameObject[] m_Prefabs;
20+
1721
/// <summary>
1822
/// Factor for distribution of choice of Prefabs to paint
1923
/// </summary>
20-
public float m_PerlinScale = 0.5f;
24+
[SerializeField] float m_PerlinScale = 0.5f;
25+
#pragma warning restore 0649
26+
2127
/// <summary>
22-
/// Paints Prefabs into a given position within the selected layers.
23-
/// The PrefabBrush overrides this to provide Prefab painting functionality.
28+
/// If true, erases any GameObjects that are in a given position within the selected layers with Erasing.
29+
/// Otherwise, erases only GameObjects that are created from owned Prefab in a given position within the selected layers with Erasing.
30+
/// </summary>
31+
bool m_EraseAnyObjects;
32+
33+
/// <summary>
34+
/// Paints GameObject from containg Prefabs with randomly into a given position within the selected layers.
35+
/// The PrefabRandomBrush overrides this to provide Prefab painting functionality.
2436
/// </summary>
2537
/// <param name="grid">Grid used for layout.</param>
2638
/// <param name="brushTarget">Target of the paint operation. By default the currently selected GameObject.</param>
2739
/// <param name="position">The coordinates of the cell to paint data to.</param>
2840
public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
2941
{
30-
var index = Mathf.Clamp(Mathf.FloorToInt(GetPerlinValue(position, m_PerlinScale, k_PerlinOffset) * m_Prefabs.Length), 0, m_Prefabs.Length - 1);
31-
Prefab = m_Prefabs[index];
32-
base.Paint(grid, brushTarget, position);
42+
// Do not allow editing palettes
43+
if (brushTarget.layer == 31 || brushTarget == null)
44+
{
45+
return;
46+
}
47+
48+
var objectsInCell = GetObjectsInCell(grid, brushTarget.transform, position);
49+
var existPrefabObjectInCell = objectsInCell.Any(objectInCell =>
50+
{
51+
return m_Prefabs.Any(prefab => PrefabUtility.GetCorrespondingObjectFromSource(objectInCell) == prefab);
52+
});
53+
54+
if (!existPrefabObjectInCell)
55+
{
56+
var index = Mathf.Clamp(Mathf.FloorToInt(GetPerlinValue(position, m_PerlinScale, k_PerlinOffset) * m_Prefabs.Length), 0, m_Prefabs.Length - 1);
57+
var prefab = m_Prefabs[index];
58+
base.InstantiatePrefabInCell(grid, brushTarget, position, prefab);
59+
}
3360
}
34-
35-
private static float GetPerlinValue(Vector3Int position, float scale, float offset)
61+
62+
/// <summary>
63+
/// If "Erase Any Objects" is true, erases any GameObjects that are in a given position within the selected layers.
64+
/// If "Erase Any Objects" is false, erases only GameObjects that are created from owned Prefab in a given position within the selected layers.
65+
/// The PrefabRandomBrush overrides this to provide Prefab erasing functionality.
66+
/// </summary>
67+
/// <param name="grid">Grid used for layout.</param>
68+
/// <param name="brushTarget">Target of the erase operation. By default the currently selected GameObject.</param>
69+
/// <param name="position">The coordinates of the cell to erase data from.</param>
70+
public override void Erase(GridLayout grid, GameObject brushTarget, Vector3Int position)
3671
{
37-
return Mathf.PerlinNoise((position.x + offset)*scale, (position.y + offset)*scale);
38-
}
39-
}
72+
if (brushTarget.layer == 31 || brushTarget.transform == null)
73+
{
74+
return;
75+
}
4076

41-
/// <summary>
42-
/// The Brush Editor for a Prefab Brush.
43-
/// </summary>
44-
[CustomEditor(typeof(PrefabRandomBrush))]
45-
public class PrefabRandomBrushEditor : BasePrefabBrushEditor
46-
{
47-
private PrefabRandomBrush prefabBrush { get { return target as PrefabRandomBrush; } }
77+
foreach (var objectInCell in GetObjectsInCell(grid, brushTarget.transform, position))
78+
{
79+
foreach (var prefab in m_Prefabs)
80+
{
81+
if (objectInCell != null && (m_EraseAnyObjects || PrefabUtility.GetCorrespondingObjectFromSource(objectInCell) == prefab))
82+
{
83+
Undo.DestroyObjectImmediate(objectInCell);
84+
}
85+
}
86+
}
87+
}
4888

49-
private SerializedProperty m_Prefabs;
50-
51-
protected override void OnEnable()
89+
private static float GetPerlinValue(Vector3Int position, float scale, float offset)
5290
{
53-
base.OnEnable();
54-
m_Prefabs = m_SerializedObject.FindProperty("m_Prefabs");
91+
return Mathf.PerlinNoise((position.x + offset)*scale, (position.y + offset)*scale);
5592
}
5693

5794
/// <summary>
58-
/// Callback for painting the inspector GUI for the PrefabBrush in the Tile Palette.
59-
/// The PrefabBrush Editor overrides this to have a custom inspector for this Brush.
95+
/// The Brush Editor for a Prefab Brush.
6096
/// </summary>
61-
public override void OnPaintInspectorGUI()
97+
[CustomEditor(typeof(PrefabRandomBrush))]
98+
public class PrefabRandomBrushEditor : BasePrefabBrushEditor
6299
{
63-
base.OnPaintInspectorGUI();
64-
m_SerializedObject.UpdateIfRequiredOrScript();
65-
prefabBrush.m_PerlinScale = EditorGUILayout.Slider("Perlin Scale", prefabBrush.m_PerlinScale, 0.001f, 0.999f);
66-
EditorGUILayout.PropertyField(m_Prefabs, true);
67-
m_SerializedObject.ApplyModifiedPropertiesWithoutUndo();
100+
private PrefabRandomBrush prefabRandomBrush => target as PrefabRandomBrush;
101+
102+
private SerializedProperty m_Prefabs;
103+
104+
protected override void OnEnable()
105+
{
106+
base.OnEnable();
107+
m_Prefabs = m_SerializedObject.FindProperty("m_Prefabs");
108+
}
109+
110+
/// <summary>
111+
/// Callback for painting the inspector GUI for the PrefabBrush in the Tile Palette.
112+
/// The PrefabBrush Editor overrides this to have a custom inspector for this Brush.
113+
/// </summary>
114+
public override void OnPaintInspectorGUI()
115+
{
116+
base.OnPaintInspectorGUI();
117+
m_SerializedObject.UpdateIfRequiredOrScript();
118+
prefabRandomBrush.m_PerlinScale = EditorGUILayout.Slider("Perlin Scale", prefabRandomBrush.m_PerlinScale, 0.001f, 0.999f);
119+
EditorGUILayout.PropertyField(m_Prefabs, true);
120+
prefabRandomBrush.m_EraseAnyObjects = EditorGUILayout.Toggle("Erase Any Objects", prefabRandomBrush.m_EraseAnyObjects);
121+
m_SerializedObject.ApplyModifiedPropertiesWithoutUndo();
122+
}
68123
}
69124
}
70125
}

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ Please use the `2017` branch or the `2017` tag for earlier versions of Unity (fr
4646
- **Coordinate**: This Brush displays the cell coordinates it is targeting in the SceneView. Use this as an example to create brushes which have extra visualization features when painting onto a Tilemap.
4747
- **Line**: This Brush helps draw lines of Tiles onto a Tilemap. The first click of the mouse sets the starting point of the line and the second click sets the ending point of the line and draws the lines of Tiles. Use this as an example to modify brush painting behaviour to making painting quicker with less actions.
4848
- **Random**: This Brush helps to place random Tiles onto a Tilemap. Use this as an example to create brushes which store specific data per brush and to make brushes which randomize behaviour.
49-
- **Prefab**: This Brush instances and places a randomly selected Prefabs onto the targeted location and parents the instanced object to the paint target. Use this as an example to quickly place an assorted type of GameObjects onto structured locations.
49+
- **Prefab**: This Brush instances and places the containing Prefab onto the targeted location and parents the instanced object to the paint target. Use this as an example to quickly place an assorted type of GameObjects onto structured locations.
50+
- **PrefabRandom**: This Brush instances and places a randomly selected Prefabs onto the targeted location and parents the instanced object to the paint target. Use this as an example to quickly place an assorted type of GameObjects onto structured locations.
5051
- **GameObject**: This Brush instances, places and manipulates GameObjects onto the scene. Use this as an example to create brushes which targets objects other than tiles for manipulation.
5152
- **TintBrush**: Brush to edit Tilemap per-cell tint colors.
5253
- **TintBrushSmooth**: Advanced tint brush for interpolated tint color per-cell. Requires the use of custom shader (see TintedTilemap.shader) and helper component TileTextureGenerator.

0 commit comments

Comments
 (0)