暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

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