using System; using System.Linq; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; using Object = UnityEngine.Object; namespace UnityEditor.Tilemaps { /// /// 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. /// [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@latest/index.html?subfolder=/manual/GameObjectBrush.html")] [CustomGridBrush(true, false, false, "GameObject Brush")] public class GameObjectBrush : GridBrushBase { [Serializable] internal class HiddenGridLayout { public Vector3 cellSize = Vector3.one; public Vector3 cellGap = Vector3.zero; public GridLayout.CellLayout cellLayout = GridLayout.CellLayout.Rectangle; public GridLayout.CellSwizzle cellSwizzle = GridLayout.CellSwizzle.XYZ; } [SerializeField] private BrushCell[] m_Cells; [SerializeField] private Vector3Int m_Size; [SerializeField] private Vector3Int m_Pivot; [SerializeField] [HideInInspector] private bool m_CanChangeZPosition; [SerializeField] [HideInInspector] internal HiddenGridLayout hiddenGridLayout = new HiddenGridLayout(); /// /// GameObject used for painting onto the Scene root /// [HideInInspector] public GameObject hiddenGrid; /// /// Anchor Point of the Instantiated GameObject in the cell when painting /// public Vector3 m_Anchor = new Vector3(0.5f, 0.5f, 0.0f); /// Size of the brush in cells. public Vector3Int size { get { return m_Size; } set { m_Size = value; SizeUpdated(); } } /// Pivot of the brush. public Vector3Int pivot { get { return m_Pivot; } set { m_Pivot = value; } } /// All the brush cells the brush holds. public BrushCell[] cells { get { return m_Cells; } } /// Number of brush cells in the brush. public int cellCount { get { return m_Cells != null ? m_Cells.Length : 0; } } /// Number of brush cells based on size. public int sizeCount { get { return m_Size.x * m_Size.y * m_Size.z; } } /// Whether the brush can change Z Position public bool canChangeZPosition { get { return m_CanChangeZPosition; } set { m_CanChangeZPosition = value; } } /// /// This Brush instances, places and manipulates GameObjects onto the scene. /// public GameObjectBrush() { Init(Vector3Int.one, Vector3Int.zero); SizeUpdated(); } private void OnEnable() { hiddenGrid = new GameObject(); hiddenGrid.name = "(Paint on SceneRoot)"; hiddenGrid.hideFlags = HideFlags.HideAndDontSave; hiddenGrid.transform.position = Vector3.zero; var grid = hiddenGrid.AddComponent(); grid.cellSize = hiddenGridLayout.cellSize; grid.cellGap = hiddenGridLayout.cellGap; grid.cellSwizzle = hiddenGridLayout.cellSwizzle; grid.cellLayout = hiddenGridLayout.cellLayout; } private void OnDisable() { DestroyImmediate(hiddenGrid); } /// /// Initializes the content of the GameObjectBrush. /// /// Size of the GameObjectBrush. public void Init(Vector3Int size) { Init(size, Vector3Int.zero); SizeUpdated(); } /// Initializes the content of the GameObjectBrush. /// Size of the GameObjectBrush. /// Pivot point of the GameObjectBrush. public void Init(Vector3Int size, Vector3Int pivot) { m_Size = size; m_Pivot = pivot; SizeUpdated(); } /// /// Paints GameObjects into a given position within the selected layers. /// The GameObjectBrush overrides this to provide GameObject painting functionality. /// /// Grid used for layout. /// Target of the paint operation. By default the currently selected GameObject. /// The coordinates of the cell to paint data to. public override void Paint(GridLayout gridLayout, GameObject brushTarget, Vector3Int position) { Vector3Int min = position - pivot; BoundsInt bounds = new BoundsInt(min, m_Size); BoxFill(gridLayout, brushTarget, bounds); } private void PaintCell(GridLayout grid, Vector3Int position, Transform parent, BrushCell cell) { if (cell.gameObject == null) return; var existingGO = GetObjectInCell(grid, parent, position, m_Anchor); if (existingGO == null) { SetSceneCell(grid, parent, position, cell.gameObject, cell.offset, cell.scale, cell.orientation, m_Anchor); } } /// /// Erases GameObjects in a given position within the selected layers. /// The GameObjectBrush overrides this to provide GameObject erasing functionality. /// /// Grid used for layout. /// Target of the erase operation. By default the currently selected GameObject. /// The coordinates of the cell to erase data from. public override void Erase(GridLayout gridLayout, GameObject brushTarget, Vector3Int position) { Vector3Int min = position - pivot; BoundsInt bounds = new BoundsInt(min, m_Size); GetGrid(ref gridLayout, ref brushTarget); BoxErase(gridLayout, brushTarget, bounds); } private void EraseCell(GridLayout grid, Vector3Int position, Transform parent) { ClearSceneCell(grid, parent, position); } /// /// Box fills GameObjects into given bounds within the selected layers. /// The GameObjectBrush overrides this to provide GameObject box-filling functionality. /// /// Grid to box fill data to. /// Target of the box fill operation. By default the currently selected GameObject. /// The bounds to box fill data into. public override void BoxFill(GridLayout gridLayout, GameObject brushTarget, BoundsInt position) { GetGrid(ref gridLayout, ref brushTarget); foreach (Vector3Int location in position.allPositionsWithin) { Vector3Int local = location - position.min; BrushCell cell = m_Cells[GetCellIndexWrapAround(local.x, local.y, local.z)]; PaintCell(gridLayout, location, brushTarget != null ? brushTarget.transform : null, cell); } } /// /// Erases GameObjects from given bounds within the selected layers. /// The GameObjectBrush overrides this to provide GameObject box-erasing functionality. /// /// Grid to erase data from. /// Target of the erase operation. By default the currently selected GameObject. /// The bounds to erase data from. public override void BoxErase(GridLayout gridLayout, GameObject brushTarget, BoundsInt position) { GetGrid(ref gridLayout, ref brushTarget); foreach (Vector3Int location in position.allPositionsWithin) { EraseCell(gridLayout, location, brushTarget != null ? brushTarget.transform : null); } } /// /// This is not supported but it should floodfill GameObjects starting from a given position within the selected layers. /// /// Grid used for layout. /// Target of the flood fill operation. By default the currently selected GameObject. /// Starting position of the flood fill. public override void FloodFill(GridLayout gridLayout, GameObject brushTarget, Vector3Int position) { Debug.LogWarning("FloodFill not supported"); } /// /// Rotates the brush by 90 degrees in the given direction. /// /// Direction to rotate by. /// Cell Layout for rotating. public override void Rotate(RotationDirection direction, GridLayout.CellLayout layout) { Vector3Int oldSize = m_Size; BrushCell[] oldCells = m_Cells.Clone() as BrushCell[]; size = new Vector3Int(oldSize.y, oldSize.x, oldSize.z); BoundsInt oldBounds = new BoundsInt(Vector3Int.zero, oldSize); foreach (Vector3Int oldPos in oldBounds.allPositionsWithin) { int newX = direction == RotationDirection.Clockwise ? oldSize.y - oldPos.y - 1 : oldPos.y; int newY = direction == RotationDirection.Clockwise ? oldPos.x : oldSize.x - oldPos.x - 1; int toIndex = GetCellIndex(newX, newY, oldPos.z); int fromIndex = GetCellIndex(oldPos.x, oldPos.y, oldPos.z, oldSize.x, oldSize.y, oldSize.z); m_Cells[toIndex] = oldCells[fromIndex]; } int newPivotX = direction == RotationDirection.Clockwise ? oldSize.y - pivot.y - 1 : pivot.y; int newPivotY = direction == RotationDirection.Clockwise ? pivot.x : oldSize.x - pivot.x - 1; pivot = new Vector3Int(newPivotX, newPivotY, pivot.z); Quaternion orientation = Quaternion.Euler(0f, 0f, direction != RotationDirection.Clockwise ? 90f : -90f); foreach (BrushCell cell in m_Cells) cell.orientation = cell.orientation * orientation; } /// Flips the brush in the given axis. /// Axis to flip by. /// Cell Layout for flipping. public override void Flip(FlipAxis flip, GridLayout.CellLayout layout) { if (flip == FlipAxis.X) FlipX(); else FlipY(); } /// /// Picks child GameObjects given the coordinates of the cells. /// The GameObjectBrush overrides this to provide GameObject picking functionality. /// /// Grid to pick data from. /// Target of the picking operation. By default the currently selected GameObject. /// The coordinates of the cells to paint data from. /// Pivot of the picking brush. public override void Pick(GridLayout gridLayout, GameObject brushTarget, BoundsInt position, Vector3Int pivot) { Reset(); UpdateSizeAndPivot(new Vector3Int(position.size.x, position.size.y, 1), new Vector3Int(pivot.x, pivot.y, 0)); GetGrid(ref gridLayout, ref brushTarget); foreach (Vector3Int pos in position.allPositionsWithin) { Vector3Int brushPosition = new Vector3Int(pos.x - position.x, pos.y - position.y, 0); PickCell(pos, brushPosition, gridLayout, brushTarget != null ? brushTarget.transform : null, true); } } private void PickCell(Vector3Int position, Vector3Int brushPosition, GridLayout grid, Transform parent, bool withoutAnchor = false) { var go = GetObjectInCell(grid, parent, position, m_Anchor); if (go == null) { go = GetObjectInCell(grid, parent, position, Vector3.zero); } var anchorRatio = GetAnchorRatio(grid, m_Anchor); var cellCenter = grid.LocalToWorld(grid.CellToLocalInterpolated(position) + grid.CellToLocalInterpolated(anchorRatio)); if (go != null) { Object prefab = PrefabUtility.GetCorrespondingObjectFromSource(go); if (prefab) { SetGameObject(brushPosition, (GameObject) prefab); } else { GameObject newInstance = Instantiate(go); newInstance.hideFlags = HideFlags.HideAndDontSave; newInstance.SetActive(false); SetGameObject(brushPosition, newInstance); } SetOffset(brushPosition, go.transform.position - cellCenter); SetScale(brushPosition, go.transform.localScale); SetOrientation(brushPosition, go.transform.localRotation); } } /// /// MoveStart is called when user starts moving the area previously selected with the selection marquee. /// The GameObjectBrush overrides this to provide GameObject moving functionality. /// /// Grid used for layout. /// Target of the move operation. By default the currently selected GameObject. /// Position where the move operation has started. public override void MoveStart(GridLayout gridLayout, GameObject brushTarget, BoundsInt position) { Reset(); UpdateSizeAndPivot(new Vector3Int(position.size.x, position.size.y, 1), Vector3Int.zero); GetGrid(ref gridLayout, ref brushTarget); var targetTransform = brushTarget != null ? brushTarget.transform : null; foreach (Vector3Int pos in position.allPositionsWithin) { Vector3Int brushPosition = new Vector3Int(pos.x - position.x, pos.y - position.y, 0); PickCell(pos, brushPosition, gridLayout, targetTransform); ClearSceneCell(gridLayout, targetTransform, pos); } } /// /// MoveEnd is called when user has ended the move of the area previously selected with the selection marquee. /// The GameObjectBrush overrides this to provide GameObject moving functionality. /// /// Grid used for layout. /// Target of the move operation. By default the currently selected GameObject. /// Position where the move operation has ended. public override void MoveEnd(GridLayout gridLayout, GameObject brushTarget, BoundsInt position) { GetGrid(ref gridLayout, ref brushTarget); Paint(gridLayout, brushTarget, position.min); Reset(); } private void GetGrid(ref GridLayout gridLayout, ref GameObject brushTarget) { if (brushTarget == hiddenGrid) brushTarget = null; if (brushTarget != null) { var targetGridLayout = brushTarget.GetComponent(); if (targetGridLayout != null) gridLayout = targetGridLayout; } } /// Clears all data of the brush. public void Reset() { foreach (var cell in m_Cells) { if (cell.gameObject != null && !EditorUtility.IsPersistent(cell.gameObject)) { DestroyImmediate(cell.gameObject); } cell.gameObject = null; } UpdateSizeAndPivot(Vector3Int.one, Vector3Int.zero); } private void FlipX() { BrushCell[] oldCells = m_Cells.Clone() as BrushCell[]; BoundsInt oldBounds = new BoundsInt(Vector3Int.zero, m_Size); foreach (Vector3Int oldPos in oldBounds.allPositionsWithin) { var newX = m_Size.x - oldPos.x - 1; var toIndex = GetCellIndex(newX, oldPos.y, oldPos.z); var fromIndex = GetCellIndex(oldPos); m_Cells[toIndex] = oldCells[fromIndex]; } var newPivotX = m_Size.x - pivot.x - 1; pivot = new Vector3Int(newPivotX, pivot.y, pivot.z); FlipCells(ref m_Cells, new Vector3(-1f, 1f, 1f)); } private void FlipY() { BrushCell[] oldCells = m_Cells.Clone() as BrushCell[]; BoundsInt oldBounds = new BoundsInt(Vector3Int.zero, m_Size); foreach (Vector3Int oldPos in oldBounds.allPositionsWithin) { var newY = m_Size.y - oldPos.y - 1; var toIndex = GetCellIndex(oldPos.x, newY, oldPos.z); var fromIndex = GetCellIndex(oldPos); m_Cells[toIndex] = oldCells[fromIndex]; } var newPivotY = m_Size.y - pivot.y - 1; pivot = new Vector3Int(pivot.x, newPivotY, pivot.z); FlipCells(ref m_Cells, new Vector3(1f, -1f, 1f)); } private static void FlipCells(ref BrushCell[] cells, Vector3 scale) { foreach (BrushCell cell in cells) { cell.scale = Vector3.Scale(cell.scale, scale); } } /// Updates the size, pivot and the number of layers of the brush. /// New size of the brush. /// New pivot of the brush. public void UpdateSizeAndPivot(Vector3Int size, Vector3Int pivot) { m_Size = size; m_Pivot = pivot; SizeUpdated(); } /// /// Sets a GameObject at the position in the brush. /// /// Position to set the GameObject in the brush. /// GameObject to set in the brush. public void SetGameObject(Vector3Int position, GameObject go) { if (ValidateCellPosition(position)) m_Cells[GetCellIndex(position)].gameObject = go; } /// /// Sets a position offset at the position in the brush. /// /// Position to set the offset in the brush. /// Offset to set in the brush. public void SetOffset(Vector3Int position, Vector3 offset) { if (ValidateCellPosition(position)) m_Cells[GetCellIndex(position)].offset = offset; } /// /// Sets an orientation at the position in the brush. /// /// Position to set the orientation in the brush. /// Orientation to set in the brush. public void SetOrientation(Vector3Int position, Quaternion orientation) { if (ValidateCellPosition(position)) m_Cells[GetCellIndex(position)].orientation = orientation; } /// /// Sets a scale at the position in the brush. /// /// Position to set the scale in the brush. /// Scale to set in the brush. public void SetScale(Vector3Int position, Vector3 scale) { if (ValidateCellPosition(position)) m_Cells[GetCellIndex(position)].scale = scale; } /// Gets the index to the GameObjectBrush::ref::BrushCell based on the position of the BrushCell. /// Position of the BrushCell. /// The cell index for the position of the BrushCell. public int GetCellIndex(Vector3Int brushPosition) { return GetCellIndex(brushPosition.x, brushPosition.y, brushPosition.z); } /// Gets the index to the GameObjectBrush::ref::BrushCell based on the position of the BrushCell. /// X Position of the BrushCell. /// Y Position of the BrushCell. /// Z Position of the BrushCell. /// The cell index for the position of the BrushCell. public int GetCellIndex(int x, int y, int z) { return x + m_Size.x * y + m_Size.x * m_Size.y * z; } /// Gets the index to the GameObjectBrush::ref::BrushCell based on the position of the BrushCell. /// X Position of the BrushCell. /// Y Position of the BrushCell. /// Z Position of the BrushCell. /// X Size of Brush. /// Y Size of Brush. /// Z Size of Brush. /// The cell index for the position of the BrushCell. public int GetCellIndex(int x, int y, int z, int sizex, int sizey, int sizez) { return x + sizex * y + sizex * sizey * z; } /// Gets the index to the GameObjectBrush::ref::BrushCell based on the position of the BrushCell. Wraps each coordinate if it is larger than the size of the GameObjectBrush. /// X Position of the BrushCell. /// Y Position of the BrushCell. /// Z Position of the BrushCell. /// The cell index for the position of the BrushCell. public int GetCellIndexWrapAround(int x, int y, int z) { return (x % m_Size.x) + m_Size.x * (y % m_Size.y) + m_Size.x * m_Size.y * (z % m_Size.z); } private GameObject GetObjectInCell(GridLayout grid, Transform parent, Vector3Int position, Vector3 anchor) { int childCount; GameObject[] sceneChildren = null; if (parent == null) { var scene = SceneManager.GetActiveScene(); sceneChildren = scene.GetRootGameObjects(); childCount = scene.rootCount; } else { childCount = parent.childCount; } var anchorRatio = GetAnchorRatio(grid, anchor); var anchorWorld = grid.CellToLocalInterpolated(anchorRatio); for (var i = 0; i < childCount; i++) { var child = sceneChildren == null ? parent.GetChild(i) : sceneChildren[i].transform; var childCell = grid.LocalToCell(grid.WorldToLocal(child.position) - anchorWorld); if (position == childCell) return child.gameObject; } return null; } private bool ValidateCellPosition(Vector3Int position) { var valid = position.x >= 0 && position.x < size.x && position.y >= 0 && position.y < size.y && position.z >= 0 && position.z < size.z; if (!valid) throw new ArgumentException(string.Format("Position {0} is an invalid cell position. Valid range is between [{1}, {2}).", position, Vector3Int.zero, size)); return true; } internal void SizeUpdated(bool keepContents = false) { Array.Resize(ref m_Cells, sizeCount); BoundsInt bounds = new BoundsInt(Vector3Int.zero, m_Size); foreach (Vector3Int pos in bounds.allPositionsWithin) { if (keepContents || m_Cells[GetCellIndex(pos)] == null) m_Cells[GetCellIndex(pos)] = new BrushCell(); } } private static void SetSceneCell(GridLayout grid, Transform parent, Vector3Int position, GameObject go, Vector3 offset, Vector3 scale, Quaternion orientation, Vector3 anchor) { if (go == null) return; GameObject instance; if (PrefabUtility.IsPartOfPrefabAsset(go)) { instance = (GameObject) PrefabUtility.InstantiatePrefab(go, parent != null ? parent.root.gameObject.scene : SceneManager.GetActiveScene()); instance.transform.parent = parent; } else { instance = Instantiate(go, parent); instance.name = go.name; instance.SetActive(true); foreach (var renderer in instance.GetComponentsInChildren()) { renderer.enabled = true; } } Undo.RegisterCreatedObjectUndo(instance, "Paint GameObject"); var anchorRatio = GetAnchorRatio(grid, anchor); instance.transform.position = grid.LocalToWorld(grid.CellToLocalInterpolated(position) + grid.CellToLocalInterpolated(anchorRatio)); instance.transform.localRotation = orientation; instance.transform.localScale = scale; instance.transform.Translate(offset); } private static Vector3 GetAnchorRatio(GridLayout grid, Vector3 cellAnchor) { var cellSize = grid.cellSize; var cellStride = cellSize + grid.cellGap; cellStride.x = Mathf.Approximately(0f, cellStride.x) ? 1f : cellStride.x; cellStride.y = Mathf.Approximately(0f, cellStride.y) ? 1f : cellStride.y; cellStride.z = Mathf.Approximately(0f, cellStride.z) ? 1f : cellStride.z; var anchorRatio = new Vector3( cellAnchor.x * cellSize.x / cellStride.x, cellAnchor.y * cellSize.y / cellStride.y, cellAnchor.z * cellSize.z / cellStride.z ); return anchorRatio; } private void ClearSceneCell(GridLayout grid, Transform parent, Vector3Int position) { var erased = GetObjectInCell(grid, parent, position, m_Anchor); if (erased != null) Undo.DestroyObjectImmediate(erased); } /// /// Hashes the contents of the brush. /// /// A hash code of the brush public override int GetHashCode() { int hash = 0; unchecked { foreach (var cell in cells) { hash = hash * 33 + cell.GetHashCode(); } } return hash; } internal void UpdateHiddenGridLayout() { var grid = hiddenGrid.GetComponent(); hiddenGridLayout.cellSize = grid.cellSize; hiddenGridLayout.cellGap = grid.cellGap; hiddenGridLayout.cellSwizzle = grid.cellSwizzle; hiddenGridLayout.cellLayout = grid.cellLayout; } /// ///Brush Cell stores the data to be painted in a grid cell. /// [Serializable] public class BrushCell { /// /// GameObject to be placed when painting. /// public GameObject gameObject { get { return m_GameObject; } set { m_GameObject = value; } } /// /// Position offset of the GameObject when painted. /// public Vector3 offset { get { return m_Offset; } set { m_Offset = value; } } /// /// Scale of the GameObject when painted. /// public Vector3 scale { get { return m_Scale; } set { m_Scale = value; } } /// /// Orientatio of the GameObject when painted. /// public Quaternion orientation { get { return m_Orientation; } set { m_Orientation = value; } } [SerializeField] private GameObject m_GameObject; [SerializeField] Vector3 m_Offset = Vector3.zero; [SerializeField] Vector3 m_Scale = Vector3.one; [SerializeField] Quaternion m_Orientation = Quaternion.identity; /// /// Hashes the contents of the brush cell. /// /// A hash code of the brush cell. public override int GetHashCode() { int hash; unchecked { hash = gameObject != null ? gameObject.GetInstanceID() : 0; hash = hash * 33 + offset.GetHashCode(); hash = hash * 33 + scale.GetHashCode(); hash = hash * 33 + orientation.GetHashCode(); } return hash; } } } /// /// The Brush Editor for a GameObject Brush. /// [CustomEditor(typeof(GameObjectBrush))] public class GameObjectBrushEditor : GridBrushEditorBase { private bool hiddenGridFoldout; private Editor hiddenGridEditor; /// /// The GameObjectBrush for this Editor /// public GameObjectBrush brush { get { return target as GameObjectBrush; } } /// Whether the GridBrush can change Z Position. public override bool canChangeZPosition { get { return brush.canChangeZPosition; } set { brush.canChangeZPosition = value; } } /// /// Callback for painting the GUI for the GridBrush in the Scene View. /// The GameObjectBrush Editor overrides this to draw the preview of the brush when drawing lines. /// /// Grid that the brush is being used on. /// Target of the GameObjectBrush::ref::Tool operation. By default the currently selected GameObject. /// Current selected location of the brush. /// Current GameObjectBrush::ref::Tool selected. /// Whether brush is being used. public override void OnPaintSceneGUI(GridLayout gridLayout, GameObject brushTarget, BoundsInt position, GridBrushBase.Tool tool, bool executing) { BoundsInt gizmoRect = position; if (tool == GridBrushBase.Tool.Paint || tool == GridBrushBase.Tool.Erase) gizmoRect = new BoundsInt(position.min - brush.pivot, brush.size); base.OnPaintSceneGUI(gridLayout, brushTarget, gizmoRect, tool, executing); } /// /// Callback for painting the inspector GUI for the GameObjectBrush in the tilemap palette. /// The GameObjectBrush Editor overrides this to show the usage of this Brush. /// public override void OnPaintInspectorGUI() { EditorGUI.BeginChangeCheck(); base.OnInspectorGUI(); if (EditorGUI.EndChangeCheck() && brush.cellCount != brush.sizeCount) { brush.SizeUpdated(true); } hiddenGridFoldout = EditorGUILayout.Foldout(hiddenGridFoldout, "SceneRoot Grid"); if (hiddenGridFoldout) { EditorGUI.indentLevel++; using (new EditorGUI.DisabledScope(GridPaintingState.scenePaintTarget != brush.hiddenGrid)) { if (hiddenGridEditor == null) { hiddenGridEditor = Editor.CreateEditor(brush.hiddenGrid.GetComponent()); } brush.hiddenGrid.hideFlags = HideFlags.None; EditorGUI.BeginChangeCheck(); hiddenGridEditor.OnInspectorGUI(); if (EditorGUI.EndChangeCheck()) { brush.UpdateHiddenGridLayout(); EditorUtility.SetDirty(brush); SceneView.RepaintAll(); } brush.hiddenGrid.hideFlags = HideFlags.HideAndDontSave; } EditorGUI.indentLevel--; } } /// /// The targets that the GameObjectBrush can paint on /// public override GameObject[] validTargets { get { StageHandle currentStageHandle = StageUtility.GetCurrentStageHandle(); var results = currentStageHandle.FindComponentsOfType().Where(x => { GameObject gameObject; return (gameObject = x.gameObject).scene.isLoaded && gameObject.activeInHierarchy; }).Select(x => x.gameObject); return results.Prepend(brush.hiddenGrid).ToArray(); } } } }