Godot 4.5
I’m trying to create a desktop pet style game. I have a working sprite that you can click and move around the screen, and the rest of the screen is transparent and can be clicked through without issue.
However, I’d like the option to add another draggable element to the screen, like another sprite or a moveable menu. But everything I’ve tried either attaches the second element to the first sprite or the window area around the elements become unclickable.
I’m trying to do this all/mostly with GDScript, at least avoiding C# for now (I don’t have much experience with C#).
I’m controlling this mostly with the window manager, since that was the only way I could get the click-through working properly.
Any help would be amazing.
Here’s some of my code:
Window Manager
extends Node
class_name WindowManager
var window: Window
var player_size: Vector2i
func initialize(size: Vector2i, area_2d: Area2D):
window = get_window()
player_size = size
window.size = player_size
setup_mouse_passthrough(area_2d)
func setup_mouse_passthrough(area_2d: Area2D):
var click_area = area_2d.get_node("ClickArea") as CollisionShape2D
if click_area and click_area.shape is RectangleShape2D:
var rect_shape = click_area.shape as RectangleShape2D
var extents = rect_shape.size / 2.0
var sprite = get_parent().get_node("AnimatedSprite2D")
var center = sprite.position + area_2d.position + click_area.position
var corners: PackedVector2Array = [
center - extents,
center + Vector2(extents.x, -extents.y),
center + extents,
center + Vector2(-extents.x, extents.y)
]
DisplayServer.window_set_mouse_passthrough(corners)
func get_current_screen() -> int:
var window_center = Vector2(window.position) + Vector2(player_size) / 2.0
for i in range(DisplayServer.get_screen_count()):
var screen_pos = DisplayServer.screen_get_position(i)
var screen_size = DisplayServer.screen_get_size(i)
var screen_rect = Rect2(screen_pos, screen_size)
if screen_rect.has_point(window_center):
return i
return 0
func get_all_screens_bounds() -> Dictionary:
var screen_count = DisplayServer.get_screen_count()
var min_x = INF
var min_y = INF
var max_x = -INF
var max_y = -INF
for i in range(screen_count):
var screen_pos = DisplayServer.screen_get_position(i)
var screen_size = DisplayServer.screen_get_size(i)
min_x = min(min_x, screen_pos.x)
min_y = min(min_y, screen_pos.y)
max_x = max(max_x, screen_pos.x + screen_size.x)
max_y = max(max_y, screen_pos.y + screen_size.y)
return {
"min_x": min_x,
"min_y": min_y,
"max_x": max_x,
"max_y": max_y
}
func get_screen_bounds(screen_index: int) -> Dictionary:
var screen_pos = DisplayServer.screen_get_position(screen_index)
var screen_size = DisplayServer.screen_get_size(screen_index)
return {
"min_x": screen_pos.x,
"min_y": screen_pos.y,
"max_x": screen_pos.x + screen_size.x,
"max_y": screen_pos.y + screen_size.y
}
Drag Manager
extends Node
class_name DragManager
signal drag_started
signal drag_ended
var dragging = false
var offset = Vector2()
var window: Window
var window_manager: WindowManager
var player_size: Vector2i
func initialize(area_2d: Area2D):
window = get_window()
window_manager = get_parent().get_node("WindowManager")
player_size = get_parent().player_size
area_2d.input_event.connect(_on_area_input)
func _on_area_input(_viewport, event, _shape_idx):
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
dragging = true
var mouse_screen_pos = DisplayServer.mouse_get_position()
offset = Vector2(window.position) - Vector2(mouse_screen_pos)
drag_started.emit()
else:
dragging = false
drag_ended.emit()
func _process(_delta):
if dragging:
var mouse_screen_pos = DisplayServer.mouse_get_position()
var new_position = Vector2(mouse_screen_pos) + offset
var bounds = window_manager.get_all_screens_bounds()
new_position.x = clamp(new_position.x, bounds.min_x, bounds.max_x - player_size.x)
new_position.y = clamp(new_position.y, bounds.min_y, bounds.max_y - player_size.y)
window.position = Vector2i(new_position)