Did you ever wish there was a Debug.DrawArrow()? I did. Now I made one and I'd like to share it with you guys. This is useful for showing debug of a direction. Visualizing head and camera rotation: Visualizing spawn points: Code (csharp): using UnityEngine; using System.Collections; public static class DrawArrow { public static void ForGizmo(Vector3 pos, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Gizmos.DrawRay(pos, direction); Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180+arrowHeadAngle,0) * new Vector3(0,0,1); Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180-arrowHeadAngle,0) * new Vector3(0,0,1); Gizmos.DrawRay(pos + direction, right * arrowHeadLength); Gizmos.DrawRay(pos + direction, left * arrowHeadLength); } public static void ForGizmo(Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Gizmos.color = color; Gizmos.DrawRay(pos, direction); Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180+arrowHeadAngle,0) * new Vector3(0,0,1); Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180-arrowHeadAngle,0) * new Vector3(0,0,1); Gizmos.DrawRay(pos + direction, right * arrowHeadLength); Gizmos.DrawRay(pos + direction, left * arrowHeadLength); } public static void ForDebug(Vector3 pos, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Debug.DrawRay(pos, direction); Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180+arrowHeadAngle,0) * new Vector3(0,0,1); Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180-arrowHeadAngle,0) * new Vector3(0,0,1); Debug.DrawRay(pos + direction, right * arrowHeadLength); Debug.DrawRay(pos + direction, left * arrowHeadLength); } public static void ForDebug(Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Debug.DrawRay(pos, direction, color); Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180+arrowHeadAngle,0) * new Vector3(0,0,1); Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180-arrowHeadAngle,0) * new Vector3(0,0,1); Debug.DrawRay(pos + direction, right * arrowHeadLength, color); Debug.DrawRay(pos + direction, left * arrowHeadLength, color); } } The code works very much like DrawRay(), at the very least, it requires a position and direction. To specify length, multiply a number to your direction vector.
Thank you for your script. Version with 4-rays arrow ends - useful for 2D: Code (csharp): using UnityEngine; using System.Collections; public static class DrawArrow { public static void ForGizmo (Vector3 pos, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Gizmos.DrawRay (pos, direction); DrawArrowEnd(true, pos, direction, Gizmos.color, arrowHeadLength, arrowHeadAngle); } public static void ForGizmo (Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Gizmos.DrawRay (pos, direction); DrawArrowEnd(true, pos, direction, color, arrowHeadLength, arrowHeadAngle); } public static void ForDebug (Vector3 pos, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Debug.DrawRay (pos, direction); DrawArrowEnd(false, pos, direction, Gizmos.color, arrowHeadLength, arrowHeadAngle); } public static void ForDebug (Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Debug.DrawRay (pos, direction, color); DrawArrowEnd(false, pos, direction, color, arrowHeadLength, arrowHeadAngle); } private static void DrawArrowEnd (bool gizmos, Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Vector3 right = Quaternion.LookRotation (direction) * Quaternion.Euler (arrowHeadAngle, 0, 0) * Vector3.back; Vector3 left = Quaternion.LookRotation (direction) * Quaternion.Euler (-arrowHeadAngle, 0, 0) * Vector3.back; Vector3 up = Quaternion.LookRotation (direction) * Quaternion.Euler (0, arrowHeadAngle, 0) * Vector3.back; Vector3 down = Quaternion.LookRotation (direction) * Quaternion.Euler (0, -arrowHeadAngle, 0) * Vector3.back; if (gizmos) { Gizmos.color = color; Gizmos.DrawRay (pos + direction, right * arrowHeadLength); Gizmos.DrawRay (pos + direction, left * arrowHeadLength); Gizmos.DrawRay (pos + direction, up * arrowHeadLength); Gizmos.DrawRay (pos + direction, down * arrowHeadLength); } else { Debug.DrawRay (pos + direction, right * arrowHeadLength, color); Debug.DrawRay (pos + direction, left * arrowHeadLength, color); Debug.DrawRay (pos + direction, up * arrowHeadLength, color); Debug.DrawRay (pos + direction, down * arrowHeadLength, color); } } }
Sometimes you don't want arrows on the end of the line, you want them 50 % (or any %) along the line. If so, this modification lets you do that: Code (CSharp): using UnityEngine; /// <summary> /// Based on https://forum.unity3d.com/threads/debug-drawarrow.85980/ /// </summary> public static class DrawArrow { public static void ForGizmo(Vector3 pos, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { ForGizmo(pos, direction, Gizmos.color, arrowHeadLength, arrowHeadAngle, arrowPosition); } public static void ForGizmo(Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { Gizmos.color = color; Gizmos.DrawRay(pos, direction); DrawArrowEnd(true, pos, direction, color, arrowHeadLength, arrowHeadAngle, arrowPosition); } public static void ForDebug(Vector3 pos, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { ForDebug(pos, direction, Color.white, arrowHeadLength, arrowHeadAngle, arrowPosition); } public static void ForDebug(Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { Debug.DrawRay(pos, direction, color); DrawArrowEnd(false, pos, direction, color, arrowHeadLength, arrowHeadAngle, arrowPosition); } private static void DrawArrowEnd(bool gizmos, Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { Vector3 right = (Quaternion.LookRotation(direction) * Quaternion.Euler(arrowHeadAngle, 0, 0) * Vector3.back) * arrowHeadLength; Vector3 left = (Quaternion.LookRotation(direction) * Quaternion.Euler(-arrowHeadAngle, 0, 0) * Vector3.back) * arrowHeadLength; Vector3 up = (Quaternion.LookRotation(direction) * Quaternion.Euler(0, arrowHeadAngle, 0) * Vector3.back) * arrowHeadLength; Vector3 down = (Quaternion.LookRotation(direction) * Quaternion.Euler(0, -arrowHeadAngle, 0) * Vector3.back) * arrowHeadLength; Vector3 arrowTip = pos + (direction*arrowPosition); if (gizmos) { Gizmos.color = color; Gizmos.DrawRay(arrowTip, right); Gizmos.DrawRay(arrowTip, left); Gizmos.DrawRay(arrowTip, up); Gizmos.DrawRay(arrowTip, down); } else { Debug.DrawRay(arrowTip, right, color); Debug.DrawRay(arrowTip, left, color); Debug.DrawRay(arrowTip, up, color); Debug.DrawRay(arrowTip, down, color); } } }
This is great work! I've added to it a bit. I wanted to encapsulate everything OnDrawGizmos within this one class so that other classes can use all drawable capabilities (including handles) without having to implement OnDrawGizmos. I needed to utilize a singleton pattern instead of static. Rather than call DrawArrow from anywhere, I add it to an empty object in-scene and let everyone call its singleton instance (see http://wiki.unity3d.com/index.php/Singleton). This way, DrawArrow can call OnDrawGizmos. To manage rerendering, I created a dictionary of lines to be drawn. Memory allocation is heavier this way, but I figure I only need this for a few lines at a time. With this in place, labels to display arrow magnitude are much easier. Code (CSharp): using UnityEngine; using System.Collections; using UnityEditor; using System.Collections.Generic; public class DrawArrow : Singleton<DrawArrow> { Dictionary<string, DebugLineData> linesToDraw; List<string> keysToUpdate; private void Awake() { linesToDraw = new Dictionary<string, DebugLineData>(); keysToUpdate = new List<string>(); } public void ForGizmo(Vector3 pos, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Gizmos.DrawRay(pos, direction); Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * new Vector3(0, 0, 1); Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * new Vector3(0, 0, 1); Gizmos.DrawRay(pos + direction, right * arrowHeadLength); Gizmos.DrawRay(pos + direction, left * arrowHeadLength); } public void ForGizmo(Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Gizmos.color = color; Gizmos.DrawRay(pos, direction); Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * new Vector3(0, 0, 1); Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * new Vector3(0, 0, 1); Gizmos.DrawRay(pos + direction, right * arrowHeadLength); Gizmos.DrawRay(pos + direction, left * arrowHeadLength); } public void ForGizmoWithMagnitude(string key, Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { DebugLineData lineData = new DebugLineData(); lineData.drawing = true; lineData.pos = pos; lineData.direction = direction; lineData.color = color; lineData.arrowHeadLength = arrowHeadLength; lineData.arrowHeadAngle = arrowHeadAngle; if (!linesToDraw.ContainsKey(key)) linesToDraw.Add(key, lineData); else linesToDraw[key] = lineData; } private void ForGizmoWithMagnitudeInternal(DebugLineData lineData) { Gizmos.color = lineData.color; Gizmos.DrawRay(lineData.pos, lineData.direction); Vector3 right = Quaternion.LookRotation(lineData.direction) * Quaternion.Euler(0, 180 + lineData.arrowHeadAngle, 0) * new Vector3(0, 0, 1); Vector3 left = Quaternion.LookRotation(lineData.direction) * Quaternion.Euler(0, 180 - lineData.arrowHeadAngle, 0) * new Vector3(0, 0, 1); Gizmos.DrawRay(lineData.pos + lineData.direction, right * lineData.arrowHeadLength); Gizmos.DrawRay(lineData.pos + lineData.direction, left * lineData.arrowHeadLength); Vector3 midPoint = lineData.pos + 0.5f * lineData.direction + new Vector3(0, 0.1f, 0); string magnitude = lineData.direction.magnitude.ToString(); Handles.Label(midPoint, magnitude); } public void ForDebug(Vector3 pos, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Debug.DrawRay(pos, direction); Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * new Vector3(0, 0, 1); Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * new Vector3(0, 0, 1); Debug.DrawRay(pos + direction, right * arrowHeadLength); Debug.DrawRay(pos + direction, left * arrowHeadLength); } public void ForDebug(Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Debug.DrawRay(pos, direction, color); Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * new Vector3(0, 0, 1); Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * new Vector3(0, 0, 1); Debug.DrawRay(pos + direction, right * arrowHeadLength, color); Debug.DrawRay(pos + direction, left * arrowHeadLength, color); } private void OnDrawGizmos() { if (linesToDraw != null && linesToDraw.Count > 0) { foreach (KeyValuePair<string, DebugLineData> lineDataPair in linesToDraw) if (lineDataPair.Value.drawing) { ForGizmoWithMagnitudeInternal(lineDataPair.Value); keysToUpdate.Add(lineDataPair.Key); } for (int i = 0; i < keysToUpdate.Count; i++) { DebugLineData lineData = linesToDraw[keysToUpdate[i]]; lineData.drawing = false; linesToDraw[keysToUpdate[i]] = lineData; } if (keysToUpdate.Count > 0) keysToUpdate.Clear(); } } } public struct DebugLineData { public bool drawing; public Vector3 pos; public Vector3 direction; public Color color; public float arrowHeadLength; public float arrowHeadAngle; }
You will find all built-in gizmos, written in the Debug.DrawLine style in the DebugPlus asset, currently in beta. See here !
Added Handles support Code (CSharp): using UnityEditor; using UnityEngine; public static class DrawArrow { public static void ForHandle(in Vector3 pos, in Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Arrow(TargetType.Handle, pos, direction, Handles.color, arrowHeadLength, arrowHeadAngle); } public static void ForHandle(in Vector3 pos, in Vector3 direction, in Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Arrow(TargetType.Handle, pos, direction, color, arrowHeadLength, arrowHeadAngle); } public static void ForGizmo(in Vector3 pos, in Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Arrow(TargetType.Gizmo, pos, direction, Gizmos.color, arrowHeadLength, arrowHeadAngle); } public static void ForGizmo(in Vector3 pos, in Vector3 direction, in Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Arrow(TargetType.Gizmo, pos, direction, color, arrowHeadLength, arrowHeadAngle); } public static void ForDebug(in Vector3 pos, in Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Debug.DrawRay(pos, direction); Arrow(TargetType.Debug, pos, direction, Gizmos.color, arrowHeadLength, arrowHeadAngle); } public static void ForDebug(in Vector3 pos, in Vector3 direction, in Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { Debug.DrawRay(pos, direction, color); Arrow(TargetType.Debug, pos, direction, color, arrowHeadLength, arrowHeadAngle); } private static void Arrow(TargetType targetType, in Vector3 pos, in Vector3 direction, in Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { var right = Quaternion.LookRotation(direction) * Quaternion.Euler(arrowHeadAngle, 0, 0) * Vector3.back * arrowHeadLength; var left = Quaternion.LookRotation(direction) * Quaternion.Euler(-arrowHeadAngle, 0, 0) * Vector3.back * arrowHeadLength; var up = Quaternion.LookRotation(direction) * Quaternion.Euler(0, arrowHeadAngle, 0) * Vector3.back * arrowHeadLength; var down = Quaternion.LookRotation(direction) * Quaternion.Euler(0, -arrowHeadAngle, 0) * Vector3.back * arrowHeadLength; var end = pos + direction; Color colorPrew; switch (targetType) { case TargetType.Gizmo: colorPrew = Gizmos.color; Gizmos.color = color; Gizmos.DrawRay(pos, direction); Gizmos.DrawRay(end, right); Gizmos.DrawRay(end, left); Gizmos.DrawRay(end, up); Gizmos.DrawRay(end, down); Gizmos.color = colorPrew; break; case TargetType.Debug: Debug.DrawRay(end, right, color); Debug.DrawRay(end, left, color); Debug.DrawRay(end, up, color); Debug.DrawRay(end, down, color); break; case TargetType.Handle: colorPrew = Handles.color; Handles.color = color; Handles.DrawLine(pos, end); Handles.DrawLine(end, end + right); Handles.DrawLine(end, end + left); Handles.DrawLine(end, end + up); Handles.DrawLine(end, end + down); Handles.color = colorPrew; break; } } private enum TargetType { Gizmo, Debug, Handle } }
Based on the work of Bomadeno, added support for drawing arrows between two points Code (CSharp): using UnityEngine; /// <summary> /// Based on https://forum.unity3d.com/threads/debug-drawarrow.85980/ /// </summary> public static class DrawArrow { public static void ForGizmo(Vector3 pos, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { ForGizmo(pos, direction, Gizmos.color, arrowHeadLength, arrowHeadAngle, arrowPosition); } public static void ForGizmoTwoPoints(Vector3 from, Vector3 to, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { ForGizmoTwoPoints(from, to, Gizmos.color, arrowHeadLength, arrowHeadAngle, arrowPosition); } public static void ForGizmo(Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { Gizmos.color = color; Gizmos.DrawRay(pos, direction); DrawArrowEnd(true, pos, direction, color, arrowHeadLength, arrowHeadAngle, arrowPosition); } public static void ForGizmoTwoPoints(Vector3 from, Vector3 to, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { Gizmos.DrawLine(from, to); Vector3 direction = to - from; DrawArrowEnd(true, from, direction, color, arrowHeadLength, arrowHeadAngle, arrowPosition); } public static void ForDebug(Vector3 pos, Vector3 direction, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { ForDebug(pos, direction, Color.white, arrowHeadLength, arrowHeadAngle, arrowPosition); } public static void ForDebug(Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { Debug.DrawRay(pos, direction, color); DrawArrowEnd(false, pos, direction, color, arrowHeadLength, arrowHeadAngle, arrowPosition); } private static void DrawArrowEnd(bool gizmos, Vector3 pos, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f, float arrowPosition = 0.5f) { Vector3 right = (Quaternion.LookRotation(direction) * Quaternion.Euler(arrowHeadAngle, 0, 0) * Vector3.back) * arrowHeadLength; Vector3 left = (Quaternion.LookRotation(direction) * Quaternion.Euler(-arrowHeadAngle, 0, 0) * Vector3.back) * arrowHeadLength; Vector3 up = (Quaternion.LookRotation(direction) * Quaternion.Euler(0, arrowHeadAngle, 0) * Vector3.back) * arrowHeadLength; Vector3 down = (Quaternion.LookRotation(direction) * Quaternion.Euler(0, -arrowHeadAngle, 0) * Vector3.back) * arrowHeadLength; Vector3 arrowTip = pos + (direction * arrowPosition); if (gizmos) { Gizmos.color = color; Gizmos.DrawRay(arrowTip, right); Gizmos.DrawRay(arrowTip, left); Gizmos.DrawRay(arrowTip, up); Gizmos.DrawRay(arrowTip, down); } else { Debug.DrawRay(arrowTip, right, color); Debug.DrawRay(arrowTip, left, color); Debug.DrawRay(arrowTip, up, color); Debug.DrawRay(arrowTip, down, color); } } }
This is all the stuff not many care about. So I came to this thread looking for answers on how to do this. I couldn't fully figure out the mathematics at first and this thread lead me to reading more on Quaternions & Vectors as Directions. Some maths I had to pick up elsewhere when I didn't know it or recall it. This stuff is pretty cool and I want to leave something here for those who can't spot some of the maths and can't tell what's going on. Some quick things to know for those who don't know what's going on yet also want cool arrow gizmo stuff: Getting a Direction from two known Vector Points is: Direction = EndVector - StartVector This provides the difference between the start and end point for Unity do more maths with. Getting a Point along a distance of a vector is: PointAlongLine = Start + (Direction * Distance) Adding Direction multiplied by Distance to the start vector gives us the desired point along the line between the Start and End vectors because we're adding a fraction of the difference between the two vectors. Getting a Point from a Direction is: End = Start + (Direction [normalized] * distance) [a, b] This gives us the direct position to work with as we're adding the normalized direction of the arrow multiplied by the distance. Useful for changing a position and a direction to two positions[c] for DrawLine() in Gizmos, Handles and Debug namespaces. a. Getting a point from a direction is simplified to [End = Origin + Direction] if you already normalized and multiplied by distance. b. Distance is normally between 0f and 1f. It's possible to go beyond but this will draw the arrow off the line. c. Using positions can be frivolous to some but others who start out with vector maths in Unity may know positions. Method Values Vector3 a - Start Position (e.g. gameObject.transform.position) Vector3 b - End Position float arrowheadAngle - Angle between arrowhead lines float arrowheadDistance - Distance to draw the arrow between the start and end. float arrowheadLength - Length of the arrowhead lines 2D Gizmo Arrow Method Code (CSharp): private void DrawArrow(Vector3 a, Vector3 b, float arrowheadAngle, float arrowheadDistance, float arrowheadLength) { // Get the Direction of the Vector Vector3 dir = b - a; // Get the Position of the Arrowhead along the length of the line. Vector3 arrowPos = a + (dir * arrowheadDistance); // Get the Arrowhead Lines using the direction from earlier multiplied by a vector representing half of the full angle of the arrowhead (y) // and -1 for going backwards instead of forwards (z), which is then multiplied by the desired length of the arrowhead lines coming from the point. Vector3 up = Quaternion.LookRotation(dir) * new Vector3(0f, Mathf.Sin(arrowheadAngle* Mathf.Deg2Rad), -1f) * arrowheadLength; Vector3 down = Quaternion.LookRotation(dir) * new Vector3(0f, -Mathf.Sin(arrowheadAngle* Mathf.Deg2Rad), -1f) * arrowheadLength; // Draw the line from A to B Gizmos.DrawLine(a, b); // Draw the rays representing the arrowhead. Gizmos.DrawRay(arrowPos, up); Gizmos.DrawRay(arrowPos, down); } 3D Gizmo Arrow Method with Connected Ends Code (CSharp): private void DrawArrow(Vector3 a, Vector3 b, float arrowheadAngle, float arrowheadDistance, float arrowheadLength) { // Get the Direction of the Vector Vector3 dir = b - a; // Get the Position of the Arrowhead along the length of the line. Vector3 arrowPos = a + (dir * arrowheadDistance); // Get the Arrowhead Lines using the direction from earlier multiplied by a vector representing half of the full angle of the arrowhead (y) // and -1 for going backwards instead of forwards (z), which is then multiplied by the desired length of the arrowhead lines coming from the point. Vector3 up = Quaternion.LookRotation(dir) * new Vector3(0f, Mathf.Sin(arrowheadAngle* Mathf.Deg2Rad), -1f) * arrowheadLength; Vector3 down = Quaternion.LookRotation(dir) * new Vector3(0f, -Mathf.Sin(arrowheadAngle* Mathf.Deg2Rad), -1f) * arrowheadLength; Vector3 left= Quaternion.LookRotation(dir) * new Vector3(Mathf.Sin(arrowheadAngle* Mathf.Deg2Rad), 0f, -1f) * arrowheadLength; Vector3 right = Quaternion.LookRotation(dir) * new Vector3(-Mathf.Sin(arrowheadAngle* Mathf.Deg2Rad), 0f, -1f) * arrowheadLength; // Get the End Locations of all points for connecting arrowhead lines. Vector3 upPos = arrowPos + up; Vector3 downPos = arrowPos + down; Vector3 leftPos = arrowPos + left; Vector3 rightPos = arrowPos + right; // Draw the line from A to B Gizmos.DrawLine(a, b); // Draw the rays representing the arrowhead. Gizmos.DrawRay(arrowPos, up); Gizmos.DrawRay(arrowPos, down); Gizmos.DrawRay(arrowPos, left); Gizmos.DrawRay(arrowPos, right); // Draw Connections between rays representing the arrowhead Gizmos.DrawLine(upPos, leftPos); Gizmos.DrawLine(leftPos, downPos); Gizmos.DrawLine(downPos, rightPos); Gizmos.DrawLine(rightPos, upPos); } Result of 2D Arrow (Left) | Result of 3D Arrow (Right) Revised Notes: Some of these are just if you're using either/or function and want to DIY things yourself. Quaternions.Euler() is not a vital part of the method. The multiplication is used to add rotation to the end direction via quaternions. Add it after Quaternion.LookRotation() and before multiplying the new Vector3 to apply a custom rotation. Add rotation to the Z axis in this case to rotate the arrow points around the line (which preserves the direction it's facing). Rotating across the X and Y axes result in changing the direction of the arrow accordingly Because there is no benefit to Quaternions.Euler() for simple arrows, this can be skipped entirely. Same with doing Mathf.Sin(arrowheadAngle* Mathf.Rad2Deg). If you want to skip this for quicker drawing, replace Mathf.Sin(arrowheadAngle * Mathf.Rad2Deg) with a value between 0f and 1f of your choice. Multiplying the result by arrowheadLength can be skipped and put at a fixed value between 0f and 1f. This is just the point along the total length of the line to draw the arrowhead at. To get an arrow along xz instead of xy, switch the x and y values within [ new Vector3(x, y, z) ] on line 12 and 13. To get a 4-point arrow, use xz and xy variants of the 2D arrowhead simultaneously. Then call DrawRay for all four arrowhead lines. To connect the lines, store the value of ArrowPos + up/down/left/right (point from direction) and draw lines connecting between each point using DrawLine(startPosition, endPosition). To get the length of the arrow taken by the arrow head so you can only draw the interconnecting line up until the arrow head, you need to do a repeated calculation like the direction vectors for the arrowheads. [Vector3 len = Quaternion.LookRotation(dir) * new Vector3(0f, 0f, -1f) * arrowheadLength;]. You then subtract this vector from the end point in the final line's end point to get the result you're looking for. If you see any errors feel free to correct them or tell me about them. I'm still learning more about this stuff and tried to do this alone for months without knowledge of quaternions. Everything with the maths is heavily watered down and may not be accurate. I just want to provide a clearer explanation of what's going on for those who don't particularly see what's happening or why it's happening. Something I wish I had when I started the trip of learning about all the maths used here 2-3 days ago (and I'm glad I have). Post Original Date: June 1st, 2022 - 00:21 AM (GMT) Revision Date: June 2nd, 2022 - 22:05 PM (GMT) Revision 2 Date: November 8th, 2022 - 12:20 PM (GMT) - Removed a repeated "Magic Number" with a better solution I found in university while testing something for an independent project*. Revision 3 Date: February 22nd, 2023 -18:15PM (GMT): Added a revised note on calculating the length of the arrow head and subtracting it from the interconnecting line between the start and end points.
Simple 2D. Code (CSharp): Vector2 arrowPos; Vector2 arrowDirection; Vector3 angleVectorUp=new Vector3(0f, 0.40f,-1f)*0.2f/*length*/; Vector3 angleVectorDown=new Vector3(0f, -0.40f,-1f)*0.2f/*length*/; Vector2 upTmp; Vector2 downTmp; private void DrawArrow(Vector2 startPos, Vector2 endPos) { arrowDirection=endPos - startPos; arrowPos = startPos + (arrowDirection*0.9f/*position along line*/); upTmp = Quaternion.LookRotation(arrowDirection) * angleVectorUp ; downTmp = Quaternion.LookRotation(arrowDirection) * angleVectorDown; Gizmos.DrawLine(startPos, endPos); Gizmos.DrawRay(arrowPos, upTmp); Gizmos.DrawRay(arrowPos, downTmp); } Usage: Code (CSharp): private void OnDrawGizmos() { DrawArrow(startPos,endPos); } Thanks to Neopolitans.
@AnomalusUndrdog Thanks, I'm not a coder but I was able to figure it out, nice script Thanks again. cheers
May I suggest this little modification, so the arrow head is always flat on the camera? Code (CSharp): public static void DrawArrow(Vector3 pos, Vector3 direction, float arrowLength, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20.0f) { var arrowTip = pos + direction * arrowLength; Gizmos.DrawLine(pos, arrowTip); Camera c = Camera.current; if (c == null) return; Vector3 right = Quaternion.LookRotation(direction, c.transform.forward) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * new Vector3(0, 0, 1); Vector3 left = Quaternion.LookRotation(direction, c.transform.forward) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * new Vector3(0, 0, 1); Gizmos.DrawLine(arrowTip, arrowTip + right * arrowHeadLength); Gizmos.DrawLine(arrowTip, arrowTip + left * arrowHeadLength); }