Godot-RTS-Template/dev/debug_tools.gd
2025-07-31 22:06:10 +02:00

154 lines
5.7 KiB
GDScript

extends Control
## A utility node for drawing 3D debug visuals (lines, rays, cubes) in screen space.
@export var MouseOverlay: Node
@onready var Cam = get_viewport().get_camera_3d()
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
if !OS.is_debug_build():
self.queue_free()
return
# Ensure the node processes and draws even when the game is paused.
process_mode = Node.PROCESS_MODE_ALWAYS
# Ensure the node is visible and set to draw in screen space.
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
## A class representing a single debug line with start/end points, color, and duration.
class Line:
var Start:Vector3
var End:Vector3
var LineColor:Color
var time:float
## Constructor for DebugLine. [br]
## [param _start] Starting point in 3D space.[br]
## [param _end] Ending point in 3D space.[br]
## [param _color] Color of the line.[br]
## [param _time] Duration the line persists (seconds).
func _init(_Start:Vector3, _End:Vector3, _LineColor:Color, _time:float):
self.Start = _Start
self.End = _End
self.LineColor = _LineColor
self.time = _time
## Array storing all active debug lines.
var Lines = []
## Flag to track if a line was removed, triggering a redraw.
var RemovedLine = false
func _process(delta):
for i in range(len(Lines)):
Lines[i].time -= delta
# Trigger a redraw if lines exist or a line was removed.
if (len(Lines) > 0 || RemovedLine):
queue_redraw() #Calls _draw
RemovedLine = false
func _draw():
# Exit early if no lines to draw or camera is not available.
if Lines.is_empty() or not Cam:
return
for i in range(len(Lines)):
if Lines[i].Start.length() == 0:return
var ScreenPointStart = Cam.unproject_position(Lines[i].Start)
var ScreenPointEnd = Cam.unproject_position(Lines[i].End)
#Dont draw line if either start or end is considered behind the camera
#this causes the line to not be drawn sometimes but avoids a bug where the
#line is drawn incorrectly
if (Cam.is_position_behind(Lines[i].Start) ||
Cam.is_position_behind(Lines[i].End)):
continue
draw_line(ScreenPointStart, ScreenPointEnd, Lines[i].LineColor)
#Remove lines that have timed out
var i = Lines.size() - 1
while (i >= 0):
if (Lines[i].time < 0.0):
Lines.remove_at(i)
RemovedLine = true
i -= 1
## Draws a line between two 3D points. [br]
## [param start] Starting point in 3D space.[br]
## [param end] Ending point in 3D space.[br]
## [param time] Duration the line persists (seconds, default: 0.0 for one frame).[br]
## [param color] Color of the line (default: red, fully opaque).
func DrawLine(Start, End, time := 0.0, LineColor := Color(1.0, 0.0, 0.0, 1.0)):
Lines.append(Line.new(Start, End, LineColor, time))
## Draws a ray from a starting point in a given direction.[br]
## [param start] Starting point in 3D space.[br]
## [param ray] Direction and magnitude of the ray (Vector3).[br]
## [param time] Duration the ray persists (seconds, default: 0.0 for one frame).[br]
## [param color] Color of the ray (default: red, fully opaque).
func DrawRay(Start, Ray, time := 0.0, LineColor := Color(1.0, 0.0, 0.0, 1.0)):
Lines.append(Line.new(Start, Start + Ray, LineColor, time))
## Draws a wireframe cube in 3D space.[br]
## [param center] Center point of the cube in 3D space.[br]
## [param half_extents] Half the width of the cube (default: 0.1).[br]
## [param time] Duration the cube persists (seconds, default: 0.0 for one frame).[br]
## [param color] Color of the cube's edges (default: red, fully opaque).
func DrawCube(Center, HalfExtents := 0.1, time := 0.0, LineColor := Color(1.0, 0.0, 0.0, 1.0)):
#Start at the 'top left'
var LinePointStart = Center
LinePointStart.x -= HalfExtents
LinePointStart.y += HalfExtents
LinePointStart.z -= HalfExtents
# Draw top square (four edges).
var LinePointEnd = LinePointStart + Vector3(0, 0, HalfExtents * 2.0)
DrawLine(LinePointStart, LinePointEnd, time, LineColor);
LinePointStart = LinePointEnd
LinePointEnd = LinePointStart + Vector3(HalfExtents * 2.0, 0, 0)
DrawLine(LinePointStart, LinePointEnd, time, LineColor);
LinePointStart = LinePointEnd
LinePointEnd = LinePointStart + Vector3(0, 0, -HalfExtents * 2.0)
DrawLine(LinePointStart, LinePointEnd, time, LineColor);
LinePointStart = LinePointEnd
LinePointEnd = LinePointStart + Vector3(-HalfExtents * 2.0, 0, 0)
DrawLine(LinePointStart, LinePointEnd, time, LineColor);
# Draw bottom square (four edges).
LinePointStart = LinePointEnd + Vector3(0, -HalfExtents * 2.0, 0)
LinePointEnd = LinePointStart + Vector3(0, 0, HalfExtents * 2.0)
DrawLine(LinePointStart, LinePointEnd, time, LineColor);
LinePointStart = LinePointEnd
LinePointEnd = LinePointStart + Vector3(HalfExtents * 2.0, 0, 0)
DrawLine(LinePointStart, LinePointEnd, time, LineColor);
LinePointStart = LinePointEnd
LinePointEnd = LinePointStart + Vector3(0, 0, -HalfExtents * 2.0)
DrawLine(LinePointStart, LinePointEnd, time, LineColor);
LinePointStart = LinePointEnd
LinePointEnd = LinePointStart + Vector3(-HalfExtents * 2.0, 0, 0)
DrawLine(LinePointStart, LinePointEnd, time, LineColor);
# Draw vertical lines (four edges connecting top and bottom squares).
LinePointStart = LinePointEnd
DrawRay(LinePointStart, Vector3(0, HalfExtents * 2.0, 0),time, LineColor)
LinePointStart += Vector3(0, 0, HalfExtents * 2.0)
DrawRay(LinePointStart, Vector3(0, HalfExtents * 2.0, 0), time, LineColor)
LinePointStart += Vector3(HalfExtents * 2.0, 0, 0)
DrawRay(LinePointStart, Vector3(0, HalfExtents * 2.0, 0), time, LineColor)
LinePointStart += Vector3(0, 0, -HalfExtents * 2.0)
DrawRay(LinePointStart, Vector3(0, HalfExtents * 2.0, 0), time, LineColor)
func DrawSphere(Position:Vector3):
var SphereShape = CSGSphere3D.new()
var node = Node3D.new()
node.add_child(SphereShape)
node.position = Position
get_tree().root.add_child(node)