Geen omschrijving
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.

LineBrush.cs 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Tilemaps;
  5. using System.Linq;
  6. namespace UnityEditor.Tilemaps
  7. {
  8. /// <summary>
  9. /// This Brush helps draw lines of Tiles onto a Tilemap.
  10. /// Use this as an example to modify brush painting behaviour to making painting quicker with less actions.
  11. /// </summary>
  12. [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@latest/index.html?subfolder=/manual/LineBrush.html")]
  13. [CustomGridBrush(true, false, false, "Line Brush")]
  14. public class LineBrush : GridBrush
  15. {
  16. /// <summary>
  17. /// Whether the Line Brush has started drawing a line.
  18. /// </summary>
  19. public bool lineStartActive;
  20. /// <summary>
  21. /// Ensures that there are orthogonal connections of Tiles from the start of the line to the end.
  22. /// </summary>
  23. public bool fillGaps;
  24. /// <summary>
  25. /// The current starting point of the line.
  26. /// </summary>
  27. public Vector3Int lineStart = Vector3Int.zero;
  28. /// <summary>
  29. /// Indicates whether the brush is currently
  30. /// moving something using the "Move selection with active brush" tool.
  31. /// </summary>
  32. public bool IsMoving { get; private set; }
  33. /// <summary>
  34. /// Paints tiles and GameObjects into a given position within the selected layers.
  35. /// The LineBrush overrides this to provide line painting functionality.
  36. /// The first paint action sets the starting point of the line.
  37. /// The next paint action sets the ending point of the line and paints Tile from start to end.
  38. /// </summary>
  39. /// <param name="grid">Grid used for layout.</param>
  40. /// <param name="brushTarget">Target of the paint operation. By default the currently selected GameObject.</param>
  41. /// <param name="position">The coordinates of the cell to paint data to.</param>
  42. public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
  43. {
  44. if (lineStartActive)
  45. {
  46. Vector2Int startPos = new Vector2Int(lineStart.x, lineStart.y);
  47. Vector2Int endPos = new Vector2Int(position.x, position.y);
  48. if (startPos == endPos)
  49. base.Paint(grid, brushTarget, position);
  50. else
  51. {
  52. foreach (var point in GetPointsOnLine(startPos, endPos, fillGaps))
  53. {
  54. Vector3Int paintPos = new Vector3Int(point.x, point.y, position.z);
  55. base.Paint(grid, brushTarget, paintPos);
  56. }
  57. }
  58. lineStartActive = false;
  59. }
  60. else if (IsMoving)
  61. {
  62. base.Paint(grid, brushTarget, position);
  63. }
  64. else
  65. {
  66. lineStart = position;
  67. lineStartActive = true;
  68. }
  69. }
  70. /// <summary>
  71. /// Starts the movement of tiles and GameObjects from a given position within the selected layers.
  72. /// </summary>
  73. /// <param name="grid">Grid used for layout.</param>
  74. /// <param name="brushTarget">Target of the Move operation. By default the currently selected GameObject.</param>
  75. /// <param name="position">The coordinates of the cell to move data from.</param>
  76. public override void MoveStart(GridLayout grid, GameObject brushTarget, BoundsInt position)
  77. {
  78. base.MoveStart(grid, brushTarget, position);
  79. IsMoving = true;
  80. }
  81. /// <summary>
  82. /// Ends the movement of tiles and GameObjects to a given position within the selected layers.
  83. /// </summary>
  84. /// <param name="grid">Grid used for layout.</param>
  85. /// <param name="brushTarget">Target of the Move operation. By default the currently selected GameObject.</param>
  86. /// <param name="position">The coordinates of the cell to move data to.</param>
  87. public override void MoveEnd(GridLayout grid, GameObject brushTarget, BoundsInt position)
  88. {
  89. base.MoveEnd(grid, brushTarget, position);
  90. IsMoving = false;
  91. }
  92. /// <summary>
  93. /// Enumerates all the points between the start and end position which are
  94. /// linked diagonally or orthogonally.
  95. /// </summary>
  96. /// <param name="startPos">Start position of the line.</param>
  97. /// <param name="endPos">End position of the line.</param>
  98. /// <param name="fillGaps">Fills any gaps between the start and end position so that
  99. /// all points are linked only orthogonally.</param>
  100. /// <returns>Returns an IEnumerable which enumerates all the points between the start and end position which are
  101. /// linked diagonally or orthogonally.</returns>
  102. public static IEnumerable<Vector2Int> GetPointsOnLine(Vector2Int startPos, Vector2Int endPos, bool fillGaps)
  103. {
  104. var points = GetPointsOnLine(startPos, endPos);
  105. if (fillGaps)
  106. {
  107. var rise = endPos.y - startPos.y;
  108. var run = endPos.x - startPos.x;
  109. if (rise != 0 || run != 0)
  110. {
  111. var extraStart = startPos;
  112. var extraEnd = endPos;
  113. if (Mathf.Abs(rise) >= Mathf.Abs(run))
  114. {
  115. // up
  116. if (rise > 0)
  117. {
  118. extraStart.y += 1;
  119. extraEnd.y += 1;
  120. }
  121. // down
  122. else // rise < 0
  123. {
  124. extraStart.y -= 1;
  125. extraEnd.y -= 1;
  126. }
  127. }
  128. else // Mathf.Abs(rise) < Mathf.Abs(run)
  129. {
  130. // right
  131. if (run > 0)
  132. {
  133. extraStart.x += 1;
  134. extraEnd.x += 1;
  135. }
  136. // left
  137. else // run < 0
  138. {
  139. extraStart.x -= 1;
  140. extraEnd.x -= 1;
  141. }
  142. }
  143. var extraPoints = GetPointsOnLine(extraStart, extraEnd);
  144. extraPoints = extraPoints.Except(new[] { extraEnd });
  145. points = points.Union(extraPoints);
  146. }
  147. }
  148. return points;
  149. }
  150. /// <summary>
  151. /// Gets an enumerable for all the cells directly between two points
  152. /// http://ericw.ca/notes/bresenhams-line-algorithm-in-csharp.html
  153. /// </summary>
  154. /// <param name="p1">A starting point of a line</param>
  155. /// <param name="p2">An ending point of a line</param>
  156. /// <returns>Gets an enumerable for all the cells directly between two points</returns>
  157. public static IEnumerable<Vector2Int> GetPointsOnLine(Vector2Int p1, Vector2Int p2)
  158. {
  159. int x0 = p1.x;
  160. int y0 = p1.y;
  161. int x1 = p2.x;
  162. int y1 = p2.y;
  163. bool steep = Math.Abs(y1 - y0) > Math.Abs(x1 - x0);
  164. if (steep)
  165. {
  166. int t;
  167. t = x0; // swap x0 and y0
  168. x0 = y0;
  169. y0 = t;
  170. t = x1; // swap x1 and y1
  171. x1 = y1;
  172. y1 = t;
  173. }
  174. if (x0 > x1)
  175. {
  176. int t;
  177. t = x0; // swap x0 and x1
  178. x0 = x1;
  179. x1 = t;
  180. t = y0; // swap y0 and y1
  181. y0 = y1;
  182. y1 = t;
  183. }
  184. int dx = x1 - x0;
  185. int dy = Math.Abs(y1 - y0);
  186. int error = dx / 2;
  187. int ystep = (y0 < y1) ? 1 : -1;
  188. int y = y0;
  189. for (int x = x0; x <= x1; x++)
  190. {
  191. yield return new Vector2Int((steep ? y : x), (steep ? x : y));
  192. error = error - dy;
  193. if (error < 0)
  194. {
  195. y += ystep;
  196. error += dx;
  197. }
  198. }
  199. yield break;
  200. }
  201. }
  202. /// <summary>
  203. /// The Brush Editor for a Line Brush.
  204. /// </summary>
  205. [CustomEditor(typeof(LineBrush))]
  206. public class LineBrushEditor : GridBrushEditor
  207. {
  208. private LineBrush lineBrush { get { return target as LineBrush; } }
  209. private Tilemap lastTilemap;
  210. /// <summary>
  211. /// Callback for painting the GUI for the GridBrush in the Scene View.
  212. /// The CoordinateBrush Editor overrides this to draw the preview of the brush when drawing lines.
  213. /// </summary>
  214. /// <param name="grid">Grid that the brush is being used on.</param>
  215. /// <param name="brushTarget">Target of the GridBrushBase::ref::Tool operation. By default the currently selected GameObject.</param>
  216. /// <param name="position">Current selected location of the brush.</param>
  217. /// <param name="tool">Current GridBrushBase::ref::Tool selected.</param>
  218. /// <param name="executing">Whether brush is being used.</param>
  219. public override void OnPaintSceneGUI(GridLayout grid, GameObject brushTarget, BoundsInt position, GridBrushBase.Tool tool, bool executing)
  220. {
  221. base.OnPaintSceneGUI(grid, brushTarget, position, tool, executing);
  222. if (lineBrush.lineStartActive && brushTarget != null)
  223. {
  224. Tilemap tilemap = brushTarget.GetComponent<Tilemap>();
  225. if (tilemap != null)
  226. {
  227. tilemap.ClearAllEditorPreviewTiles();
  228. lastTilemap = tilemap;
  229. }
  230. // Draw preview tiles for tilemap
  231. Vector2Int startPos = new Vector2Int(lineBrush.lineStart.x, lineBrush.lineStart.y);
  232. Vector2Int endPos = new Vector2Int(position.x, position.y);
  233. if (startPos == endPos)
  234. PaintPreview(grid, brushTarget, position.min);
  235. else
  236. {
  237. foreach (var point in LineBrush.GetPointsOnLine(startPos, endPos, lineBrush.fillGaps))
  238. {
  239. Vector3Int paintPos = new Vector3Int(point.x, point.y, position.z);
  240. PaintPreview(grid, brushTarget, paintPos);
  241. }
  242. }
  243. if (Event.current.type == EventType.Repaint)
  244. {
  245. var min = lineBrush.lineStart;
  246. var max = lineBrush.lineStart + position.size;
  247. // Draws a box on the picked starting position
  248. GL.PushMatrix();
  249. GL.MultMatrix(GUI.matrix);
  250. GL.Begin(GL.LINES);
  251. Handles.color = Color.blue;
  252. Handles.DrawLine(new Vector3(min.x, min.y, min.z), new Vector3(max.x, min.y, min.z));
  253. Handles.DrawLine(new Vector3(max.x, min.y, min.z), new Vector3(max.x, max.y, min.z));
  254. Handles.DrawLine(new Vector3(max.x, max.y, min.z), new Vector3(min.x, max.y, min.z));
  255. Handles.DrawLine(new Vector3(min.x, max.y, min.z), new Vector3(min.x, min.y, min.z));
  256. GL.End();
  257. GL.PopMatrix();
  258. }
  259. }
  260. }
  261. /// <summary>
  262. /// Clears all line previews.
  263. /// </summary>
  264. public override void ClearPreview()
  265. {
  266. base.ClearPreview();
  267. if (lastTilemap != null)
  268. {
  269. lastTilemap.ClearAllEditorPreviewTiles();
  270. lastTilemap = null;
  271. }
  272. }
  273. }
  274. }