No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GameObjectBrush.cs 38KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEditor.SceneManagement;
  4. using UnityEngine;
  5. using UnityEngine.SceneManagement;
  6. using Object = UnityEngine.Object;
  7. namespace UnityEditor.Tilemaps
  8. {
  9. /// <summary>
  10. /// This Brush instances, places and manipulates GameObjects onto the scene.
  11. /// Use this as an example to create brushes which targets objects other than tiles for manipulation.
  12. /// </summary>
  13. [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@latest/index.html?subfolder=/manual/GameObjectBrush.html")]
  14. [CustomGridBrush(true, false, false, "GameObject Brush")]
  15. public class GameObjectBrush : GridBrushBase
  16. {
  17. [Serializable]
  18. internal class HiddenGridLayout
  19. {
  20. public Vector3 cellSize = Vector3.one;
  21. public Vector3 cellGap = Vector3.zero;
  22. public GridLayout.CellLayout cellLayout = GridLayout.CellLayout.Rectangle;
  23. public GridLayout.CellSwizzle cellSwizzle = GridLayout.CellSwizzle.XYZ;
  24. }
  25. [SerializeField]
  26. private BrushCell[] m_Cells;
  27. [SerializeField]
  28. private Vector3Int m_Size;
  29. [SerializeField]
  30. private Vector3Int m_Pivot;
  31. [SerializeField]
  32. [HideInInspector]
  33. private bool m_CanChangeZPosition;
  34. [SerializeField]
  35. [HideInInspector]
  36. internal HiddenGridLayout hiddenGridLayout = new HiddenGridLayout();
  37. /// <summary>
  38. /// GameObject used for painting onto the Scene root
  39. /// </summary>
  40. [HideInInspector]
  41. public GameObject hiddenGrid;
  42. /// <summary>
  43. /// Anchor Point of the Instantiated GameObject in the cell when painting
  44. /// </summary>
  45. public Vector3 m_Anchor = new Vector3(0.5f, 0.5f, 0.0f);
  46. /// <summary>Size of the brush in cells. </summary>
  47. public Vector3Int size { get { return m_Size; } set { m_Size = value; SizeUpdated(); } }
  48. /// <summary>Pivot of the brush. </summary>
  49. public Vector3Int pivot { get { return m_Pivot; } set { m_Pivot = value; } }
  50. /// <summary>All the brush cells the brush holds. </summary>
  51. public BrushCell[] cells { get { return m_Cells; } }
  52. /// <summary>Number of brush cells in the brush.</summary>
  53. public int cellCount { get { return m_Cells != null ? m_Cells.Length : 0; } }
  54. /// <summary>Number of brush cells based on size.</summary>
  55. public int sizeCount
  56. {
  57. get { return m_Size.x * m_Size.y * m_Size.z; }
  58. }
  59. /// <summary>Whether the brush can change Z Position</summary>
  60. public bool canChangeZPosition
  61. {
  62. get { return m_CanChangeZPosition; }
  63. set { m_CanChangeZPosition = value; }
  64. }
  65. /// <summary>
  66. /// This Brush instances, places and manipulates GameObjects onto the scene.
  67. /// </summary>
  68. public GameObjectBrush()
  69. {
  70. Init(Vector3Int.one, Vector3Int.zero);
  71. SizeUpdated();
  72. }
  73. private void OnEnable()
  74. {
  75. hiddenGrid = new GameObject();
  76. hiddenGrid.name = "(Paint on SceneRoot)";
  77. hiddenGrid.hideFlags = HideFlags.HideAndDontSave;
  78. hiddenGrid.transform.position = Vector3.zero;
  79. var grid = hiddenGrid.AddComponent<Grid>();
  80. grid.cellSize = hiddenGridLayout.cellSize;
  81. grid.cellGap = hiddenGridLayout.cellGap;
  82. grid.cellSwizzle = hiddenGridLayout.cellSwizzle;
  83. grid.cellLayout = hiddenGridLayout.cellLayout;
  84. }
  85. private void OnDisable()
  86. {
  87. DestroyImmediate(hiddenGrid);
  88. }
  89. private void OnValidate()
  90. {
  91. if (m_Size.x < 0)
  92. m_Size.x = 0;
  93. if (m_Size.y < 0)
  94. m_Size.y = 0;
  95. if (m_Size.z < 0)
  96. m_Size.z = 0;
  97. }
  98. /// <summary>
  99. /// Initializes the content of the GameObjectBrush.
  100. /// </summary>
  101. /// <param name="size">Size of the GameObjectBrush.</param>
  102. public void Init(Vector3Int size)
  103. {
  104. Init(size, Vector3Int.zero);
  105. SizeUpdated();
  106. }
  107. /// <summary>Initializes the content of the GameObjectBrush.</summary>
  108. /// <param name="size">Size of the GameObjectBrush.</param>
  109. /// <param name="pivot">Pivot point of the GameObjectBrush.</param>
  110. public void Init(Vector3Int size, Vector3Int pivot)
  111. {
  112. m_Size = size;
  113. m_Pivot = pivot;
  114. SizeUpdated();
  115. }
  116. /// <summary>
  117. /// Paints GameObjects into a given position within the selected layers.
  118. /// The GameObjectBrush overrides this to provide GameObject painting functionality.
  119. /// </summary>
  120. /// <param name="gridLayout">Grid used for layout.</param>
  121. /// <param name="brushTarget">Target of the paint operation. By default the currently selected GameObject.</param>
  122. /// <param name="position">The coordinates of the cell to paint data to.</param>
  123. public override void Paint(GridLayout gridLayout, GameObject brushTarget, Vector3Int position)
  124. {
  125. var min = position - pivot;
  126. var bounds = new BoundsInt(min, m_Size);
  127. BoxFill(gridLayout, brushTarget, bounds);
  128. }
  129. private void PaintCell(GridLayout grid, Vector3Int position, Transform parent, BrushCell cell)
  130. {
  131. if (cell.gameObject == null)
  132. return;
  133. var existingGO = GetObjectInCell(grid, parent, position, m_Anchor, cell.offset);
  134. if (existingGO == null)
  135. {
  136. SetSceneCell(grid, parent, position, cell.gameObject, cell.offset, cell.scale, cell.orientation, m_Anchor);
  137. }
  138. }
  139. /// <summary>
  140. /// Erases GameObjects in a given position within the selected layers.
  141. /// The GameObjectBrush overrides this to provide GameObject erasing functionality.
  142. /// </summary>
  143. /// <param name="gridLayout">Grid used for layout.</param>
  144. /// <param name="brushTarget">Target of the erase operation. By default the currently selected GameObject.</param>
  145. /// <param name="position">The coordinates of the cell to erase data from.</param>
  146. public override void Erase(GridLayout gridLayout, GameObject brushTarget, Vector3Int position)
  147. {
  148. var min = position - pivot;
  149. var bounds = new BoundsInt(min, m_Size);
  150. GetGrid(ref gridLayout, ref brushTarget);
  151. BoxErase(gridLayout, brushTarget, bounds);
  152. }
  153. private void EraseCell(GridLayout grid, Vector3Int position, Transform parent, BrushCell cell)
  154. {
  155. ClearSceneCell(grid, parent, position, cell);
  156. }
  157. /// <summary>
  158. /// Box fills GameObjects into given bounds within the selected layers.
  159. /// The GameObjectBrush overrides this to provide GameObject box-filling functionality.
  160. /// </summary>
  161. /// <param name="gridLayout">Grid to box fill data to.</param>
  162. /// <param name="brushTarget">Target of the box fill operation. By default the currently selected GameObject.</param>
  163. /// <param name="position">The bounds to box fill data into.</param>
  164. public override void BoxFill(GridLayout gridLayout, GameObject brushTarget, BoundsInt position)
  165. {
  166. GetGrid(ref gridLayout, ref brushTarget);
  167. foreach (var location in position.allPositionsWithin)
  168. {
  169. var local = location - position.min;
  170. var cell = m_Cells[GetCellIndexWrapAround(local.x, local.y, local.z)];
  171. PaintCell(gridLayout, location, brushTarget != null ? brushTarget.transform : null, cell);
  172. }
  173. }
  174. /// <summary>
  175. /// Erases GameObjects from given bounds within the selected layers.
  176. /// The GameObjectBrush overrides this to provide GameObject box-erasing functionality.
  177. /// </summary>
  178. /// <param name="gridLayout">Grid to erase data from.</param>
  179. /// <param name="brushTarget">Target of the erase operation. By default the currently selected GameObject.</param>
  180. /// <param name="position">The bounds to erase data from.</param>
  181. public override void BoxErase(GridLayout gridLayout, GameObject brushTarget, BoundsInt position)
  182. {
  183. GetGrid(ref gridLayout, ref brushTarget);
  184. foreach (var location in position.allPositionsWithin)
  185. {
  186. var local = location - position.min;
  187. var cell = m_Cells[GetCellIndexWrapAround(local.x, local.y, local.z)];
  188. EraseCell(gridLayout, location, brushTarget != null ? brushTarget.transform : null, cell);
  189. }
  190. }
  191. /// <summary>
  192. /// This is not supported but it should flood-fill GameObjects starting from a given position within the selected layers.
  193. /// </summary>
  194. /// <param name="gridLayout">Grid used for layout.</param>
  195. /// <param name="brushTarget">Target of the flood fill operation. By default the currently selected GameObject.</param>
  196. /// <param name="position">Starting position of the flood fill.</param>
  197. public override void FloodFill(GridLayout gridLayout, GameObject brushTarget, Vector3Int position)
  198. {
  199. Debug.LogWarning("FloodFill not supported");
  200. }
  201. /// <summary>
  202. /// Rotates the brush by 90 degrees in the given direction.
  203. /// </summary>
  204. /// <param name="direction">Direction to rotate by.</param>
  205. /// <param name="layout">Cell Layout for rotating.</param>
  206. public override void Rotate(RotationDirection direction, GridLayout.CellLayout layout)
  207. {
  208. var oldSize = m_Size;
  209. var oldCells = m_Cells.Clone() as BrushCell[];
  210. size = new Vector3Int(oldSize.y, oldSize.x, oldSize.z);
  211. var oldBounds = new BoundsInt(Vector3Int.zero, oldSize);
  212. foreach (Vector3Int oldPos in oldBounds.allPositionsWithin)
  213. {
  214. var newX = direction == RotationDirection.Clockwise ? oldSize.y - oldPos.y - 1 : oldPos.y;
  215. var newY = direction == RotationDirection.Clockwise ? oldPos.x : oldSize.x - oldPos.x - 1;
  216. var toIndex = GetCellIndex(newX, newY, oldPos.z);
  217. var fromIndex = GetCellIndex(oldPos.x, oldPos.y, oldPos.z, oldSize.x, oldSize.y, oldSize.z);
  218. m_Cells[toIndex] = oldCells[fromIndex];
  219. }
  220. var newPivotX = direction == RotationDirection.Clockwise ? oldSize.y - pivot.y - 1 : pivot.y;
  221. var newPivotY = direction == RotationDirection.Clockwise ? pivot.x : oldSize.x - pivot.x - 1;
  222. pivot = new Vector3Int(newPivotX, newPivotY, pivot.z);
  223. Quaternion orientation = Quaternion.Euler(0f, 0f, direction != RotationDirection.Clockwise ? 90f : -90f);
  224. foreach (BrushCell cell in m_Cells)
  225. cell.orientation = cell.orientation * orientation;
  226. }
  227. /// <summary>Flips the brush in the given axis.</summary>
  228. /// <param name="flip">Axis to flip by.</param>
  229. /// <param name="layout">Cell Layout for flipping.</param>
  230. public override void Flip(FlipAxis flip, GridLayout.CellLayout layout)
  231. {
  232. if (flip == FlipAxis.X)
  233. FlipX();
  234. else
  235. FlipY();
  236. }
  237. /// <summary>
  238. /// Picks child GameObjects given the coordinates of the cells.
  239. /// The GameObjectBrush overrides this to provide GameObject picking functionality.
  240. /// </summary>
  241. /// <param name="gridLayout">Grid to pick data from.</param>
  242. /// <param name="brushTarget">Target of the picking operation. By default the currently selected GameObject.</param>
  243. /// <param name="position">The coordinates of the cells to paint data from.</param>
  244. /// <param name="pivot">Pivot of the picking brush.</param>
  245. public override void Pick(GridLayout gridLayout, GameObject brushTarget, BoundsInt position, Vector3Int pivot)
  246. {
  247. Reset();
  248. UpdateSizeAndPivot(new Vector3Int(position.size.x, position.size.y, 1), new Vector3Int(pivot.x, pivot.y, 0));
  249. GetGrid(ref gridLayout, ref brushTarget);
  250. foreach (Vector3Int pos in position.allPositionsWithin)
  251. {
  252. Vector3Int brushPosition = new Vector3Int(pos.x - position.x, pos.y - position.y, 0);
  253. PickCell(pos, brushPosition, gridLayout, brushTarget != null ? brushTarget.transform : null, true);
  254. }
  255. }
  256. private void PickCell(Vector3Int position, Vector3Int brushPosition, GridLayout grid, Transform parent, bool withoutAnchor = false)
  257. {
  258. GameObject go = null;
  259. if (!withoutAnchor)
  260. go = GetObjectInCell(grid, parent, position, m_Anchor, Vector3.zero);
  261. if (go == null)
  262. go = GetObjectInCell(grid, parent, position, Vector3.zero, Vector3.zero);
  263. var anchorRatio = GetAnchorRatio(grid, m_Anchor);
  264. var cellLocalPosition = grid.CellToLocalInterpolated(position);
  265. var anchorLocalPosition = grid.CellToLocalInterpolated(anchorRatio);
  266. var cellCenter = grid.LocalToWorld(cellLocalPosition + anchorLocalPosition);
  267. if (go != null)
  268. {
  269. Object prefab = PrefabUtility.GetCorrespondingObjectFromSource(go);
  270. if (prefab)
  271. {
  272. SetGameObject(brushPosition, (GameObject) prefab);
  273. }
  274. else
  275. {
  276. GameObject newInstance = Instantiate(go);
  277. newInstance.hideFlags = HideFlags.HideAndDontSave;
  278. newInstance.SetActive(false);
  279. SetGameObject(brushPosition, newInstance);
  280. }
  281. SetOffset(brushPosition, go.transform.position - cellCenter);
  282. SetScale(brushPosition, go.transform.localScale);
  283. SetOrientation(brushPosition, go.transform.localRotation);
  284. }
  285. }
  286. /// <summary>
  287. /// MoveStart is called when user starts moving the area previously selected with the selection marquee.
  288. /// The GameObjectBrush overrides this to provide GameObject moving functionality.
  289. /// </summary>
  290. /// <param name="gridLayout">Grid used for layout.</param>
  291. /// <param name="brushTarget">Target of the move operation. By default the currently selected GameObject.</param>
  292. /// <param name="position">Position where the move operation has started.</param>
  293. public override void MoveStart(GridLayout gridLayout, GameObject brushTarget, BoundsInt position)
  294. {
  295. Reset();
  296. UpdateSizeAndPivot(new Vector3Int(position.size.x, position.size.y, 1), Vector3Int.zero);
  297. GetGrid(ref gridLayout, ref brushTarget);
  298. var targetTransform = brushTarget != null ? brushTarget.transform : null;
  299. foreach (var pos in position.allPositionsWithin)
  300. {
  301. var brushPosition = new Vector3Int(pos.x - position.x, pos.y - position.y, 0);
  302. PickCell(pos, brushPosition, gridLayout, targetTransform);
  303. var cell = m_Cells[GetCellIndex(brushPosition)];
  304. ClearSceneCell(gridLayout, targetTransform, pos, cell);
  305. }
  306. }
  307. /// <summary>
  308. /// MoveEnd is called when user has ended the move of the area previously selected with the selection marquee.
  309. /// The GameObjectBrush overrides this to provide GameObject moving functionality.
  310. /// </summary>
  311. /// <param name="gridLayout">Grid used for layout.</param>
  312. /// <param name="brushTarget">Target of the move operation. By default the currently selected GameObject.</param>
  313. /// <param name="position">Position where the move operation has ended.</param>
  314. public override void MoveEnd(GridLayout gridLayout, GameObject brushTarget, BoundsInt position)
  315. {
  316. GetGrid(ref gridLayout, ref brushTarget);
  317. Paint(gridLayout, brushTarget, position.min);
  318. Reset();
  319. }
  320. private void GetGrid(ref GridLayout gridLayout, ref GameObject brushTarget)
  321. {
  322. if (brushTarget == hiddenGrid)
  323. brushTarget = null;
  324. if (brushTarget != null)
  325. {
  326. var targetGridLayout = brushTarget.GetComponent<GridLayout>();
  327. if (targetGridLayout != null)
  328. gridLayout = targetGridLayout;
  329. }
  330. }
  331. /// <summary>Clears all data of the brush.</summary>
  332. public void Reset()
  333. {
  334. foreach (var cell in m_Cells)
  335. {
  336. if (cell.gameObject != null && !EditorUtility.IsPersistent(cell.gameObject))
  337. {
  338. DestroyImmediate(cell.gameObject);
  339. }
  340. cell.gameObject = null;
  341. }
  342. UpdateSizeAndPivot(Vector3Int.one, Vector3Int.zero);
  343. }
  344. private void FlipX()
  345. {
  346. var oldCells = m_Cells.Clone() as BrushCell[];
  347. var oldBounds = new BoundsInt(Vector3Int.zero, m_Size);
  348. foreach (var oldPos in oldBounds.allPositionsWithin)
  349. {
  350. var newX = m_Size.x - oldPos.x - 1;
  351. var toIndex = GetCellIndex(newX, oldPos.y, oldPos.z);
  352. var fromIndex = GetCellIndex(oldPos);
  353. m_Cells[toIndex] = oldCells[fromIndex];
  354. }
  355. var newPivotX = m_Size.x - pivot.x - 1;
  356. pivot = new Vector3Int(newPivotX, pivot.y, pivot.z);
  357. FlipCells(ref m_Cells, new Vector3(-1f, 1f, 1f));
  358. }
  359. private void FlipY()
  360. {
  361. var oldCells = m_Cells.Clone() as BrushCell[];
  362. var oldBounds = new BoundsInt(Vector3Int.zero, m_Size);
  363. foreach (var oldPos in oldBounds.allPositionsWithin)
  364. {
  365. var newY = m_Size.y - oldPos.y - 1;
  366. var toIndex = GetCellIndex(oldPos.x, newY, oldPos.z);
  367. var fromIndex = GetCellIndex(oldPos);
  368. m_Cells[toIndex] = oldCells[fromIndex];
  369. }
  370. var newPivotY = m_Size.y - pivot.y - 1;
  371. pivot = new Vector3Int(pivot.x, newPivotY, pivot.z);
  372. FlipCells(ref m_Cells, new Vector3(1f, -1f, 1f));
  373. }
  374. private static void FlipCells(ref BrushCell[] cells, Vector3 scale)
  375. {
  376. foreach (BrushCell cell in cells)
  377. {
  378. cell.scale = Vector3.Scale(cell.scale, scale);
  379. }
  380. }
  381. /// <summary>Updates the size, pivot and the number of layers of the brush.</summary>
  382. /// <param name="size">New size of the brush.</param>
  383. /// <param name="pivot">New pivot of the brush.</param>
  384. public void UpdateSizeAndPivot(Vector3Int size, Vector3Int pivot)
  385. {
  386. m_Size = size;
  387. m_Pivot = pivot;
  388. SizeUpdated();
  389. }
  390. /// <summary>
  391. /// Sets a GameObject at the position in the brush.
  392. /// </summary>
  393. /// <param name="position">Position to set the GameObject in the brush.</param>
  394. /// <param name="go">GameObject to set in the brush.</param>
  395. public void SetGameObject(Vector3Int position, GameObject go)
  396. {
  397. if (ValidateCellPosition(position))
  398. m_Cells[GetCellIndex(position)].gameObject = go;
  399. }
  400. /// <summary>
  401. /// Sets a position offset at the position in the brush.
  402. /// </summary>
  403. /// <param name="position">Position to set the offset in the brush.</param>
  404. /// <param name="offset">Offset to set in the brush.</param>
  405. public void SetOffset(Vector3Int position, Vector3 offset)
  406. {
  407. if (ValidateCellPosition(position))
  408. m_Cells[GetCellIndex(position)].offset = offset;
  409. }
  410. /// <summary>
  411. /// Sets an orientation at the position in the brush.
  412. /// </summary>
  413. /// <param name="position">Position to set the orientation in the brush.</param>
  414. /// <param name="orientation">Orientation to set in the brush.</param>
  415. public void SetOrientation(Vector3Int position, Quaternion orientation)
  416. {
  417. if (ValidateCellPosition(position))
  418. m_Cells[GetCellIndex(position)].orientation = orientation;
  419. }
  420. /// <summary>
  421. /// Sets a scale at the position in the brush.
  422. /// </summary>
  423. /// <param name="position">Position to set the scale in the brush.</param>
  424. /// <param name="scale">Scale to set in the brush.</param>
  425. public void SetScale(Vector3Int position, Vector3 scale)
  426. {
  427. if (ValidateCellPosition(position))
  428. m_Cells[GetCellIndex(position)].scale = scale;
  429. }
  430. /// <summary>Gets the index to the GameObjectBrush::ref::BrushCell based on the position of the BrushCell.</summary>
  431. /// <param name="brushPosition">Position of the BrushCell.</param>
  432. /// <returns>The cell index for the position of the BrushCell.</returns>
  433. public int GetCellIndex(Vector3Int brushPosition)
  434. {
  435. return GetCellIndex(brushPosition.x, brushPosition.y, brushPosition.z);
  436. }
  437. /// <summary>Gets the index to the GameObjectBrush::ref::BrushCell based on the position of the BrushCell.</summary>
  438. /// <param name="x">X Position of the BrushCell.</param>
  439. /// <param name="y">Y Position of the BrushCell.</param>
  440. /// <param name="z">Z Position of the BrushCell.</param>
  441. /// <returns>The cell index for the position of the BrushCell.</returns>
  442. public int GetCellIndex(int x, int y, int z)
  443. {
  444. return x + m_Size.x * y + m_Size.x * m_Size.y * z;
  445. }
  446. /// <summary>Gets the index to the GameObjectBrush::ref::BrushCell based on the position of the BrushCell.</summary>
  447. /// <param name="x">X Position of the BrushCell.</param>
  448. /// <param name="y">Y Position of the BrushCell.</param>
  449. /// <param name="z">Z Position of the BrushCell.</param>
  450. /// <param name="sizex">X Size of Brush.</param>
  451. /// <param name="sizey">Y Size of Brush.</param>
  452. /// <param name="sizez">Z Size of Brush.</param>
  453. /// <returns>The cell index for the position of the BrushCell.</returns>
  454. public int GetCellIndex(int x, int y, int z, int sizex, int sizey, int sizez)
  455. {
  456. return x + sizex * y + sizex * sizey * z;
  457. }
  458. /// <summary>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.</summary>
  459. /// <param name="x">X Position of the BrushCell.</param>
  460. /// <param name="y">Y Position of the BrushCell.</param>
  461. /// <param name="z">Z Position of the BrushCell.</param>
  462. /// <returns>The cell index for the position of the BrushCell.</returns>
  463. public int GetCellIndexWrapAround(int x, int y, int z)
  464. {
  465. return (x % m_Size.x) + m_Size.x * (y % m_Size.y) + m_Size.x * m_Size.y * (z % m_Size.z);
  466. }
  467. private GameObject GetObjectInCell(GridLayout grid, Transform parent, Vector3Int position, Vector3 anchor, Vector3 offset)
  468. {
  469. int childCount;
  470. GameObject[] sceneChildren = null;
  471. if (parent == null)
  472. {
  473. var scene = SceneManager.GetActiveScene();
  474. sceneChildren = scene.GetRootGameObjects();
  475. childCount = scene.rootCount;
  476. }
  477. else
  478. {
  479. childCount = parent.childCount;
  480. }
  481. var anchorRatio = GetAnchorRatio(grid, anchor);
  482. var anchorLocal = grid.CellToLocalInterpolated(anchorRatio);
  483. for (var i = 0; i < childCount; i++)
  484. {
  485. var child = sceneChildren == null ? parent.GetChild(i) : sceneChildren[i].transform;
  486. var childCell = grid.LocalToCell(grid.WorldToLocal(child.position) - anchorLocal - offset);
  487. if (position == childCell)
  488. return child.gameObject;
  489. }
  490. return null;
  491. }
  492. private bool ValidateCellPosition(Vector3Int position)
  493. {
  494. var valid =
  495. position.x >= 0 && position.x < size.x &&
  496. position.y >= 0 && position.y < size.y &&
  497. position.z >= 0 && position.z < size.z;
  498. if (!valid)
  499. throw new ArgumentException(string.Format("Position {0} is an invalid cell position. Valid range is between [{1}, {2}).", position, Vector3Int.zero, size));
  500. return true;
  501. }
  502. internal void SizeUpdated(bool keepContents = false)
  503. {
  504. OnValidate();
  505. Array.Resize(ref m_Cells, sizeCount);
  506. var bounds = new BoundsInt(Vector3Int.zero, m_Size);
  507. foreach (var pos in bounds.allPositionsWithin)
  508. {
  509. if (keepContents || m_Cells[GetCellIndex(pos)] == null)
  510. m_Cells[GetCellIndex(pos)] = new BrushCell();
  511. }
  512. }
  513. private static void SetSceneCell(GridLayout grid, Transform parent, Vector3Int position, GameObject go, Vector3 offset, Vector3 scale, Quaternion orientation, Vector3 anchor)
  514. {
  515. if (go == null)
  516. return;
  517. GameObject instance;
  518. if (PrefabUtility.IsPartOfPrefabAsset(go))
  519. {
  520. instance = (GameObject) PrefabUtility.InstantiatePrefab(go, parent != null ? parent.root.gameObject.scene : SceneManager.GetActiveScene());
  521. instance.transform.parent = parent;
  522. }
  523. else
  524. {
  525. instance = Instantiate(go, parent);
  526. instance.name = go.name;
  527. instance.SetActive(true);
  528. foreach (var renderer in instance.GetComponentsInChildren<Renderer>())
  529. {
  530. renderer.enabled = true;
  531. }
  532. }
  533. instance.hideFlags = HideFlags.None;
  534. Undo.RegisterCreatedObjectUndo(instance, "Paint GameObject");
  535. var anchorRatio = GetAnchorRatio(grid, anchor);
  536. instance.transform.position = grid.LocalToWorld(grid.CellToLocalInterpolated(position) + grid.CellToLocalInterpolated(anchorRatio));
  537. instance.transform.localRotation = orientation;
  538. instance.transform.localScale = scale;
  539. instance.transform.Translate(offset);
  540. }
  541. private static Vector3 GetAnchorRatio(GridLayout grid, Vector3 cellAnchor)
  542. {
  543. var cellSize = grid.cellSize;
  544. var cellStride = cellSize + grid.cellGap;
  545. cellStride.x = Mathf.Approximately(0f, cellStride.x) ? 1f : cellStride.x;
  546. cellStride.y = Mathf.Approximately(0f, cellStride.y) ? 1f : cellStride.y;
  547. cellStride.z = Mathf.Approximately(0f, cellStride.z) ? 1f : cellStride.z;
  548. var anchorRatio = new Vector3(
  549. cellAnchor.x * cellSize.x / cellStride.x,
  550. cellAnchor.y * cellSize.y / cellStride.y,
  551. cellAnchor.z * cellSize.z / cellStride.z
  552. );
  553. return anchorRatio;
  554. }
  555. private void ClearSceneCell(GridLayout grid, Transform parent, Vector3Int position, BrushCell cell)
  556. {
  557. var erased = GetObjectInCell(grid, parent, position, m_Anchor, cell.offset);
  558. if (erased != null)
  559. Undo.DestroyObjectImmediate(erased);
  560. }
  561. /// <summary>
  562. /// Hashes the contents of the brush.
  563. /// </summary>
  564. /// <returns>A hash code of the brush</returns>
  565. public override int GetHashCode()
  566. {
  567. int hash = 0;
  568. unchecked
  569. {
  570. foreach (var cell in cells)
  571. {
  572. hash = hash * 33 + cell.GetHashCode();
  573. }
  574. }
  575. return hash;
  576. }
  577. internal void UpdateHiddenGridLayout()
  578. {
  579. var grid = hiddenGrid.GetComponent<Grid>();
  580. hiddenGridLayout.cellSize = grid.cellSize;
  581. hiddenGridLayout.cellGap = grid.cellGap;
  582. hiddenGridLayout.cellSwizzle = grid.cellSwizzle;
  583. hiddenGridLayout.cellLayout = grid.cellLayout;
  584. }
  585. /// <summary>
  586. ///Brush Cell stores the data to be painted in a grid cell.
  587. /// </summary>
  588. [Serializable]
  589. public class BrushCell
  590. {
  591. /// <summary>
  592. /// GameObject to be placed when painting.
  593. /// </summary>
  594. public GameObject gameObject
  595. {
  596. get => m_GameObject;
  597. set => m_GameObject = value;
  598. }
  599. /// <summary>
  600. /// Position offset of the GameObject when painted.
  601. /// </summary>
  602. public Vector3 offset
  603. {
  604. get => m_Offset;
  605. set => m_Offset = value;
  606. }
  607. /// <summary>
  608. /// Scale of the GameObject when painted.
  609. /// </summary>
  610. public Vector3 scale
  611. {
  612. get => m_Scale;
  613. set => m_Scale = value;
  614. }
  615. /// <summary>
  616. /// Orientation of the GameObject when painted.
  617. /// </summary>
  618. public Quaternion orientation
  619. {
  620. get => m_Orientation;
  621. set => m_Orientation = value;
  622. }
  623. [SerializeField]
  624. private GameObject m_GameObject;
  625. [SerializeField]
  626. Vector3 m_Offset = Vector3.zero;
  627. [SerializeField]
  628. Vector3 m_Scale = Vector3.one;
  629. [SerializeField]
  630. Quaternion m_Orientation = Quaternion.identity;
  631. /// <summary>
  632. /// Hashes the contents of the brush cell.
  633. /// </summary>
  634. /// <returns>A hash code of the brush cell.</returns>
  635. public override int GetHashCode()
  636. {
  637. int hash;
  638. unchecked
  639. {
  640. hash = gameObject != null ? gameObject.GetInstanceID() : 0;
  641. hash = hash * 33 + offset.GetHashCode();
  642. hash = hash * 33 + scale.GetHashCode();
  643. hash = hash * 33 + orientation.GetHashCode();
  644. }
  645. return hash;
  646. }
  647. }
  648. }
  649. /// <summary>
  650. /// The Brush Editor for a GameObject Brush.
  651. /// </summary>
  652. [CustomEditor(typeof(GameObjectBrush))]
  653. public class GameObjectBrushEditor : GridBrushEditorBase
  654. {
  655. private static readonly string iconPath = "Packages/com.unity.2d.tilemap.extras/Editor/Brushes/GameObjectBrush/GameObjectBrush.png";
  656. private Texture2D m_BrushIcon;
  657. private bool hiddenGridFoldout;
  658. private Editor hiddenGridEditor;
  659. /// <summary>
  660. /// The GameObjectBrush for this Editor
  661. /// </summary>
  662. public GameObjectBrush brush { get { return target as GameObjectBrush; } }
  663. /// <summary> Whether the GridBrush can change Z Position. </summary>
  664. public override bool canChangeZPosition
  665. {
  666. get => brush.canChangeZPosition;
  667. set => brush.canChangeZPosition = value;
  668. }
  669. /// <summary>
  670. /// Whether the Brush is in a state that should be saved for selection.
  671. /// </summary>
  672. public override bool shouldSaveBrushForSelection
  673. {
  674. get
  675. {
  676. if (brush.cells != null)
  677. {
  678. foreach (var cell in brush.cells)
  679. {
  680. if (cell != null && cell.gameObject != null)
  681. return true;
  682. }
  683. }
  684. return false;
  685. }
  686. }
  687. /// <summary>
  688. /// Callback for painting the GUI for the GridBrush in the Scene View.
  689. /// The GameObjectBrush Editor overrides this to draw the preview of the brush when drawing lines.
  690. /// </summary>
  691. /// <param name="gridLayout">Grid that the brush is being used on.</param>
  692. /// <param name="brushTarget">Target of the GameObjectBrush::ref::Tool operation. By default the currently selected GameObject.</param>
  693. /// <param name="position">Current selected location of the brush.</param>
  694. /// <param name="tool">Current GameObjectBrush::ref::Tool selected.</param>
  695. /// <param name="executing">Whether brush is being used.</param>
  696. public override void OnPaintSceneGUI(GridLayout gridLayout, GameObject brushTarget, BoundsInt position, GridBrushBase.Tool tool, bool executing)
  697. {
  698. var gizmoRect = position;
  699. if (tool == GridBrushBase.Tool.Paint || tool == GridBrushBase.Tool.Erase)
  700. gizmoRect = new BoundsInt(position.min - brush.pivot, brush.size);
  701. base.OnPaintSceneGUI(gridLayout, brushTarget, gizmoRect, tool, executing);
  702. }
  703. /// <summary>
  704. /// Callback for painting the inspector GUI for the GameObjectBrush in the tilemap palette.
  705. /// The GameObjectBrush Editor overrides this to show the usage of this Brush.
  706. /// </summary>
  707. public override void OnPaintInspectorGUI()
  708. {
  709. EditorGUI.BeginChangeCheck();
  710. base.OnInspectorGUI();
  711. if (EditorGUI.EndChangeCheck() && brush.cellCount != brush.sizeCount)
  712. {
  713. brush.SizeUpdated(true);
  714. }
  715. hiddenGridFoldout = EditorGUILayout.Foldout(hiddenGridFoldout, "SceneRoot Grid");
  716. if (hiddenGridFoldout)
  717. {
  718. EditorGUI.indentLevel++;
  719. using (new EditorGUI.DisabledScope(GridPaintingState.scenePaintTarget != brush.hiddenGrid))
  720. {
  721. if (hiddenGridEditor == null)
  722. {
  723. hiddenGridEditor = Editor.CreateEditor(brush.hiddenGrid.GetComponent<Grid>());
  724. }
  725. brush.hiddenGrid.hideFlags = HideFlags.None;
  726. EditorGUI.BeginChangeCheck();
  727. hiddenGridEditor.OnInspectorGUI();
  728. if (EditorGUI.EndChangeCheck())
  729. {
  730. brush.UpdateHiddenGridLayout();
  731. EditorUtility.SetDirty(brush);
  732. SceneView.RepaintAll();
  733. }
  734. brush.hiddenGrid.hideFlags = HideFlags.HideAndDontSave;
  735. }
  736. EditorGUI.indentLevel--;
  737. }
  738. }
  739. /// <summary>
  740. /// The targets that the GameObjectBrush can paint on
  741. /// </summary>
  742. public override GameObject[] validTargets
  743. {
  744. get
  745. {
  746. var currentStageHandle = StageUtility.GetCurrentStageHandle();
  747. var results = currentStageHandle.FindComponentsOfType<GridLayout>();
  748. var validGridLayouts = new List<GameObject>(results.Length + 1) { brush.hiddenGrid };
  749. foreach (var result in results)
  750. {
  751. if (result.gameObject.scene.isLoaded && result.gameObject.activeInHierarchy)
  752. validGridLayouts.Add(result.gameObject);
  753. }
  754. return validGridLayouts.ToArray();
  755. }
  756. }
  757. /// <summary>
  758. /// Creates a static preview of the GameObjectBrush with its current selection.
  759. /// </summary>
  760. /// <param name="assetPath">The asset to operate on.</param>
  761. /// <param name="subAssets">An array of all Assets at assetPath.</param>
  762. /// <param name="width">Width of the created texture.</param>
  763. /// <param name="height">Height of the created texture.</param>
  764. /// <returns>Generated texture or null.</returns>
  765. public override Texture2D RenderStaticPreview(string assetPath, UnityEngine.Object[] subAssets, int width, int height)
  766. {
  767. if (brush == null)
  768. return null;
  769. var previewInstance = new GameObject("Brush Preview", typeof(Grid));
  770. var previewGrid = previewInstance.GetComponent<Grid>();
  771. brush.Paint(previewGrid, previewInstance, Vector3Int.zero);
  772. var center = ((Vector3) brush.size * 0.5f) - (Vector3) brush.pivot;
  773. center.z -= 10f;
  774. var rect = new Rect(0, 0, width, height);
  775. var previewUtility = new PreviewRenderUtility(true, true);
  776. previewUtility.camera.orthographic = true;
  777. previewUtility.camera.orthographicSize = 1.0f + Math.Max(brush.size.x, brush.size.y);
  778. if (rect.height > rect.width)
  779. previewUtility.camera.orthographicSize *= rect.height / rect.width;
  780. previewUtility.camera.transform.position = center;
  781. previewUtility.AddSingleGO(previewInstance);
  782. previewUtility.BeginStaticPreview(rect);
  783. previewUtility.camera.Render();
  784. var tex = previewUtility.EndStaticPreview();
  785. previewUtility.Cleanup();
  786. DestroyImmediate(previewInstance);
  787. return tex;
  788. }
  789. /// <summary> Returns an icon identifying the GameObject Brush. </summary>
  790. public override Texture2D icon
  791. {
  792. get
  793. {
  794. if (m_BrushIcon == null)
  795. {
  796. var gui = EditorGUIUtility.TrIconContent(iconPath);
  797. m_BrushIcon = gui.image as Texture2D;
  798. }
  799. return m_BrushIcon;
  800. }
  801. }
  802. }
  803. }