using UnityEngine; using System.Collections.Generic; using Lean.Common; using FSA = UnityEngine.Serialization.FormerlySerializedAsAttribute; namespace Lean.Touch { /// This component allows you to define a shape using 2D points. [HelpURL(LeanTouch.PlusHelpUrlPrefix + "LeanShape")] [AddComponentMenu(LeanTouch.ComponentPathPrefix + "Shape")] public class LeanShape : MonoBehaviour { /// Should the start and end points of this shape be connected, forming a loop? public bool ConnectEnds { set { connectEnds = value; } get { return connectEnds; } } [FSA("ConnectEnds")] [SerializeField] private bool connectEnds; /// If you want to visualize the shape, you can specify an output LineRenderer here. public LineRenderer Visual { set { visual = value; } get { return visual; } } [FSA("Visual")] [SerializeField] private LineRenderer visual; /// The points that define the shape. public List Points { get { if (points == null) points = new List(); return points; } } [FSA("Points")] [SerializeField] private List points; public static int Mod(int a, int b) { var m = a % b; return m < 0 ? m + b : m; } public Vector2 GetPoint(int index, bool reverse) { if (points != null && points.Count > 0) { if (reverse == true) { index = points.Count - index - 1; } if (connectEnds == true) { index = Mod(index, points.Count); } else { index = Mathf.Clamp(index, 0, points.Count - 1); } return points[index]; } return default(Vector2); } public void UpdateVisual() { if (visual != null) { if (points != null) { visual.positionCount = points.Count; for (var i = points.Count - 1; i >= 0; i--) { visual.SetPosition(i, points[i]); } if (connectEnds == true) { visual.positionCount += 1; visual.SetPosition(visual.positionCount - 1, points[0]); } } else { visual.positionCount = 0; } } } #if UNITY_EDITOR protected virtual void Start() { UpdateVisual(); } #endif #if UNITY_EDITOR protected virtual void OnValidate() { UpdateVisual(); } #endif #if UNITY_EDITOR protected virtual void OnDrawGizmosSelected() { if (points != null && points.Count > 1) { Gizmos.matrix = transform.localToWorldMatrix; if (connectEnds == true) { for (var i = 0; i < points.Count; i++) { Gizmos.DrawLine(points[i], points[(i + 1) % points.Count]); } } else { for (var i = 1; i < points.Count; i++) { Gizmos.DrawLine(points[i - 1], points[i]); } } } } #endif } } #if UNITY_EDITOR namespace Lean.Touch.Editor { using UnityEditor; using TARGET = LeanShape; [UnityEditor.CanEditMultipleObjects] [UnityEditor.CustomEditor(typeof(TARGET))] public class LeanShape_Editor : LeanEditor { private bool drawing; private int dragging = -1; private static float radius = 5.0f; private List points = new List(); private List scaledPoints = new List(); protected override void OnInspector() { TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts); Draw("connectEnds", "Should the start and end points of this shape be connected, forming a loop?"); Draw("visual", "If you want to visualize the shape, you can specify an output LineRenderer here."); Separator(); if (GUILayout.Button(drawing == true ? "Cancel Drawing" : "Draw") == true) { drawing = !drawing; points.Clear(); } if (drawing == true) { var rect = EditorGUILayout.BeginVertical(); { EditorGUILayout.LabelField(string.Empty, GUILayout.Height(200.0f)); } EditorGUILayout.EndVertical(); GUI.Box(rect, ""); var e = Event.current; if (rect.Contains(e.mousePosition) == true) { var point = e.mousePosition; if (e.type == EventType.MouseDown) { dragging = TryGet(point); if (dragging == -1) { dragging = points.Count; points.Add(point); } Repaint(); } else if (e.type == EventType.MouseMove || e.type == EventType.MouseDrag) { if (dragging >= 0) { points[dragging] = point; Repaint(); } } else if (e.type == EventType.MouseUp) { dragging = -1; } } for (var i = 0; i < points.Count - 1; i++) { Line(points[i], points[i + 1]); } for (var i = 0; i < points.Count; i++) { var point = points[i]; GUI.DrawTexture(new Rect(point.x - 7.0f, point.y - 7.0f, 14.0f, 14.0f), EditorGUIUtility.whiteTexture, ScaleMode.StretchToFill, true, 0.0f, Color.white, 0.0f, 0.0f); GUI.Label(new Rect(point.x - 10.0f, point.y - 10.0f, 20.0f, 20.0f), i.ToString(), EditorStyles.centeredGreyMiniLabel); } radius = EditorGUILayout.FloatField("Radius", radius); if (GUILayout.Button("Use These " + points.Count + " points!") == true) { Undo.RecordObject(tgt, "Shape Points Changed"); tgt.Points.Clear(); tgt.Points.AddRange(ScalePoints()); tgt.UpdateVisual(); EditorUtility.SetDirty(tgt); } } Separator(); Draw("points", "The points that define the shape."); } private List ScalePoints() { var min = points[0]; var max = points[0]; foreach (var point in points) { min = Vector2.Min(min, point); max = Vector2.Max(max, point); } scaledPoints.Clear(); var size = Mathf.Max(max.x - min.x, max.y - min.y) * 0.5f; if (size > 0.0f) { var center = new Vector2((min.x + max.x) * 0.5f, (min.y + max.y) * 0.5f); for (var i = 0; i < points.Count; i++) { var point = points[i] - center; point /= size; point.y = -point.y; scaledPoints.Add(point * radius); } } return scaledPoints; } private static void Line(Vector2 a, Vector2 b, float thickness = 4.0f) { var matrix = GUI.matrix; var vector = b - a; var angle = Mathf.Atan2(vector.y, vector.x) * Mathf.Rad2Deg; GUIUtility.ScaleAroundPivot(new Vector2((b - a).magnitude, thickness), new Vector2(a.x, a.y + 0.5f)); GUIUtility.RotateAroundPivot(angle, a); GUI.DrawTexture(new Rect(a.x, a.y, 1, 1), EditorGUIUtility.whiteTexture, ScaleMode.StretchToFill, true, 0.0f, Color.black, 0.0f, 0.0f); GUI.matrix = matrix; } private int TryGet(Vector2 point, float threshold = 10.0f) { for (var i = 0; i < points.Count; i++) { if (Vector2.Distance(points[i], point) <= threshold) { return i; } } return -1; } } } #endif