123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.Tilemaps;
- using System.Linq;
-
- namespace UnityEditor.Tilemaps
- {
- /// <summary>
- /// This Brush helps draw lines of Tiles onto a Tilemap.
- /// Use this as an example to modify brush painting behaviour to making painting quicker with less actions.
- /// </summary>
- [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@latest/index.html?subfolder=/manual/LineBrush.html")]
- [CustomGridBrush(true, false, false, "Line Brush")]
- public class LineBrush : GridBrush
- {
- /// <summary>
- /// Whether the Line Brush has started drawing a line.
- /// </summary>
- [NonSerialized]
- public bool lineStartActive;
- /// <summary>
- /// Ensures that there are orthogonal connections of Tiles from the start of the line to the end.
- /// </summary>
- public bool fillGaps;
- /// <summary>
- /// The current starting point of the line.
- /// </summary>
- public Vector3Int lineStart = Vector3Int.zero;
-
- /// <summary>
- /// Indicates whether the brush is currently
- /// moving something using the "Move selection with active brush" tool.
- /// </summary>
- public bool IsMoving { get; private set; }
-
- private void OnEnable()
- {
- lineStartActive = false;
- }
-
- /// <summary>
- /// Paints tiles and GameObjects into a given position within the selected layers.
- /// The LineBrush overrides this to provide line painting functionality.
- /// The first paint action sets the starting point of the line.
- /// The next paint action sets the ending point of the line and paints Tile from start to end.
- /// </summary>
- /// <param name="grid">Grid used for layout.</param>
- /// <param name="brushTarget">Target of the paint operation. By default the currently selected GameObject.</param>
- /// <param name="position">The coordinates of the cell to paint data to.</param>
- public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
- {
- if (lineStartActive)
- {
- Vector2Int startPos = new Vector2Int(lineStart.x, lineStart.y);
- Vector2Int endPos = new Vector2Int(position.x, position.y);
- if (startPos == endPos)
- base.Paint(grid, brushTarget, position);
- else
- {
- foreach (var point in GetPointsOnLine(startPos, endPos, fillGaps))
- {
- Vector3Int paintPos = new Vector3Int(point.x, point.y, position.z);
- base.Paint(grid, brushTarget, paintPos);
- }
- }
- lineStartActive = false;
- }
- else if (IsMoving)
- {
- base.Paint(grid, brushTarget, position);
- }
- else
- {
- lineStart = position;
- lineStartActive = true;
- }
- }
-
- /// <summary>
- /// Starts the movement of tiles and GameObjects from a given position within the selected layers.
- /// </summary>
- /// <param name="grid">Grid used for layout.</param>
- /// <param name="brushTarget">Target of the Move operation. By default the currently selected GameObject.</param>
- /// <param name="position">The coordinates of the cell to move data from.</param>
- public override void MoveStart(GridLayout grid, GameObject brushTarget, BoundsInt position)
- {
- base.MoveStart(grid, brushTarget, position);
- IsMoving = true;
- }
-
- /// <summary>
- /// Ends the movement of tiles and GameObjects to a given position within the selected layers.
- /// </summary>
- /// <param name="grid">Grid used for layout.</param>
- /// <param name="brushTarget">Target of the Move operation. By default the currently selected GameObject.</param>
- /// <param name="position">The coordinates of the cell to move data to.</param>
- public override void MoveEnd(GridLayout grid, GameObject brushTarget, BoundsInt position)
- {
- base.MoveEnd(grid, brushTarget, position);
- IsMoving = false;
- }
-
- /// <summary>
- /// Enumerates all the points between the start and end position which are
- /// linked diagonally or orthogonally.
- /// </summary>
- /// <param name="startPos">Start position of the line.</param>
- /// <param name="endPos">End position of the line.</param>
- /// <param name="fillGaps">Fills any gaps between the start and end position so that
- /// all points are linked only orthogonally.</param>
- /// <returns>Returns an IEnumerable which enumerates all the points between the start and end position which are
- /// linked diagonally or orthogonally.</returns>
- public static IEnumerable<Vector2Int> GetPointsOnLine(Vector2Int startPos, Vector2Int endPos, bool fillGaps)
- {
- var points = GetPointsOnLine(startPos, endPos);
- if (fillGaps)
- {
- var rise = endPos.y - startPos.y;
- var run = endPos.x - startPos.x;
-
- if (rise != 0 || run != 0)
- {
- var extraStart = startPos;
- var extraEnd = endPos;
-
-
- if (Mathf.Abs(rise) >= Mathf.Abs(run))
- {
- // up
- if (rise > 0)
- {
- extraStart.y += 1;
- extraEnd.y += 1;
- }
- // down
- else // rise < 0
- {
-
- extraStart.y -= 1;
- extraEnd.y -= 1;
- }
- }
- else // Mathf.Abs(rise) < Mathf.Abs(run)
- {
-
- // right
- if (run > 0)
- {
- extraStart.x += 1;
- extraEnd.x += 1;
- }
- // left
- else // run < 0
- {
- extraStart.x -= 1;
- extraEnd.x -= 1;
- }
- }
-
- var extraPoints = GetPointsOnLine(extraStart, extraEnd);
- extraPoints = extraPoints.Except(new[] { extraEnd });
- points = points.Union(extraPoints);
- }
-
- }
-
- return points;
- }
-
- /// <summary>
- /// Gets an enumerable for all the cells directly between two points
- /// http://ericw.ca/notes/bresenhams-line-algorithm-in-csharp.html
- /// </summary>
- /// <param name="p1">A starting point of a line</param>
- /// <param name="p2">An ending point of a line</param>
- /// <returns>Gets an enumerable for all the cells directly between two points</returns>
- public static IEnumerable<Vector2Int> GetPointsOnLine(Vector2Int p1, Vector2Int p2)
- {
- int x0 = p1.x;
- int y0 = p1.y;
- int x1 = p2.x;
- int y1 = p2.y;
-
- bool steep = Math.Abs(y1 - y0) > Math.Abs(x1 - x0);
- if (steep)
- {
- int t;
- t = x0; // swap x0 and y0
- x0 = y0;
- y0 = t;
- t = x1; // swap x1 and y1
- x1 = y1;
- y1 = t;
- }
- if (x0 > x1)
- {
- int t;
- t = x0; // swap x0 and x1
- x0 = x1;
- x1 = t;
- t = y0; // swap y0 and y1
- y0 = y1;
- y1 = t;
- }
- int dx = x1 - x0;
- int dy = Math.Abs(y1 - y0);
- int error = dx / 2;
- int ystep = (y0 < y1) ? 1 : -1;
- int y = y0;
- for (int x = x0; x <= x1; x++)
- {
- yield return new Vector2Int((steep ? y : x), (steep ? x : y));
- error = error - dy;
- if (error < 0)
- {
- y += ystep;
- error += dx;
- }
- }
- yield break;
- }
- }
-
- /// <summary>
- /// The Brush Editor for a Line Brush.
- /// </summary>
- [CustomEditor(typeof(LineBrush))]
- public class LineBrushEditor : GridBrushEditor
- {
- private static readonly string iconPath = "Packages/com.unity.2d.tilemap.extras/Editor/Brushes/LineBrush/LineBrush.png";
-
- private Texture2D m_BrushIcon;
- private LineBrush lineBrush { get { return target as LineBrush; } }
- private Tilemap lastTilemap;
-
- /// <summary>
- /// Callback for painting the GUI for the GridBrush in the Scene View.
- /// The CoordinateBrush Editor overrides this to draw the preview of the brush when drawing lines.
- /// </summary>
- /// <param name="grid">Grid that the brush is being used on.</param>
- /// <param name="brushTarget">Target of the GridBrushBase::ref::Tool operation. By default the currently selected GameObject.</param>
- /// <param name="position">Current selected location of the brush.</param>
- /// <param name="tool">Current GridBrushBase::ref::Tool selected.</param>
- /// <param name="executing">Whether brush is being used.</param>
- public override void OnPaintSceneGUI(GridLayout grid, GameObject brushTarget, BoundsInt position, GridBrushBase.Tool tool, bool executing)
- {
- base.OnPaintSceneGUI(grid, brushTarget, position, tool, executing);
- if (lineBrush.lineStartActive && brushTarget != null)
- {
- Tilemap tilemap = brushTarget.GetComponent<Tilemap>();
- if (tilemap != null)
- {
- tilemap.ClearAllEditorPreviewTiles();
- lastTilemap = tilemap;
- }
-
- // Draw preview tiles for tilemap
- Vector2Int startPos = new Vector2Int(lineBrush.lineStart.x, lineBrush.lineStart.y);
- Vector2Int endPos = new Vector2Int(position.x, position.y);
- if (startPos == endPos)
- PaintPreview(grid, brushTarget, position.min);
- else
- {
- foreach (var point in LineBrush.GetPointsOnLine(startPos, endPos, lineBrush.fillGaps))
- {
- Vector3Int paintPos = new Vector3Int(point.x, point.y, position.z);
- PaintPreview(grid, brushTarget, paintPos);
- }
- }
-
- if (Event.current.type == EventType.Repaint)
- {
- var min = lineBrush.lineStart;
- var max = lineBrush.lineStart + position.size;
-
- // Draws a box on the picked starting position
- GL.PushMatrix();
- GL.MultMatrix(GUI.matrix);
- GL.Begin(GL.LINES);
- Handles.color = Color.blue;
- Handles.DrawLine(new Vector3(min.x, min.y, min.z), new Vector3(max.x, min.y, min.z));
- Handles.DrawLine(new Vector3(max.x, min.y, min.z), new Vector3(max.x, max.y, min.z));
- Handles.DrawLine(new Vector3(max.x, max.y, min.z), new Vector3(min.x, max.y, min.z));
- Handles.DrawLine(new Vector3(min.x, max.y, min.z), new Vector3(min.x, min.y, min.z));
- GL.End();
- GL.PopMatrix();
- }
- }
- }
-
- /// <summary>
- /// Clears all line previews.
- /// </summary>
- public override void ClearPreview()
- {
- base.ClearPreview();
- if (lastTilemap != null)
- {
- lastTilemap.ClearAllEditorPreviewTiles();
- lastTilemap = null;
- }
- }
-
- /// <summary> Returns an icon identifying the Line Brush. </summary>
- public override Texture2D icon
- {
- get
- {
- if (m_BrushIcon == null)
- {
- var gui = EditorGUIUtility.TrIconContent(iconPath);
- m_BrushIcon = gui.image as Texture2D;
- }
- return m_BrushIcon;
- }
- }
- }
- }
|