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.

RandomBrush.cs 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.Tilemaps;
  6. namespace UnityEditor.Tilemaps
  7. {
  8. /// <summary>
  9. /// This Brush helps to place random Tiles onto a Tilemap.
  10. /// Use this as an example to create brushes which store specific data per brush and to make brushes which randomize behaviour.
  11. /// </summary>
  12. [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@latest/index.html?subfolder=/manual/RandomBrush.html")]
  13. [CustomGridBrush(false, false, false, "Random Brush")]
  14. public class RandomBrush : GridBrush
  15. {
  16. internal struct SizeEnumerator : IEnumerator<Vector3Int>
  17. {
  18. private readonly Vector3Int _min, _max, _delta;
  19. private Vector3Int _current;
  20. public SizeEnumerator(Vector3Int min, Vector3Int max, Vector3Int delta)
  21. {
  22. _min = _current = min;
  23. _max = max;
  24. _delta = delta;
  25. Reset();
  26. }
  27. public SizeEnumerator GetEnumerator()
  28. {
  29. return this;
  30. }
  31. public bool MoveNext()
  32. {
  33. if (_current.z >= _max.z)
  34. return false;
  35. _current.x += _delta.x;
  36. if (_current.x >= _max.x)
  37. {
  38. _current.x = _min.x;
  39. _current.y += _delta.y;
  40. if (_current.y >= _max.y)
  41. {
  42. _current.y = _min.y;
  43. _current.z += _delta.z;
  44. if (_current.z >= _max.z)
  45. return false;
  46. }
  47. }
  48. return true;
  49. }
  50. public void Reset()
  51. {
  52. _current = _min;
  53. _current.x -= _delta.x;
  54. }
  55. public Vector3Int Current { get { return _current; } }
  56. object IEnumerator.Current { get { return Current; } }
  57. void IDisposable.Dispose() {}
  58. }
  59. /// <summary>
  60. /// A data structure for storing a set of Tiles used for randomization
  61. /// </summary>
  62. [Serializable]
  63. public struct RandomTileSet
  64. {
  65. /// <summary>
  66. /// A set of tiles to be painted as a set
  67. /// </summary>
  68. public TileBase[] randomTiles;
  69. }
  70. /// <summary>
  71. /// The size of a RandomTileSet
  72. /// </summary>
  73. public Vector3Int randomTileSetSize = Vector3Int.one;
  74. /// <summary>
  75. /// An array of RandomTileSets to choose from when randomizing
  76. /// </summary>
  77. public RandomTileSet[] randomTileSets;
  78. /// <summary>
  79. /// A flag to determine if picking will add new RandomTileSets
  80. /// </summary>
  81. public bool pickRandomTiles;
  82. /// <summary>
  83. /// A flag to determine if picking will add to existing RandomTileSets
  84. /// </summary>
  85. public bool addToRandomTiles;
  86. /// <summary>
  87. /// Paints RandomTileSets into a given position within the selected layers.
  88. /// The RandomBrush overrides this to provide randomized painting functionality.
  89. /// </summary>
  90. /// <param name="grid">Grid used for layout.</param>
  91. /// <param name="brushTarget">Target of the paint operation. By default the currently selected GameObject.</param>
  92. /// <param name="position">The coordinates of the cell to paint data to.</param>
  93. public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
  94. {
  95. if (randomTileSets != null && randomTileSets.Length > 0)
  96. {
  97. if (brushTarget == null)
  98. return;
  99. var tilemap = brushTarget.GetComponent<Tilemap>();
  100. if (tilemap == null)
  101. return;
  102. Vector3Int min = position - pivot;
  103. foreach (var startLocation in new SizeEnumerator(min, min + size, randomTileSetSize))
  104. {
  105. var randomTileSet = randomTileSets[(int) (randomTileSets.Length * UnityEngine.Random.value)];
  106. var randomBounds = new BoundsInt(startLocation, randomTileSetSize);
  107. tilemap.SetTilesBlock(randomBounds, randomTileSet.randomTiles);
  108. }
  109. }
  110. else
  111. {
  112. base.Paint(grid, brushTarget, position);
  113. }
  114. }
  115. /// <summary>
  116. /// Picks RandomTileSets given the coordinates of the cells.
  117. /// The RandomBrush overrides this to provide picking functionality for RandomTileSets.
  118. /// </summary>
  119. /// <param name="gridLayout">Grid to pick data from.</param>
  120. /// <param name="brushTarget">Target of the picking operation. By default the currently selected GameObject.</param>
  121. /// <param name="bounds">The coordinates of the cells to paint data from.</param>
  122. /// <param name="pickStart">Pivot of the picking brush.</param>
  123. public override void Pick(GridLayout gridLayout, GameObject brushTarget, BoundsInt bounds, Vector3Int pickStart)
  124. {
  125. base.Pick(gridLayout, brushTarget, bounds, pickStart);
  126. if (!pickRandomTiles)
  127. return;
  128. Tilemap tilemap = brushTarget.GetComponent<Tilemap>();
  129. if (tilemap == null)
  130. return;
  131. int i = 0;
  132. int count = ((bounds.size.x + randomTileSetSize.x - 1) / randomTileSetSize.x)
  133. * ((bounds.size.y + randomTileSetSize.y - 1) / randomTileSetSize.y)
  134. * ((bounds.size.z + randomTileSetSize.z - 1) / randomTileSetSize.z);
  135. if (addToRandomTiles)
  136. {
  137. i = randomTileSets != null ? randomTileSets.Length : 0;
  138. count += i;
  139. }
  140. Array.Resize(ref randomTileSets, count);
  141. foreach (var startLocation in new SizeEnumerator(bounds.min, bounds.max, randomTileSetSize))
  142. {
  143. randomTileSets[i].randomTiles = new TileBase[randomTileSetSize.x * randomTileSetSize.y * randomTileSetSize.z];
  144. var randomBounds = new BoundsInt(startLocation, randomTileSetSize);
  145. int j = 0;
  146. foreach (Vector3Int pos in randomBounds.allPositionsWithin)
  147. {
  148. var tile = (pos.x < bounds.max.x && pos.y < bounds.max.y && pos.z < bounds.max.z)
  149. ? tilemap.GetTile(pos)
  150. : null;
  151. randomTileSets[i].randomTiles[j++] = tile;
  152. }
  153. i++;
  154. }
  155. }
  156. }
  157. /// <summary>
  158. /// The Brush Editor for a Random Brush.
  159. /// </summary>
  160. [CustomEditor(typeof(RandomBrush))]
  161. public class RandomBrushEditor : GridBrushEditor
  162. {
  163. private RandomBrush randomBrush { get { return target as RandomBrush; } }
  164. private GameObject lastBrushTarget;
  165. /// <summary>
  166. /// Paints preview data into a cell of a grid given the coordinates of the cell.
  167. /// The RandomBrush Editor overrides this to draw the preview of the brush for RandomTileSets
  168. /// </summary>
  169. /// <param name="grid">Grid to paint data to.</param>
  170. /// <param name="brushTarget">Target of the paint operation. By default the currently selected GameObject.</param>
  171. /// <param name="position">The coordinates of the cell to paint data to.</param>
  172. public override void PaintPreview(GridLayout grid, GameObject brushTarget, Vector3Int position)
  173. {
  174. if (randomBrush.randomTileSets != null && randomBrush.randomTileSets.Length > 0)
  175. {
  176. base.PaintPreview(grid, null, position);
  177. if (brushTarget == null)
  178. return;
  179. var tilemap = brushTarget.GetComponent<Tilemap>();
  180. if (tilemap == null)
  181. return;
  182. Vector3Int min = position - randomBrush.pivot;
  183. foreach (var startLocation in new RandomBrush.SizeEnumerator(min, min + randomBrush.size, randomBrush.randomTileSetSize))
  184. {
  185. var randomTileSet = randomBrush.randomTileSets[(int) (randomBrush.randomTileSets.Length * UnityEngine.Random.value)];
  186. var randomBounds = new BoundsInt(startLocation, randomBrush.randomTileSetSize);
  187. int j = 0;
  188. foreach (Vector3Int pos in randomBounds.allPositionsWithin)
  189. {
  190. tilemap.SetEditorPreviewTile(pos, randomTileSet.randomTiles[j++]);
  191. }
  192. }
  193. lastBrushTarget = brushTarget;
  194. }
  195. else
  196. {
  197. base.PaintPreview(grid, brushTarget, position);
  198. }
  199. }
  200. /// <summary>
  201. /// Clears all RandomTileSet previews.
  202. /// </summary>
  203. public override void ClearPreview()
  204. {
  205. if (lastBrushTarget != null)
  206. {
  207. var tilemap = lastBrushTarget.GetComponent<Tilemap>();
  208. if (tilemap == null)
  209. return;
  210. tilemap.ClearAllEditorPreviewTiles();
  211. lastBrushTarget = null;
  212. }
  213. else
  214. {
  215. base.ClearPreview();
  216. }
  217. }
  218. /// <summary>
  219. /// Callback for painting the inspector GUI for the RandomBrush in the Tile Palette.
  220. /// The RandomBrush Editor overrides this to have a custom inspector for this Brush.
  221. /// </summary>
  222. public override void OnPaintInspectorGUI()
  223. {
  224. EditorGUI.BeginChangeCheck();
  225. randomBrush.pickRandomTiles = EditorGUILayout.Toggle("Pick Random Tiles", randomBrush.pickRandomTiles);
  226. using (new EditorGUI.DisabledScope(!randomBrush.pickRandomTiles))
  227. {
  228. randomBrush.addToRandomTiles = EditorGUILayout.Toggle("Add To Random Tiles", randomBrush.addToRandomTiles);
  229. }
  230. EditorGUI.BeginChangeCheck();
  231. randomBrush.randomTileSetSize = EditorGUILayout.Vector3IntField("Tile Set Size", randomBrush.randomTileSetSize);
  232. if (EditorGUI.EndChangeCheck())
  233. {
  234. for (int i = 0; i < randomBrush.randomTileSets.Length; ++i)
  235. {
  236. int sizeCount = randomBrush.randomTileSetSize.x * randomBrush.randomTileSetSize.y *
  237. randomBrush.randomTileSetSize.z;
  238. randomBrush.randomTileSets[i].randomTiles = new TileBase[sizeCount];
  239. }
  240. }
  241. int randomTileSetCount = EditorGUILayout.DelayedIntField("Number of Tiles", randomBrush.randomTileSets != null ? randomBrush.randomTileSets.Length : 0);
  242. if (randomTileSetCount < 0)
  243. randomTileSetCount = 0;
  244. if (randomBrush.randomTileSets == null || randomBrush.randomTileSets.Length != randomTileSetCount)
  245. {
  246. Array.Resize(ref randomBrush.randomTileSets, randomTileSetCount);
  247. for (int i = 0; i < randomBrush.randomTileSets.Length; ++i)
  248. {
  249. int sizeCount = randomBrush.randomTileSetSize.x * randomBrush.randomTileSetSize.y *
  250. randomBrush.randomTileSetSize.z;
  251. if (randomBrush.randomTileSets[i].randomTiles == null
  252. || randomBrush.randomTileSets[i].randomTiles.Length != sizeCount)
  253. randomBrush.randomTileSets[i].randomTiles = new TileBase[sizeCount];
  254. }
  255. }
  256. if (randomTileSetCount > 0)
  257. {
  258. EditorGUILayout.Space();
  259. EditorGUILayout.LabelField("Place random tiles.");
  260. for (int i = 0; i < randomTileSetCount; i++)
  261. {
  262. EditorGUILayout.LabelField("Tile Set " + (i+1));
  263. for (int j = 0; j < randomBrush.randomTileSets[i].randomTiles.Length; ++j)
  264. {
  265. randomBrush.randomTileSets[i].randomTiles[j] = (TileBase) EditorGUILayout.ObjectField("Tile " + (j+1), randomBrush.randomTileSets[i].randomTiles[j], typeof(TileBase), false, null);
  266. }
  267. }
  268. }
  269. if (EditorGUI.EndChangeCheck())
  270. EditorUtility.SetDirty(randomBrush);
  271. }
  272. }
  273. }