Hello, Im trying to make a RTS style selection box in my 3D project. I just cant figure out what’s not working out. It complains but I have Panel as a node to my scene… Anyone got a clue? Im sending the code as well, might be something wrong so far.
extends Camera3D
var start = Vector2()
var end = Vector2()
var isDragging = false
signal area_selected
func _ready():
set_process_input(true)
func _input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
# Start dragging
isDragging = true
start = event.position
end = event.position
$Panel.visible = true # Make sure the panel is visible when dragging starts
else:
# Stop dragging
isDragging = false
end = event.position
$Panel.visible = false # Hide the panel when dragging stops
draw_area(false)
emit_signal("area_selected", start, end) # Emit signal with the selected area coordinates
elif event is InputEventMouseMotion:
if isDragging:
# Update the end position while dragging
end = event.position
draw_area()
func draw_area(s=true):
var startVector = start
var endVector = end
var panel = $Panel
panel.custom_minimum_size = Vector2(abs(startVector.x - endVector.x), abs(startVector.y - endVector.y))
var pos = Vector2()
pos.x = min(startVector.x, endVector.x)
pos.y = min(startVector.y, endVector.y)
panel.rect_position = pos
# You can optionally scale the panel here if needed
if s:
panel.custom_minimum_size *= int(s)
func select_objects_in_box(start, end):
# Implement your selection logic here
pass
I suppose the script is attached to the node Camera3D, right? But Panel isn’t a child node of that node, so you cannot use the path $Panel. Something like $"../Panel" should work. Or drag the Panel node to your script to get the correct path.
Alternatively, you could use a scene unique name too - right click the panel, ‘Access as Unique Name’. Change the code to ‘%Panel’. It no longer matters where it is.
Now the selection box works fine! A raycast of the mouseposition and collision is also working out well.
Code in ColorRect (UI Scene):
extends ColorRect
var mouse_down : bool = false
var mouse_start_pos : Vector2
var mouse_end_pos : Vector2
var box_grid : int = 1
var nodes_in_rect : Array = []
var camera : Camera3D
func _ready():
# Find the Camera3D node
camera = get_tree().root.get_node("Game/Camera3D")
assert(camera != null, "Camera3D node not found. Please check the path.")
func _input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed():
# Start the selection box
if not mouse_down:
nodes_in_rect.clear()
mouse_down = true
mouse_start_pos = event.global_position
global_position = mouse_start_pos
size = Vector2.ZERO # Reset size initially
elif event.button_index == MOUSE_BUTTON_LEFT and not event.is_pressed():
# End the selection box
if mouse_down:
mouse_down = false
mouse_end_pos = event.global_position
update_selection()
size = Vector2.ZERO # Reset size after selection
elif event is InputEventMouseMotion:
if mouse_down:
update_selection_box()
func update_selection_box():
var mouse_pos = get_global_mouse_position()
size = (mouse_pos - mouse_start_pos).abs()
position = mouse_start_pos
if mouse_pos.x < mouse_start_pos.x:
position.x = mouse_pos.x
if mouse_pos.y < mouse_start_pos.y:
position.y = mouse_pos.y
func update_selection():
# Find all nodes within the selection box
var nodes_to_get = get_tree().get_nodes_in_group("Selectables")
if nodes_to_get:
for node in nodes_to_get:
var node_global_pos = node.global_transform.origin
var node_2d_pos = camera.unproject_position(node_global_pos)
if get_global_rect().has_point(node_2d_pos):
nodes_in_rect.append(node)
print("Selected node: ", node.name) # Add this line to print the name of the selected node
# Utility function for snapping to grid
func snapped(value, grid_size):
return round(value / grid_size) * grid_size
Code in Camera3D (Game scene):
extends Camera3D
@onready var ray_cast_3d: RayCast3D = $RayCast3D
var is_panning = false
var last_mouse_position = Vector2()
# Adjust these values to control panning speed
var pan_speed = 0.1
func _unhandled_input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_MIDDLE:
is_panning = event.pressed
if is_panning:
last_mouse_position = event.position
if event is InputEventMouseMotion and is_panning:
var mouse_delta = event.position - last_mouse_position
var move_vector = Vector3(mouse_delta.x, 0, mouse_delta.y) * pan_speed
global_translate(move_vector)
last_mouse_position = event.position
func _process(delta: float) -> void:
var mouse_position : Vector2 = get_viewport().get_mouse_position()
ray_cast_3d.target_position = project_local_ray_normal(mouse_position) * 100.0
ray_cast_3d.force_raycast_update()
print(ray_cast_3d.get_collider())
print(ray_cast_3d.get_collision_point())