I want to draw debug lines in the scene. How could I do this? ty
What is the equivalent of "OnDrawGizmos" (from Unity) in Godot?
As far as I know, there isn't equivalent. That's why I hacked together this fast and ugly piece of code when I came from Unity to Godot myself:
using Godot;
using System.Collections.Generic;
public partial class Gizmo : Node
{
private static float drawDistance;
private static Camera3D camera;
private static readonly List<Vector3> vertexList = new();
private static readonly List<Color> colorList = new();
private static readonly MeshInstance3D MeshInstance = new();
private static readonly ArrayMesh ArrayMesh = new();
private static readonly StandardMaterial3D Material = new()
{
VertexColorUseAsAlbedo = true,
ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded,
Transparency = BaseMaterial3D.TransparencyEnum.Disabled,
DistanceFadeMode = BaseMaterial3D.DistanceFadeModeEnum.PixelAlpha,
};
private static readonly Godot.Collections.Array MeshArrays = new();
public Gizmo()
{
drawDistance = 100;
Material.DistanceFadeMinDistance = drawDistance;
}
public override void _EnterTree()
{
ProcessPriority = 1000;
AddChild(MeshInstance);
}
public override void _Ready()
{
camera = GetViewport().GetCamera3D();
}
public override void _Process(double delta)
{
Draw();
}
public static void Line(Vector3 a, Vector3 b, Color color)
{
vertexList.Add(a); colorList.Add(color);
vertexList.Add(b); colorList.Add(color);
}
public static void Circle(Vector3 origin, Basis orientation, Color color, float radius)
{
int levelOfDetail = GetLevelOfDetail(origin, radius);
for (int i = 0; i < levelOfDetail; i++)
{
float ax = Mathf.Cos(Mathf.Tau / levelOfDetail * i) * radius;
float ay = Mathf.Sin(Mathf.Tau / levelOfDetail * i) * radius;
float bx = Mathf.Cos(Mathf.Tau / levelOfDetail * (i + 1)) * radius;
float by = Mathf.Sin(Mathf.Tau / levelOfDetail * (i + 1)) * radius;
Vector3 pointA = origin + (orientation * new Vector3(ax, 0, ay));
Vector3 pointB = origin + (orientation * new Vector3(bx, 0, by));
vertexList.Add(pointA); colorList.Add(color);
vertexList.Add(pointB); colorList.Add(color);
}
}
public static void Sphere(Vector3 origin, Basis orientation, Color color, float radius)
{
int levelOfDetail = GetLevelOfDetail(origin, radius);
for (int i = 0; i < levelOfDetail; i++)
{
float ax = Mathf.Cos(Mathf.Tau / levelOfDetail * i) * radius;
float ay = Mathf.Sin(Mathf.Tau / levelOfDetail * i) * radius;
float bx = Mathf.Cos(Mathf.Tau / levelOfDetail * (i + 1)) * radius;
float by = Mathf.Sin(Mathf.Tau / levelOfDetail * (i + 1)) * radius;
Vector3 pointAX = origin + (orientation * new Vector3(0, ax, ay));
Vector3 pointBX = origin + (orientation * new Vector3(0, bx, by));
Vector3 pointAY = origin + (orientation * new Vector3(ax, 0, ay));
Vector3 pointBY = origin + (orientation * new Vector3(bx, 0, by));
Vector3 pointAZ = origin + (orientation * new Vector3(ax, ay, 0));
Vector3 pointBZ = origin + (orientation * new Vector3(bx, by, 0));
vertexList.Add(pointAX); colorList.Add(color);
vertexList.Add(pointBX); colorList.Add(color);
vertexList.Add(pointAY); colorList.Add(color);
vertexList.Add(pointBY); colorList.Add(color);
vertexList.Add(pointAZ); colorList.Add(color);
vertexList.Add(pointBZ); colorList.Add(color);
}
}
public static void Box(Vector3 origin, Basis orientation, Color color, Vector3 size)
{
if (GetLevelOfDetail(origin, (size.X + size.Y + size.Z) / 3) == 0) return;
Vector3[] corners = new Vector3[8];
corners[0] = origin + (-orientation.X + orientation.Y - orientation.Z) * 0.5f * size;
corners[1] = origin + (-orientation.X - orientation.Y - orientation.Z) * 0.5f * size;
corners[2] = origin + (orientation.X - orientation.Y - orientation.Z) * 0.5f * size;
corners[3] = origin + (orientation.X + orientation.Y - orientation.Z) * 0.5f * size;
corners[4] = origin + (-orientation.X + orientation.Y + orientation.Z) * 0.5f * size;
corners[5] = origin + (-orientation.X - orientation.Y + orientation.Z) * 0.5f * size;
corners[6] = origin + (orientation.X - orientation.Y + orientation.Z) * 0.5f * size;
corners[7] = origin + (orientation.X + orientation.Y + orientation.Z) * 0.5f * size;
Line(corners[0], corners[1], color);
Line(corners[0], corners[3], color);
Line(corners[0], corners[4], color);
Line(corners[1], corners[2], color);
Line(corners[1], corners[5], color);
Line(corners[2], corners[3], color);
Line(corners[2], corners[6], color);
Line(corners[3], corners[7], color);
Line(corners[4], corners[5], color);
Line(corners[4], corners[7], color);
Line(corners[5], corners[6], color);
Line(corners[6], corners[7], color);
}
public static void Cone(Vector3 origin, Basis orientation, Color color, float radius, float height)
{
int levelOfDetail = GetLevelOfDetail(origin, radius);
for (int i = 0; i < levelOfDetail; i++)
{
float AX = Mathf.Cos(Mathf.Tau / levelOfDetail * i) * radius;
float AZ = Mathf.Sin(Mathf.Tau / levelOfDetail * i) * radius;
Vector3 PointA = origin + (orientation * new Vector3(AX, 0, AZ));
Line(PointA, origin + (orientation.Y * height), color);
}
Circle(origin, orientation, color, radius);
}
public static void Cylinder(Vector3 origin, Basis orientation, Color color, float radius, float height)
{
int levelOfDetail = GetLevelOfDetail(origin, radius);
for (int i = 0; i < levelOfDetail; i++)
{
float AX = Mathf.Cos(Mathf.Tau / levelOfDetail * i) * radius;
float AZ = Mathf.Sin(Mathf.Tau / levelOfDetail * i) * radius;
Vector3 PointA = origin + (orientation * new Vector3(AX, 0, AZ));
Line(PointA + (orientation.Y * height * 0.5f), PointA - (orientation.Y * height * 0.5f), color);
}
Circle(origin + (orientation.Y * height * 0.5f), orientation, color, radius);
Circle(origin - (orientation.Y * height * 0.5f), orientation, color, radius);
}
public static void Capsule(Vector3 origin, Basis orientation, Color color, float radius, float height)
{
int levelOfDetail = GetLevelOfDetail(origin, radius);
Vector3 PointA1;
Vector3 PointB1;
Vector3 PointA2;
Vector3 PointB2;
for (int i = 0; i < levelOfDetail; i++)
{
float AX = Mathf.Cos(Mathf.Tau / levelOfDetail * i) * radius;
float AY = Mathf.Sin(Mathf.Tau / levelOfDetail * i) * radius;
float BX = Mathf.Cos(Mathf.Tau / levelOfDetail * (i + 1)) * radius;
float BY = Mathf.Sin(Mathf.Tau / levelOfDetail * (i + 1)) * radius;
if (AY == 0)
{
for (int angle = 0; angle < 4; angle++)
{
Vector3 Point = origin + orientation.Z.Rotated(orientation.Y, Mathf.Tau / 4 * angle) * radius;
Line(Point + (orientation.Y * (height * 0.5f - radius)), Point - (orientation.Y * (height * 0.5f - radius)), color);
}
}
if (AY >= 0)
{
PointA1 = origin + (orientation.Y * (height * 0.5f - radius)) + (orientation * new Vector3(0, AY, AX));
PointB1 = origin + (orientation.Y * (height * 0.5f - radius)) + (orientation * new Vector3(0, BY, BX));
PointA2 = origin + (orientation.Y * (height * 0.5f - radius)) + (orientation * new Vector3(AX, AY, 0));
PointB2 = origin + (orientation.Y * (height * 0.5f - radius)) + (orientation * new Vector3(BX, BY, 0));
}
else
{
PointA1 = origin - (orientation.Y * (height * 0.5f - radius)) + (orientation * new Vector3(AX, AY, 0));
PointB1 = origin - (orientation.Y * (height * 0.5f - radius)) + (orientation * new Vector3(BX, BY, 0));
PointA2 = origin - (orientation.Y * (height * 0.5f - radius)) + (orientation * new Vector3(0, AY, AX));
PointB2 = origin - (orientation.Y * (height * 0.5f - radius)) + (orientation * new Vector3(0, BY, BX));
}
vertexList.Add(PointA1); colorList.Add(color);
vertexList.Add(PointB1); colorList.Add(color);
vertexList.Add(PointA2); colorList.Add(color);
vertexList.Add(PointB2); colorList.Add(color);
Circle(origin + (orientation.Y * (height * 0.5f - radius)), orientation, color, radius);
Circle(origin - (orientation.Y * (height * 0.5f - radius)), orientation, color, radius);
}
}
public static void Helix(Vector3 origin, Basis orientation, Color color, float radius, float height, int coils)
{
int levelOfDetail = GetLevelOfDetail(origin, radius);
for (int i = 0; i < levelOfDetail * coils; i++)
{
float AX = Mathf.Cos(Mathf.Tau / levelOfDetail * i) * radius;
float AY = Mathf.Sin(Mathf.Tau / levelOfDetail * i) * radius;
float AZ = i * (height / (levelOfDetail * coils));
float BX = Mathf.Cos(Mathf.Tau / levelOfDetail * (i + 1)) * radius;
float BY = Mathf.Sin(Mathf.Tau / levelOfDetail * (i + 1)) * radius;
float BZ = (i + 1) * (height / (levelOfDetail * coils));
Vector3 PointA = origin + (orientation * new Vector3(AX, AY, AZ));
Vector3 PointB = origin + (orientation * new Vector3(BX, BY, BZ));
vertexList.Add(PointA); colorList.Add(color);
vertexList.Add(PointB); colorList.Add(color);
}
}
public static void Circle(Transform3D transform, Color color, float radius)
{
Circle(transform.Origin, transform.Basis, color, radius);
}
public static void Sphere(Transform3D transform, Color color, float radius)
{
Sphere(transform.Origin, transform.Basis, color, radius);
}
public static void Box(Transform3D transform, Color color, Vector3 size)
{
Box(transform.Origin, transform.Basis, color, size);
}
public static void Cone(Transform3D transform, Color color, float radius, float height)
{
Cone(transform.Origin, transform.Basis, color, radius, height);
}
public static void Cylinder(Transform3D transform, Color color, float radius, float height)
{
Cylinder(transform.Origin, transform.Basis, color, radius, height);
}
public static void Capsule(Transform3D transform, Color color, float radius, float height)
{
Capsule(transform.Origin, transform.Basis, color, radius, height);
}
public static void Helix(Transform3D transform, Color color, float radius, float height, int coils)
{
Helix(transform.Origin, transform.Basis, color, radius, height, coils);
}
private static int GetLevelOfDetail(Vector3 position, float radius)
{
float squaredDrawDistance = (drawDistance * drawDistance) + (radius * radius);
int levelOfDetail = Mathf.RoundToInt(36 - Mathf.Remap(Mathf.Min(camera.GlobalPosition.DistanceSquaredTo(position), squaredDrawDistance), 0, squaredDrawDistance, 0, 36));
return levelOfDetail > 6 ? levelOfDetail : 0;
}
private static void Draw()
{
ArrayMesh.ClearSurfaces();
MeshArrays.Resize((int)Mesh.ArrayType.Max);
if (vertexList.Count == 0) return;
MeshArrays[(int)Mesh.ArrayType.Vertex] = vertexList.ToArray();
MeshArrays[(int)Mesh.ArrayType.Color] = colorList.ToArray();
ArrayMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Lines, MeshArrays);
MeshInstance.Mesh = ArrayMesh;
MeshInstance.Mesh.SurfaceSetMaterial(0, Material);
vertexList.Clear();
colorList.Clear();
}
}
You can save this as "Gizmo.cs" and add the script file to autoload list or create a plain node and attach the script to that node. Then in your own code you just call "Gizmo.Shape(position, basis, etc.)", for example Gizmo.Capsule(Vector3.Zero, Basis.Identity, Colors.Green, 2f, 5f);
Under the hood it generates one mesh containing all the shapes drawn at that frame, and the mesh is cleared every frame. Oh, and you also need a Camera3D into your scene to use this. That's about it, it isn't pretty, but have been good enough for my needs at least.
Available shapes at the moment are Line, Circle, Sphere, Box, Cone, Capsule and Helix.
- Best Answerset by pecollinni
I'm unsure why @Red had to piece this together, but what you might be looking for is writing an EditorPlugin
containing a 3D Gizmo plugin. See the relevant manual pages below:
- Edited
This may also be helpful:
@tool is a powerful line of code that, when added at the top of your script, makes it execute in the editor. You can also decide which parts of the script execute in the editor, which in game, and which in both.
You can use it for doing many things, but it is mostly useful in level design for visually presenting things that are hard to predict ourselves.
https://docs.godotengine.org/en/4.1/tutorials/plugins/running_code_in_the_editor.html
artchzh the addon works beautifully, thank you for the help
Nice stiff, I wasusing a primitive one made by myself. Didn't know there is s official plugin example.