Unity — Drawing Custom Debug Shapes — Part 5
Draw Debug Triangles
As many of you know, a triangle drawn in three-dimensional space is a foundation of 3D graphics. As such, it can be useful to be able to draw different types of triangles within your Unity project and to understand their properties.
Through this tutorial we will cover four different approaches for drawing triangles:
- Triangle defined by three points in 3D space,
- Isosceles triangle defined by its base length and height,
- Equilateral triangle defined by its origin and height.
- Equilateral triangle defined by its origin and size of the side(s).
Each approach will also include a possibility to add a linear offset and orientation.
This tutorial is a part of Unity– Drawing Custom Debug Shapes series. I would suggest to check the previous tutorials too, since they explain some of the basic geometry and Unity concepts in more detail.
Three Points Triangle
Drawing a triangle using the three points, although by far the simplest, will be a base for each following approach. The only work this method needs to perform is to connect three points using the Unity’s built-in Debug method DrawLine. As the title suggest, the method needs to receive four arguments (points A, B and C) and a drawing color. Afterwards, it simply needs to connect the following points, A-B, B-C and C-A. Source code of this method can be seen below:
// Draw a triangle defined by three points
public static void DrawTriangle(Vector3 pointA, Vector3 pointB, Vector3 pointC, Color color)
{
// Connect pointA and pointB
Debug.DrawLine(pointA, pointB, color);
// Connect pointB and pointC
Debug.DrawLine(pointB, pointC, color);
// Connect pointC and pointA
Debug.DrawLine(pointC, pointA, color);
}
As in the Draw a Debug Circle tutorial, we can use Example class’ Update method to test this method:
void Update()
{
Debug.DrawTriangle(new Vector3(0, 0, 0), new Vector3(0.25f, 1, 0), new Vector3(0.5f, 0.5f), Color.red);
}
The result of this code can be seen in the following image:
Additionally, we can increase the usefulness of this method by extending it with two parameters: linear offset and orientation. This could, for example make it possible to “attach” a certain triangle to a moving object and draw the triangle in that object’s space. To do so, we can use overloaded method (a method with the same name, but with a different set of parameters). As explained in Unity — Draw a Debug Circle — Part 2, we can offset and rotate the triangle by using some basic linear algebra operations. The source code of this “enhanced” method is show below:
public static void DrawTriangle(Vector3 pointA, Vector3 pointB, Vector3 pointC, Vector3 offset, Quaternion orientation, Color color)
{
pointA = offset + orientation * pointA;
pointB = offset + orientation * pointB;
pointC = offset + orientation * pointC;
DrawTriangle(pointA, pointB, pointC, color);
}
As you can see, the new, overloading version of the DrawTriangle is calling the original version of the DrawTriangle method, but in such case it is not starting a recursive behavior. For an example, we can draw a triangle with the same points as the previous one, but this time “attached” to the Example GameObject’s position and orientation, and for easier differentiation, drawn using green color:
void Update()
{
Debug.DrawTriangle(new Vector3(0, 0, 0), new Vector3(0.25f, 1, 0), new Vector3(0.5f, 0.5f), Color.red);
Debug.DrawTriangle(new Vector3(0, 0, 0), new Vector3(0.25f, 1, 0), new Vector3(0.5f, 0.5f),
transform.position, transform.rotation, Color.green);
}
By offsetting the Example GameObject by (1.0, 0, 0) and rotating it by (0°, 0°, -30°) we receive the following result (manipulation handles are added for increased clarity on what is happening):
Isosceles Triangle
Isosceles triangle is a triangle with two equal sides. Usually, in a context of game development its use-cases are more limited than the ones of a triangle defined by three points, but in the context of debugging it can be extremely useful when it comes to debugging various directions or “heading” of certain objects, as well as drawing arrows which describe vector’s directions and their magnitude.
In their basic form, isosceles triangles are defined by the length of their base, and the length of the equal sides or by it’s height. Since the approach using a base length and triangle height is generally more useful, we will focus on implementing that one. As with the previous example, we will also incorporate linear offset and orientation as the additional parameters.
To calculate the points of isosceles triangle, we need to make some assumptions first:
- Base of the triangle is defined by the points A and C,
- Base of the triangle is bisected by the point D,
- Distance between the point B and bisector point D is triangle’s height
- Segment B-D is perpendicular to the triangle base (A-C)
Using Unity’s built-in math methods makes calculating these points relatively easy, by simply offsetting the origin location in different direction, by certain lengths:
- We can consider the point D as the origin of the triangle and set it to location (0, 0, 0),
- Point A is a half base-length units away to the right,
- Point C is a half base-length units away to the left (negative right),
- Point B is height units away above the point D.
The following code shows how to do that. As you can see, point D calculation is skipped since it is simply a zero-vector (Vector3.zero). After calculating points A, B and C, they are passed to a method used to draw the three-point triangle with linear and angular offset, as well as the drawing color:
public static void DrawTriangle(Vector3 origin, Quaternion orientation, float baseLength, float height, Color color)
{
Vector3 pointA = Vector3.right * baseLength * 0.5f;
Vector3 pointC = Vector3.left * baseLength * 0.5f;
Vector3 pointB = Vector3.up * height;
DrawTriangle(pointA, pointB, pointC, origin, orientation, color);
}
And we can call this new method from our Example class:
void Update()
{
Debug.DrawTriangle(transform.position, transform.rotation, 1.0f, 2.0f, Color.yellow);
}
Which results in the following image (using a zero and non-zero offset and orientation):
Equilateral Triangle
Equilateral triangle is a triangle whose sides’ lengths and consequently angles between them are equal. One way of defining an equilateral triangle is by using it’s center and lengths of the sides. Each of the points of the equilateral triangle is also equally distant from the triangle’s center.
One of the ways to calculate the points A, B and C that are equally distant from the center (O), is to utilize the same trigonometry technique as show in the Unity — Draw Debug Circles tutorial. In a sense a equilateral triangle defined in this way can be understood as a circle-approximation with only three sides. In that sense the points are calculated as follows (for now, we can assume that O = [0, 0]). First we need to calculate the mentioned circle. In this case it will be a distance connecting the origin O with any of the triangle’s points. From the image above it is possible to construct a triangle defined by the points O, A and O’, which is a projection of the point O on the segment CA. Since the O’ is bisecting the segment CA, we can conclude that the segment O’A is half of the side length. From the following image we can see that the angle CAO is 30°, and we can use it to calculate the length of OA, which is in this case a hypothenuse of the triangle O’OA. For convenience, we can call the segment OA “radius”.
cos(30°)=0.5∗length/radius
radius = 0.5*length/cos(30°)
A=[cos(330°),sin(330°)]∗radius
B=[cos(90°),sin(90°)]∗radius
C=[cos(210°),sin(210°)]∗radius
After calculating these three, we can simply pass them to the existing three-point triangle drawing method. The following implementation does exactly that (as with the previous examples, linear offset and rotation are passed too):
public static void DrawTriangle(float length, Vector3 center, Quaternion orientation, Color color)
{
float radius = length / Mathf.Cos(30.0f * Mathf.Deg2Rad) * 0.5f;
Vector3 pointA = new Vector3(Mathf.Cos(330.0f * Mathf.Deg2Rad), Mathf.Sin(330.0f * Mathf.Deg2Rad), 0.0f) * radius;
Vector3 pointB = new Vector3(Mathf.Cos(90.0f * Mathf.Deg2Rad), Mathf.Sin(90.0f * Mathf.Deg2Rad), 0.0f) * radius;
Vector3 pointC = new Vector3(Mathf.Cos(210.0f * Mathf.Deg2Rad), Mathf.Sin(210.0f * Mathf.Deg2Rad), 0.0f) * radius;
DrawTriangle(pointA, pointB, pointC, center, orientation, color);
}
By calling this new method in our Example class, we receive the following:
void Update()
{
Debug.DrawTriangle(1.0f, transform.position, transform.rotation, Color.magenta);
}
This concludes the “triangle” section of this tutorial series.
Please leave a comment if you have any questions or suggestions, or if you have some topic that you would like to to cover in the future.
In the meanwhile, you can also reach me on LinkedIn.
The following tutorial covers how to draw debug rectangles.