Keine Beschreibung
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

GameObjectBrush.cs 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. using System;
  2. using System.Linq;
  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.5f);
  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. /// <summary>
  90. /// Initializes the content of the GameObjectBrush.
  91. /// </summary>
  92. /// <param name="size">Size of the GameObjectBrush.</param>
  93. public void Init(Vector3Int size)
  94. {
  95. Init(size, Vector3Int.zero);
  96. SizeUpdated();
  97. }
  98. /// <summary>Initializes the content of the GameObjectBrush.</summary>
  99. /// <param name="size">Size of the GameObjectBrush.</param>
  100. /// <param name="pivot">Pivot point of the GameObjectBrush.</param>
  101. public void Init(Vector3Int size, Vector3Int pivot)
  102. {
  103. m_Size = size;
  104. m_Pivot = pivot;
  105. SizeUpdated();
  106. }
  107. /// <summary>
  108. /// Paints GameObjects into a given position within the selected layers.
  109. /// The GameObjectBrush overrides this to provide GameObject painting functionality.
  110. /// </summary>
  111. /// <param name="gridLayout">Grid used for layout.</param>
  112. /// <param name="brushTarget">Target of the paint operation. By default the currently selected GameObject.</param>
  113. /// <param name="position">The coordinates of the cell to paint data to.</param>
  114. public override void Paint(GridLayout gridLayout, GameObject brushTarget, Vector3Int position)
  115. {
  116. Vector3Int min = position - pivot;
  117. BoundsInt bounds = new BoundsInt(min, m_Size);
  118. BoxFill(gridLayout, brushTarget, bounds);
  119. }
  120. private void PaintCell(GridLayout grid, Vector3Int position, Transform parent, BrushCell cell)
  121. {
  122. if (cell.gameObject == null)
  123. return;
  124. var existingGO = GetObjectInCell(grid, parent, position);
  125. if (existingGO == null)
  126. {
  127. SetSceneCell(grid, parent, position, cell.gameObject, cell.offset, cell.scale, cell.orientation, m_Anchor);
  128. }
  129. }
  130. /// <summary>
  131. /// Erases GameObjects in a given position within the selected layers.
  132. /// The GameObjectBrush overrides this to provide GameObject erasing functionality.
  133. /// </summary>
  134. /// <param name="gridLayout">Grid used for layout.</param>
  135. /// <param name="brushTarget">Target of the erase operation. By default the currently selected GameObject.</param>
  136. /// <param name="position">The coordinates of the cell to erase data from.</param>
  137. public override void Erase(GridLayout gridLayout, GameObject brushTarget, Vector3Int position)
  138. {
  139. Vector3Int min = position - pivot;
  140. BoundsInt bounds = new BoundsInt(min, m_Size);
  141. GetGrid(ref gridLayout, ref brushTarget);
  142. BoxErase(gridLayout, brushTarget, bounds);
  143. }
  144. private void EraseCell(GridLayout grid, Vector3Int position, Transform parent)
  145. {
  146. ClearSceneCell(grid, parent, position);
  147. }
  148. /// <summary>
  149. /// Box fills GameObjects into given bounds within the selected layers.
  150. /// The GameObjectBrush overrides this to provide GameObject box-filling functionality.
  151. /// </summary>
  152. /// <param name="gridLayout">Grid to box fill data to.</param>
  153. /// <param name="brushTarget">Target of the box fill operation. By default the currently selected GameObject.</param>
  154. /// <param name="position">The bounds to box fill data into.</param>
  155. public override void BoxFill(GridLayout gridLayout, GameObject brushTarget, BoundsInt position)
  156. {
  157. GetGrid(ref gridLayout, ref brushTarget);
  158. foreach (Vector3Int location in position.allPositionsWithin)
  159. {
  160. Vector3Int local = location - position.min;
  161. BrushCell cell = m_Cells[GetCellIndexWrapAround(local.x, local.y, local.z)];
  162. PaintCell(gridLayout, location, brushTarget != null ? brushTarget.transform : null, cell);
  163. }
  164. }
  165. /// <summary>
  166. /// Erases GameObjects from given bounds within the selected layers.
  167. /// The GameObjectBrush overrides this to provide GameObject box-erasing functionality.
  168. /// </summary>
  169. /// <param name="gridLayout">Grid to erase data from.</param>
  170. /// <param name="brushTarget">Target of the erase operation. By default the currently selected GameObject.</param>
  171. /// <param name="position">The bounds to erase data from.</param>
  172. public override void BoxErase(GridLayout gridLayout, GameObject brushTarget, BoundsInt position)
  173. {
  174. GetGrid(ref gridLayout, ref brushTarget);
  175. foreach (Vector3Int location in position.allPositionsWithin)
  176. {
  177. EraseCell(gridLayout, location, brushTarget != null ? brushTarget.transform : null);
  178. }
  179. }
  180. /// <summary>
  181. /// This is not supported but it should floodfill GameObjects starting from a given position within the selected layers.
  182. /// </summary>
  183. /// <param name="gridLayout">Grid used for layout.</param>
  184. /// <param name="brushTarget">Target of the flood fill operation. By default the currently selected GameObject.</param>
  185. /// <param name="position">Starting position of the flood fill.</param>
  186. public override void FloodFill(GridLayout gridLayout, GameObject brushTarget, Vector3Int position)
  187. {
  188. Debug.LogWarning("FloodFill not supported");
  189. }
  190. /// <summary>
  191. /// Rotates the brush by 90 degrees in the given direction.
  192. /// </summary>
  193. /// <param name="direction">Direction to rotate by.</param>
  194. /// <param name="layout">Cell Layout for rotating.</param>
  195. public override void Rotate(RotationDirection direction, GridLayout.CellLayout layout)
  196. {
  197. Vector3Int oldSize = m_Size;
  198. BrushCell[] oldCells = m_Cells.Clone() as BrushCell[];
  199. size = new Vector3Int(oldSize.y, oldSize.x, oldSize.z);
  200. BoundsInt oldBounds = new BoundsInt(Vector3Int.zero, oldSize);
  201. foreach (Vector3Int oldPos in oldBounds.allPositionsWithin)
  202. {
  203. int newX = direction == RotationDirection.Clockwise ? oldSize.y - oldPos.y - 1 : oldPos.y;
  204. int newY = direction == RotationDirection.Clockwise ? oldPos.x : oldSize.x - oldPos.x - 1;
  205. int toIndex = GetCellIndex(newX, newY, oldPos.z);
  206. int fromIndex = GetCellIndex(oldPos.x, oldPos.y, oldPos.z, oldSize.x, oldSize.y, oldSize.z);
  207. m_Cells[toIndex] = oldCells[fromIndex];
  208. }
  209. int newPivotX = direction == RotationDirection.Clockwise ? oldSize.y - pivot.y - 1 : pivot.y;
  210. int newPivotY = direction == RotationDirection.Clockwise ? pivot.x : oldSize.x - pivot.x - 1;
  211. pivot = new Vector3Int(newPivotX, newPivotY, pivot.z);
  212. Matrix4x4 rotation = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, direction == RotationDirection.Clockwise ? 90f : -90f), Vector3.one);
  213. Quaternion orientation = Quaternion.Euler(0f, 0f, direction == RotationDirection.Clockwise ? 90f : -90f);
  214. foreach (BrushCell cell in m_Cells)
  215. {
  216. cell.offset = rotation * cell.offset;
  217. cell.orientation = cell.orientation * orientation;
  218. }
  219. }
  220. /// <summary>Flips the brush in the given axis.</summary>
  221. /// <param name="flip">Axis to flip by.</param>
  222. /// <param name="layout">Cell Layout for flipping.</param>
  223. public override void Flip(FlipAxis flip, GridLayout.CellLayout layout)
  224. {
  225. if (flip == FlipAxis.X)
  226. FlipX();
  227. else
  228. FlipY();
  229. }
  230. /// <summary>
  231. /// Picks child GameObjects given the coordinates of the cells.
  232. /// The GameObjectBrush overrides this to provide GameObject picking functionality.
  233. /// </summary>
  234. /// <param name="gridLayout">Grid to pick data from.</param>
  235. /// <param name="brushTarget">Target of the picking operation. By default the currently selected GameObject.</param>
  236. /// <param name="position">The coordinates of the cells to paint data from.</param>
  237. /// <param name="pivot">Pivot of the picking brush.</param>
  238. public override void Pick(GridLayout gridLayout, GameObject brushTarget, BoundsInt position, Vector3Int pivot)
  239. {
  240. Reset();
  241. UpdateSizeAndPivot(new Vector3Int(position.size.x, position.size.y, 1), new Vector3Int(pivot.x, pivot.y, 0));
  242. GetGrid(ref gridLayout, ref brushTarget);
  243. foreach (Vector3Int pos in position.allPositionsWithin)
  244. {
  245. Vector3Int brushPosition = new Vector3Int(pos.x - position.x, pos.y - position.y, 0);
  246. PickCell(pos, brushPosition, gridLayout, brushTarget != null ? brushTarget.transform : null);
  247. }
  248. }
  249. private void PickCell(Vector3Int position, Vector3Int brushPosition, GridLayout grid, Transform parent)
  250. {
  251. Vector3 cellCenter = grid.LocalToWorld(grid.CellToLocalInterpolated(position + m_Anchor));
  252. GameObject go = GetObjectInCell(grid, parent, position);
  253. if (go != null)
  254. {
  255. Object prefab = PrefabUtility.GetCorrespondingObjectFromSource(go);
  256. if (prefab)
  257. {
  258. SetGameObject(brushPosition, (GameObject) prefab);
  259. }
  260. else
  261. {
  262. GameObject newInstance = Instantiate(go);
  263. newInstance.hideFlags = HideFlags.HideAndDontSave;
  264. newInstance.SetActive(false);
  265. SetGameObject(brushPosition, newInstance);
  266. }
  267. SetOffset(brushPosition, go.transform.position - cellCenter);
  268. SetScale(brushPosition, go.transform.localScale);
  269. SetOrientation(brushPosition, go.transform.localRotation);
  270. }
  271. }
  272. /// <summary>
  273. /// MoveStart is called when user starts moving the area previously selected with the selection marquee.
  274. /// The GameObjectBrush overrides this to provide GameObject moving functionality.
  275. /// </summary>
  276. /// <param name="gridLayout">Grid used for layout.</param>
  277. /// <param name="brushTarget">Target of the move operation. By default the currently selected GameObject.</param>
  278. /// <param name="position">Position where the move operation has started.</param>
  279. public override void MoveStart(GridLayout gridLayout, GameObject brushTarget, BoundsInt position)
  280. {
  281. Reset();
  282. UpdateSizeAndPivot(new Vector3Int(position.size.x, position.size.y, 1), Vector3Int.zero);
  283. GetGrid(ref gridLayout, ref brushTarget);
  284. var targetTransform = brushTarget != null ? brushTarget.transform : null;
  285. foreach (Vector3Int pos in position.allPositionsWithin)
  286. {
  287. Vector3Int brushPosition = new Vector3Int(pos.x - position.x, pos.y - position.y, 0);
  288. PickCell(pos, brushPosition, gridLayout, targetTransform);
  289. ClearSceneCell(gridLayout, targetTransform, pos);
  290. }
  291. }
  292. /// <summary>
  293. /// MoveEnd is called when user has ended the move of the area previously selected with the selection marquee.
  294. /// The GameObjectBrush overrides this to provide GameObject moving functionality.
  295. /// </summary>
  296. /// <param name="gridLayout">Grid used for layout.</param>
  297. /// <param name="brushTarget">Target of the move operation. By default the currently selected GameObject.</param>
  298. /// <param name="position">Position where the move operation has ended.</param>
  299. public override void MoveEnd(GridLayout gridLayout, GameObject brushTarget, BoundsInt position)
  300. {
  301. GetGrid(ref gridLayout, ref brushTarget);
  302. Paint(gridLayout, brushTarget, position.min);
  303. Reset();
  304. }
  305. private void GetGrid(ref GridLayout gridLayout, ref GameObject brushTarget)
  306. {
  307. if (brushTarget == hiddenGrid)
  308. brushTarget = null;
  309. if (brushTarget != null)
  310. {
  311. var targetGridLayout = brushTarget.GetComponent<GridLayout>();
  312. if (targetGridLayout != null)
  313. gridLayout = targetGridLayout;
  314. }
  315. }
  316. /// <summary>Clears all data of the brush.</summary>
  317. public void Reset()
  318. {
  319. foreach (var cell in m_Cells)
  320. {
  321. if (cell.gameObject != null && !EditorUtility.IsPersistent(cell.gameObject))
  322. {
  323. DestroyImmediate(cell.gameObject);
  324. }
  325. cell.gameObject = null;
  326. }
  327. UpdateSizeAndPivot(Vector3Int.one, Vector3Int.zero);
  328. }
  329. private void FlipX()
  330. {
  331. BrushCell[] oldCells = m_Cells.Clone() as BrushCell[];
  332. BoundsInt oldBounds = new BoundsInt(Vector3Int.zero, m_Size);
  333. foreach (Vector3Int oldPos in oldBounds.allPositionsWithin)
  334. {
  335. int newX = m_Size.x - oldPos.x - 1;
  336. int toIndex = GetCellIndex(newX, oldPos.y, oldPos.z);
  337. int fromIndex = GetCellIndex(oldPos);
  338. m_Cells[toIndex] = oldCells[fromIndex];
  339. }
  340. int newPivotX = m_Size.x - pivot.x - 1;
  341. pivot = new Vector3Int(newPivotX, pivot.y, pivot.z);
  342. Matrix4x4 flip = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, 1f, 1f));
  343. Quaternion orientation = Quaternion.Euler(0f, 0f, -180f);
  344. foreach (BrushCell cell in m_Cells)
  345. {
  346. Vector3 oldOffset = cell.offset;
  347. cell.offset = flip * oldOffset;
  348. cell.orientation = cell.orientation*orientation;
  349. }
  350. }
  351. private void FlipY()
  352. {
  353. BrushCell[] oldCells = m_Cells.Clone() as BrushCell[];
  354. BoundsInt oldBounds = new BoundsInt(Vector3Int.zero, m_Size);
  355. foreach (Vector3Int oldPos in oldBounds.allPositionsWithin)
  356. {
  357. int newY = m_Size.y - oldPos.y - 1;
  358. int toIndex = GetCellIndex(oldPos.x, newY, oldPos.z);
  359. int fromIndex = GetCellIndex(oldPos);
  360. m_Cells[toIndex] = oldCells[fromIndex];
  361. }
  362. int newPivotY = m_Size.y - pivot.y - 1;
  363. pivot = new Vector3Int(pivot.x, newPivotY, pivot.z);
  364. Matrix4x4 flip = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, -1f, 1f));
  365. Quaternion orientation = Quaternion.Euler(0f, 0f, -180f);
  366. foreach (BrushCell cell in m_Cells)
  367. {
  368. Vector3 oldOffset = cell.offset;
  369. cell.offset = flip * oldOffset;
  370. cell.orientation = cell.orientation * orientation;
  371. }
  372. }
  373. /// <summary>Updates the size, pivot and the number of layers of the brush.</summary>
  374. /// <param name="size">New size of the brush.</param>
  375. /// <param name="pivot">New pivot of the brush.</param>
  376. public void UpdateSizeAndPivot(Vector3Int size, Vector3Int pivot)
  377. {
  378. m_Size = size;
  379. m_Pivot = pivot;
  380. SizeUpdated();
  381. }
  382. /// <summary>
  383. /// Sets a GameObject at the position in the brush.
  384. /// </summary>
  385. /// <param name="position">Position to set the GameObject in the brush.</param>
  386. /// <param name="go">GameObject to set in the brush.</param>
  387. public void SetGameObject(Vector3Int position, GameObject go)
  388. {
  389. if (ValidateCellPosition(position))
  390. m_Cells[GetCellIndex(position)].gameObject = go;
  391. }
  392. /// <summary>
  393. /// Sets a position offset at the position in the brush.
  394. /// </summary>
  395. /// <param name="position">Position to set the offset in the brush.</param>
  396. /// <param name="offset">Offset to set in the brush.</param>
  397. public void SetOffset(Vector3Int position, Vector3 offset)
  398. {
  399. if (ValidateCellPosition(position))
  400. m_Cells[GetCellIndex(position)].offset = offset;
  401. }
  402. /// <summary>
  403. /// Sets an orientation at the position in the brush.
  404. /// </summary>
  405. /// <param name="position">Position to set the orientation in the brush.</param>
  406. /// <param name="orientation">Orientation to set in the brush.</param>
  407. public void SetOrientation(Vector3Int position, Quaternion orientation)
  408. {
  409. if (ValidateCellPosition(position))
  410. m_Cells[GetCellIndex(position)].orientation = orientation;
  411. }
  412. /// <summary>
  413. /// Sets a scale at the position in the brush.
  414. /// </summary>
  415. /// <param name="position">Position to set the scale in the brush.</param>
  416. /// <param name="scale">Scale to set in the brush.</param>
  417. public void SetScale(Vector3Int position, Vector3 scale)
  418. {
  419. if (ValidateCellPosition(position))
  420. m_Cells[GetCellIndex(position)].scale = scale;
  421. }
  422. /// <summary>Gets the index to the GameObjectBrush::ref::BrushCell based on the position of the BrushCell.</summary>
  423. /// <param name="brushPosition">Position of the BrushCell.</param>
  424. /// <returns>The cell index for the position of the BrushCell.</returns>
  425. public int GetCellIndex(Vector3Int brushPosition)
  426. {
  427. return GetCellIndex(brushPosition.x, brushPosition.y, brushPosition.z);
  428. }
  429. /// <summary>Gets the index to the GameObjectBrush::ref::BrushCell based on the position of the BrushCell.</summary>
  430. /// <param name="x">X Position of the BrushCell.</param>
  431. /// <param name="y">Y Position of the BrushCell.</param>
  432. /// <param name="z">Z Position of the BrushCell.</param>
  433. /// <returns>The cell index for the position of the BrushCell.</returns>
  434. public int GetCellIndex(int x, int y, int z)
  435. {
  436. return x + m_Size.x * y + m_Size.x * m_Size.y * z;
  437. }
  438. /// <summary>Gets the index to the GameObjectBrush::ref::BrushCell based on the position of the BrushCell.</summary>
  439. /// <param name="x">X Position of the BrushCell.</param>
  440. /// <param name="y">Y Position of the BrushCell.</param>
  441. /// <param name="z">Z Position of the BrushCell.</param>
  442. /// <param name="sizex">X Size of Brush.</param>
  443. /// <param name="sizey">Y Size of Brush.</param>
  444. /// <param name="sizez">Z Size of Brush.</param>
  445. /// <returns>The cell index for the position of the BrushCell.</returns>
  446. public int GetCellIndex(int x, int y, int z, int sizex, int sizey, int sizez)
  447. {
  448. return x + sizex * y + sizex * sizey * z;
  449. }
  450. /// <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>
  451. /// <param name="x">X Position of the BrushCell.</param>
  452. /// <param name="y">Y Position of the BrushCell.</param>
  453. /// <param name="z">Z Position of the BrushCell.</param>
  454. /// <returns>The cell index for the position of the BrushCell.</returns>
  455. public int GetCellIndexWrapAround(int x, int y, int z)
  456. {
  457. return (x % m_Size.x) + m_Size.x * (y % m_Size.y) + m_Size.x * m_Size.y * (z % m_Size.z);
  458. }
  459. private GameObject GetObjectInCell(GridLayout grid, Transform parent, Vector3Int position)
  460. {
  461. int childCount;
  462. GameObject[] sceneChildren = null;
  463. if (parent == null)
  464. {
  465. var scene = SceneManager.GetActiveScene();
  466. sceneChildren = scene.GetRootGameObjects();
  467. childCount = scene.rootCount;
  468. }
  469. else
  470. {
  471. childCount = parent.childCount;
  472. }
  473. var anchorCellOffset = Vector3Int.FloorToInt(m_Anchor);
  474. var cellSize = grid.cellSize;
  475. anchorCellOffset.x = cellSize.x == 0 ? 0 : anchorCellOffset.x;
  476. anchorCellOffset.y = cellSize.y == 0 ? 0 : anchorCellOffset.y;
  477. anchorCellOffset.z = cellSize.z == 0 ? 0 : anchorCellOffset.z;
  478. for (var i = 0; i < childCount; i++)
  479. {
  480. var child = sceneChildren == null ? parent.GetChild(i) : sceneChildren[i].transform;
  481. if (position == grid.WorldToCell(child.position) - anchorCellOffset)
  482. return child.gameObject;
  483. }
  484. return null;
  485. }
  486. private bool ValidateCellPosition(Vector3Int position)
  487. {
  488. var valid =
  489. position.x >= 0 && position.x < size.x &&
  490. position.y >= 0 && position.y < size.y &&
  491. position.z >= 0 && position.z < size.z;
  492. if (!valid)
  493. throw new ArgumentException(string.Format("Position {0} is an invalid cell position. Valid range is between [{1}, {2}).", position, Vector3Int.zero, size));
  494. return true;
  495. }
  496. internal void SizeUpdated(bool keepContents = false)
  497. {
  498. Array.Resize(ref m_Cells, sizeCount);
  499. BoundsInt bounds = new BoundsInt(Vector3Int.zero, m_Size);
  500. foreach (Vector3Int pos in bounds.allPositionsWithin)
  501. {
  502. if (keepContents || m_Cells[GetCellIndex(pos)] == null)
  503. m_Cells[GetCellIndex(pos)] = new BrushCell();
  504. }
  505. }
  506. private static void SetSceneCell(GridLayout grid, Transform parent, Vector3Int position, GameObject go, Vector3 offset, Vector3 scale, Quaternion orientation, Vector3 anchor)
  507. {
  508. if (go == null)
  509. return;
  510. GameObject instance;
  511. if (PrefabUtility.IsPartOfPrefabAsset(go))
  512. {
  513. instance = (GameObject) PrefabUtility.InstantiatePrefab(go, parent != null ? parent.root.gameObject.scene : SceneManager.GetActiveScene());
  514. instance.transform.parent = parent;
  515. }
  516. else
  517. {
  518. instance = Instantiate(go, parent);
  519. instance.name = go.name;
  520. instance.SetActive(true);
  521. foreach (var renderer in instance.GetComponentsInChildren<Renderer>())
  522. {
  523. renderer.enabled = true;
  524. }
  525. }
  526. Undo.RegisterCreatedObjectUndo(instance, "Paint GameObject");
  527. var cellSize = grid.cellSize;
  528. var cellStride = cellSize + grid.cellGap;
  529. cellStride.x = Mathf.Approximately(0f, cellStride.x) ? 1f : cellStride.x;
  530. cellStride.y = Mathf.Approximately(0f, cellStride.y) ? 1f : cellStride.y;
  531. cellStride.z = Mathf.Approximately(0f, cellStride.z) ? 1f : cellStride.z;
  532. var anchorRatio = new Vector3(
  533. anchor.x * cellSize.x / cellStride.x,
  534. anchor.y * cellSize.y / cellStride.y,
  535. anchor.z * cellSize.z / cellStride.z
  536. );
  537. instance.transform.position = grid.LocalToWorld(grid.CellToLocalInterpolated(position + anchorRatio));
  538. instance.transform.localRotation = orientation;
  539. instance.transform.localScale = scale;
  540. instance.transform.Translate(offset);
  541. }
  542. private void ClearSceneCell(GridLayout grid, Transform parent, Vector3Int position)
  543. {
  544. GameObject erased = GetObjectInCell(grid, parent, new Vector3Int(position.x, position.y, position.z));
  545. if (erased != null)
  546. Undo.DestroyObjectImmediate(erased);
  547. }
  548. /// <summary>
  549. /// Hashes the contents of the brush.
  550. /// </summary>
  551. /// <returns>A hash code of the brush</returns>
  552. public override int GetHashCode()
  553. {
  554. int hash = 0;
  555. unchecked
  556. {
  557. foreach (var cell in cells)
  558. {
  559. hash = hash * 33 + cell.GetHashCode();
  560. }
  561. }
  562. return hash;
  563. }
  564. internal void UpdateHiddenGridLayout()
  565. {
  566. var grid = hiddenGrid.GetComponent<Grid>();
  567. hiddenGridLayout.cellSize = grid.cellSize;
  568. hiddenGridLayout.cellGap = grid.cellGap;
  569. hiddenGridLayout.cellSwizzle = grid.cellSwizzle;
  570. hiddenGridLayout.cellLayout = grid.cellLayout;
  571. }
  572. /// <summary>
  573. ///Brush Cell stores the data to be painted in a grid cell.
  574. /// </summary>
  575. [Serializable]
  576. public class BrushCell
  577. {
  578. /// <summary>
  579. /// GameObject to be placed when painting.
  580. /// </summary>
  581. public GameObject gameObject { get { return m_GameObject; } set { m_GameObject = value; } }
  582. /// <summary>
  583. /// Position offset of the GameObject when painted.
  584. /// </summary>
  585. public Vector3 offset { get { return m_Offset; } set { m_Offset = value; } }
  586. /// <summary>
  587. /// Scale of the GameObject when painted.
  588. /// </summary>
  589. public Vector3 scale { get { return m_Scale; } set { m_Scale = value; } }
  590. /// <summary>
  591. /// Orientatio of the GameObject when painted.
  592. /// </summary>
  593. public Quaternion orientation { get { return m_Orientation; } set { m_Orientation = value; } }
  594. [SerializeField]
  595. private GameObject m_GameObject;
  596. [SerializeField]
  597. Vector3 m_Offset = Vector3.zero;
  598. [SerializeField]
  599. Vector3 m_Scale = Vector3.one;
  600. [SerializeField]
  601. Quaternion m_Orientation = Quaternion.identity;
  602. /// <summary>
  603. /// Hashes the contents of the brush cell.
  604. /// </summary>
  605. /// <returns>A hash code of the brush cell.</returns>
  606. public override int GetHashCode()
  607. {
  608. int hash;
  609. unchecked
  610. {
  611. hash = gameObject != null ? gameObject.GetInstanceID() : 0;
  612. hash = hash * 33 + offset.GetHashCode();
  613. hash = hash * 33 + scale.GetHashCode();
  614. hash = hash * 33 + orientation.GetHashCode();
  615. }
  616. return hash;
  617. }
  618. }
  619. }
  620. /// <summary>
  621. /// The Brush Editor for a GameObject Brush.
  622. /// </summary>
  623. [CustomEditor(typeof(GameObjectBrush))]
  624. public class GameObjectBrushEditor : GridBrushEditorBase
  625. {
  626. private bool hiddenGridFoldout;
  627. private Editor hiddenGridEditor;
  628. /// <summary>
  629. /// The GameObjectBrush for this Editor
  630. /// </summary>
  631. public GameObjectBrush brush { get { return target as GameObjectBrush; } }
  632. /// <summary> Whether the GridBrush can change Z Position. </summary>
  633. public override bool canChangeZPosition
  634. {
  635. get { return brush.canChangeZPosition; }
  636. set { brush.canChangeZPosition = value; }
  637. }
  638. /// <summary>
  639. /// Callback for painting the GUI for the GridBrush in the Scene View.
  640. /// The GameObjectBrush Editor overrides this to draw the preview of the brush when drawing lines.
  641. /// </summary>
  642. /// <param name="gridLayout">Grid that the brush is being used on.</param>
  643. /// <param name="brushTarget">Target of the GameObjectBrush::ref::Tool operation. By default the currently selected GameObject.</param>
  644. /// <param name="position">Current selected location of the brush.</param>
  645. /// <param name="tool">Current GameObjectBrush::ref::Tool selected.</param>
  646. /// <param name="executing">Whether brush is being used.</param>
  647. public override void OnPaintSceneGUI(GridLayout gridLayout, GameObject brushTarget, BoundsInt position, GridBrushBase.Tool tool, bool executing)
  648. {
  649. BoundsInt gizmoRect = position;
  650. if (tool == GridBrushBase.Tool.Paint || tool == GridBrushBase.Tool.Erase)
  651. gizmoRect = new BoundsInt(position.min - brush.pivot, brush.size);
  652. base.OnPaintSceneGUI(gridLayout, brushTarget, gizmoRect, tool, executing);
  653. }
  654. /// <summary>
  655. /// Callback for painting the inspector GUI for the GameObjectBrush in the tilemap palette.
  656. /// The GameObjectBrush Editor overrides this to show the usage of this Brush.
  657. /// </summary>
  658. public override void OnPaintInspectorGUI()
  659. {
  660. EditorGUI.BeginChangeCheck();
  661. base.OnInspectorGUI();
  662. if (EditorGUI.EndChangeCheck() && brush.cellCount != brush.sizeCount)
  663. {
  664. brush.SizeUpdated(true);
  665. }
  666. hiddenGridFoldout = EditorGUILayout.Foldout(hiddenGridFoldout, "SceneRoot Grid");
  667. if (hiddenGridFoldout)
  668. {
  669. EditorGUI.indentLevel++;
  670. using (new EditorGUI.DisabledScope(GridPaintingState.scenePaintTarget != brush.hiddenGrid))
  671. {
  672. if (hiddenGridEditor == null)
  673. {
  674. hiddenGridEditor = Editor.CreateEditor(brush.hiddenGrid.GetComponent<Grid>());
  675. }
  676. brush.hiddenGrid.hideFlags = HideFlags.None;
  677. EditorGUI.BeginChangeCheck();
  678. hiddenGridEditor.OnInspectorGUI();
  679. if (EditorGUI.EndChangeCheck())
  680. {
  681. brush.UpdateHiddenGridLayout();
  682. EditorUtility.SetDirty(brush);
  683. SceneView.RepaintAll();
  684. }
  685. brush.hiddenGrid.hideFlags = HideFlags.HideAndDontSave;
  686. }
  687. EditorGUI.indentLevel--;
  688. }
  689. }
  690. /// <summary>
  691. /// The targets that the GameObjectBrush can paint on
  692. /// </summary>
  693. public override GameObject[] validTargets
  694. {
  695. get
  696. {
  697. StageHandle currentStageHandle = StageUtility.GetCurrentStageHandle();
  698. var results = currentStageHandle.FindComponentsOfType<GridLayout>().Where(x =>
  699. {
  700. GameObject gameObject;
  701. return (gameObject = x.gameObject).scene.isLoaded
  702. && gameObject.activeInHierarchy;
  703. }).Select(x => x.gameObject);
  704. return results.Prepend(brush.hiddenGrid).ToArray();
  705. }
  706. }
  707. }
  708. }