Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 8 additions & 34 deletions Editor/Brushes/PrefabBrushes/BasePrefabBrush.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using UnityEngine;
using System.Collections.Generic;
using UnityEngine;

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

/// <summary>
/// GameObject to instantiating
/// </summary>
protected GameObject Prefab = null;

protected static Transform GetObjectInCell(GridLayout grid, Transform parent, Vector3Int position)
protected List<GameObject> GetObjectsInCell(GridLayout grid, Transform parent, Vector3Int position)
{
var results = new List<GameObject>();
var childCount = parent.childCount;
for (var i = 0; i < childCount; i++)
{
var child = parent.GetChild(i);
if (position == grid.WorldToCell(child.position))
{
return child;
results.Add(child.gameObject);
}
}
return null;
return results;
}

public override void Erase(GridLayout grid, GameObject brushTarget, Vector3Int position)
protected void InstantiatePrefabInCell(GridLayout grid, GameObject brushTarget, Vector3Int position, GameObject prefab)
{
if (brushTarget.layer == 31)
{
return;
}

var erased = GetObjectInCell(grid, brushTarget.transform, position);
if (erased != null)
{
Undo.DestroyObjectImmediate(erased.gameObject);
}
}

public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
{
// Do not allow editing palettes
if (brushTarget.layer == 31 || brushTarget == null)
return;

var tileObject = GetObjectInCell(grid, brushTarget.transform, position);
if (tileObject)
{
return;
}
var instance = (GameObject)PrefabUtility.InstantiatePrefab(Prefab);
var instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
if (instance != null)
{
Undo.MoveGameObjectToScene(instance, brushTarget.scene, "Paint Prefabs");
Expand Down
100 changes: 56 additions & 44 deletions Editor/Brushes/PrefabBrushes/PrefabBrush/PrefabBrush.cs
Original file line number Diff line number Diff line change
@@ -1,91 +1,103 @@
using System.Linq;
using UnityEngine;

namespace UnityEditor.Tilemaps
{
/// <summary>
/// This Brush instances and places a selected prefab onto the targeted location and parents the instanced object to the paint target.
/// This Brush instances and places a containing prefab onto the targeted location and parents the instanced object to the paint target.
/// </summary>
[CreateAssetMenu(fileName = "Prefab brush", menuName = "2D Extras/Brushes/Prefab brush", order = 359)]
[CreateAssetMenu(fileName = "New Prefab Brush", menuName = "2D Extras/Brushes/Prefab Brush", order = 359)]
[CustomGridBrush(false, true, false, "Prefab Brush")]
public class PrefabBrush : BasePrefabBrush
{
#pragma warning disable 0649
/// <summary>
/// The selection of Prefab to paint from
/// </summary>
public GameObject m_Prefab;
[SerializeField] GameObject m_Prefab;
#pragma warning restore 0649

/// <summary>
/// Use to remove all prefabs in the cell or just the one that is currently selected in m_Prefab
/// If true, erases any GameObjects that are in a given position within the selected layers with Erasing.
/// Otherwise, erases only GameObjects that are created from owned Prefab in a given position within the selected layers with Erasing.
/// </summary>
public bool m_ForceDelete;
bool m_EraseAnyObjects;

/// <summary>
/// Paints Prefabs into a given position within the selected layers.
/// Paints GameObject from containg Prefab into a given position within the selected layers.
/// The PrefabBrush overrides this to provide Prefab painting functionality.
/// </summary>
/// <param name="grid">Grid used for layout.</param>
/// <param name="brushTarget">Target of the paint operation. By default the currently selected GameObject.</param>
/// <param name="position">The coordinates of the cell to paint data to.</param>

public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
{


Prefab = m_Prefab;
var tileObject = GetObjectInCell(grid, brushTarget.transform, position);
if (tileObject == null || tileObject.name != m_Prefab.name)
// Do not allow editing palettes
if (brushTarget.layer == 31 || brushTarget == null)
{
base.Paint(grid, brushTarget, position);
return;
}

var objectsInCell = GetObjectsInCell(grid, brushTarget.transform, position);
var existPrefabObjectInCell = objectsInCell.Any(objectInCell => PrefabUtility.GetCorrespondingObjectFromSource(objectInCell) == m_Prefab);

if (!existPrefabObjectInCell)
{
base.InstantiatePrefabInCell(grid, brushTarget, position, m_Prefab);
}
}

/// <summary>
/// Erases all Prefabs in a given position within the selected layers if ForceDelete is true.
/// Erase only selected Prefabs in a given position within the selected layers if ForceDelete is false.
/// If "Erase Any Objects" is true, erases any GameObjects that are in a given position within the selected layers.
/// If "Erase Any Objects" is false, erases only GameObjects that are created from owned Prefab in a given position within the selected layers.
/// The PrefabBrush overrides this to provide Prefab erasing functionality.
/// </summary>
/// <param name="grid">Grid used for layout.</param>
/// <param name="brushTarget">Target of the erase operation. By default the currently selected GameObject.</param>
/// <param name="position">The coordinates of the cell to erase data from.</param>
public override void Erase(GridLayout grid, GameObject brushTarget, Vector3Int position)
{
var erased = GetObjectInCell(grid, brushTarget.transform, position);
if (erased == null)
if (brushTarget.layer == 31 || brushTarget.transform == null)
{
return;
}
if (m_ForceDelete || (!m_ForceDelete && erased.gameObject.name == m_Prefab.name))

foreach (var objectInCell in GetObjectsInCell(grid, brushTarget.transform, position))
{
base.Erase(grid, brushTarget, position);
if (m_EraseAnyObjects || PrefabUtility.GetCorrespondingObjectFromSource(objectInCell) == m_Prefab)
{
Undo.DestroyObjectImmediate(objectInCell);
}
}
}
}

/// <summary>
/// The Brush Editor for a Prefab Brush.
/// </summary>
[CustomEditor(typeof(PrefabBrush))]
public class PrefabBrushEditor : BasePrefabBrushEditor
{
private SerializedProperty m_Prefab;
private SerializedProperty m_ForceDelete;

protected override void OnEnable()
{
base.OnEnable();
m_Prefab = m_SerializedObject.FindProperty("m_Prefab");
m_ForceDelete = m_SerializedObject.FindProperty("m_ForceDelete");
}

/// <summary>
/// Callback for painting the inspector GUI for the PrefabBrush in the Tile Palette.
/// The PrefabBrush Editor overrides this to have a custom inspector for this Brush.
/// The Brush Editor for a Prefab Brush.
/// </summary>
public override void OnPaintInspectorGUI()
[CustomEditor(typeof(PrefabBrush))]
public class PrefabBrushEditor : BasePrefabBrushEditor
{
base.OnPaintInspectorGUI();
m_SerializedObject.UpdateIfRequiredOrScript();
EditorGUILayout.PropertyField(m_Prefab, true);
EditorGUILayout.PropertyField(m_ForceDelete, true);
m_SerializedObject.ApplyModifiedPropertiesWithoutUndo();
private PrefabBrush prefabBrush => target as PrefabBrush;
private SerializedProperty m_Prefab;

protected override void OnEnable()
{
base.OnEnable();
m_Prefab = m_SerializedObject.FindProperty(nameof(m_Prefab));
}

/// <summary>
/// Callback for painting the inspector GUI for the PrefabBrush in the Tile Palette.
/// The PrefabBrush Editor overrides this to have a custom inspector for this Brush.
/// </summary>
public override void OnPaintInspectorGUI()
{
base.OnPaintInspectorGUI();
m_SerializedObject.UpdateIfRequiredOrScript();
EditorGUILayout.PropertyField(m_Prefab, true);
prefabBrush.m_EraseAnyObjects = EditorGUILayout.Toggle("Erase Any Objects", prefabBrush.m_EraseAnyObjects);
m_SerializedObject.ApplyModifiedPropertiesWithoutUndo();
}
}
}
}
121 changes: 88 additions & 33 deletions Editor/Brushes/PrefabBrushes/PrefabRandomBrush/PrefabRandomBrush.cs
Original file line number Diff line number Diff line change
@@ -1,70 +1,125 @@
using System.Linq;
using UnityEngine;

namespace UnityEditor.Tilemaps
{
/// <summary>
/// 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.
/// </summary>
[CreateAssetMenu(fileName = "Prefab Random brush", menuName = "2D Extras/Brushes/Prefab Random brush", order = 359)]
[CreateAssetMenu(fileName = "New Prefab Random Brush", menuName = "2D Extras/Brushes/Prefab Random Brush", order = 359)]
[CustomGridBrush(false, true, false, "Prefab Random Brush")]
public class PrefabRandomBrush : BasePrefabBrush
{
private const float k_PerlinOffset = 100000f;

#pragma warning disable 0649
/// <summary>
/// The selection of Prefabs to paint from
/// </summary>
public GameObject[] m_Prefabs;
[SerializeField] GameObject[] m_Prefabs;

/// <summary>
/// Factor for distribution of choice of Prefabs to paint
/// </summary>
public float m_PerlinScale = 0.5f;
[SerializeField] float m_PerlinScale = 0.5f;
#pragma warning restore 0649

/// <summary>
/// Paints Prefabs into a given position within the selected layers.
/// The PrefabBrush overrides this to provide Prefab painting functionality.
/// If true, erases any GameObjects that are in a given position within the selected layers with Erasing.
/// Otherwise, erases only GameObjects that are created from owned Prefab in a given position within the selected layers with Erasing.
/// </summary>
bool m_EraseAnyObjects;

/// <summary>
/// Paints GameObject from containg Prefabs with randomly into a given position within the selected layers.
/// The PrefabRandomBrush overrides this to provide Prefab painting functionality.
/// </summary>
/// <param name="grid">Grid used for layout.</param>
/// <param name="brushTarget">Target of the paint operation. By default the currently selected GameObject.</param>
/// <param name="position">The coordinates of the cell to paint data to.</param>
public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
{
var index = Mathf.Clamp(Mathf.FloorToInt(GetPerlinValue(position, m_PerlinScale, k_PerlinOffset) * m_Prefabs.Length), 0, m_Prefabs.Length - 1);
Prefab = m_Prefabs[index];
base.Paint(grid, brushTarget, position);
// Do not allow editing palettes
if (brushTarget.layer == 31 || brushTarget == null)
{
return;
}

var objectsInCell = GetObjectsInCell(grid, brushTarget.transform, position);
var existPrefabObjectInCell = objectsInCell.Any(objectInCell =>
{
return m_Prefabs.Any(prefab => PrefabUtility.GetCorrespondingObjectFromSource(objectInCell) == prefab);
});

if (!existPrefabObjectInCell)
{
var index = Mathf.Clamp(Mathf.FloorToInt(GetPerlinValue(position, m_PerlinScale, k_PerlinOffset) * m_Prefabs.Length), 0, m_Prefabs.Length - 1);
var prefab = m_Prefabs[index];
base.InstantiatePrefabInCell(grid, brushTarget, position, prefab);
}
}

private static float GetPerlinValue(Vector3Int position, float scale, float offset)

/// <summary>
/// If "Erase Any Objects" is true, erases any GameObjects that are in a given position within the selected layers.
/// If "Erase Any Objects" is false, erases only GameObjects that are created from owned Prefab in a given position within the selected layers.
/// The PrefabRandomBrush overrides this to provide Prefab erasing functionality.
/// </summary>
/// <param name="grid">Grid used for layout.</param>
/// <param name="brushTarget">Target of the erase operation. By default the currently selected GameObject.</param>
/// <param name="position">The coordinates of the cell to erase data from.</param>
public override void Erase(GridLayout grid, GameObject brushTarget, Vector3Int position)
{
return Mathf.PerlinNoise((position.x + offset)*scale, (position.y + offset)*scale);
}
}
if (brushTarget.layer == 31 || brushTarget.transform == null)
{
return;
}

/// <summary>
/// The Brush Editor for a Prefab Brush.
/// </summary>
[CustomEditor(typeof(PrefabRandomBrush))]
public class PrefabRandomBrushEditor : BasePrefabBrushEditor
{
private PrefabRandomBrush prefabBrush { get { return target as PrefabRandomBrush; } }
foreach (var objectInCell in GetObjectsInCell(grid, brushTarget.transform, position))
{
foreach (var prefab in m_Prefabs)
{
if (objectInCell != null && (m_EraseAnyObjects || PrefabUtility.GetCorrespondingObjectFromSource(objectInCell) == prefab))
{
Undo.DestroyObjectImmediate(objectInCell);
}
}
}
}

private SerializedProperty m_Prefabs;

protected override void OnEnable()
private static float GetPerlinValue(Vector3Int position, float scale, float offset)
{
base.OnEnable();
m_Prefabs = m_SerializedObject.FindProperty("m_Prefabs");
return Mathf.PerlinNoise((position.x + offset)*scale, (position.y + offset)*scale);
}

/// <summary>
/// Callback for painting the inspector GUI for the PrefabBrush in the Tile Palette.
/// The PrefabBrush Editor overrides this to have a custom inspector for this Brush.
/// The Brush Editor for a Prefab Brush.
/// </summary>
public override void OnPaintInspectorGUI()
[CustomEditor(typeof(PrefabRandomBrush))]
public class PrefabRandomBrushEditor : BasePrefabBrushEditor
{
base.OnPaintInspectorGUI();
m_SerializedObject.UpdateIfRequiredOrScript();
prefabBrush.m_PerlinScale = EditorGUILayout.Slider("Perlin Scale", prefabBrush.m_PerlinScale, 0.001f, 0.999f);
EditorGUILayout.PropertyField(m_Prefabs, true);
m_SerializedObject.ApplyModifiedPropertiesWithoutUndo();
private PrefabRandomBrush prefabRandomBrush => target as PrefabRandomBrush;

private SerializedProperty m_Prefabs;

protected override void OnEnable()
{
base.OnEnable();
m_Prefabs = m_SerializedObject.FindProperty("m_Prefabs");
}

/// <summary>
/// Callback for painting the inspector GUI for the PrefabBrush in the Tile Palette.
/// The PrefabBrush Editor overrides this to have a custom inspector for this Brush.
/// </summary>
public override void OnPaintInspectorGUI()
{
base.OnPaintInspectorGUI();
m_SerializedObject.UpdateIfRequiredOrScript();
prefabRandomBrush.m_PerlinScale = EditorGUILayout.Slider("Perlin Scale", prefabRandomBrush.m_PerlinScale, 0.001f, 0.999f);
EditorGUILayout.PropertyField(m_Prefabs, true);
prefabRandomBrush.m_EraseAnyObjects = EditorGUILayout.Toggle("Erase Any Objects", prefabRandomBrush.m_EraseAnyObjects);
m_SerializedObject.ApplyModifiedPropertiesWithoutUndo();
}
}
}
}
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ Please use the `2017` branch or the `2017` tag for earlier versions of Unity (fr
- **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.
- **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.
- **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.
- **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.
- **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.
- **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.
- **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.
- **TintBrush**: Brush to edit Tilemap per-cell tint colors.
- **TintBrushSmooth**: Advanced tint brush for interpolated tint color per-cell. Requires the use of custom shader (see TintedTilemap.shader) and helper component TileTextureGenerator.
Expand Down