- project.godot with autoload configuration - Reception room with placeholder visuals - Draggable Character with DragDropComponent - Interactive flower object with bounce animation - GameState, SaveManager, AudioManager, InputManager autoloads - HUD with back button and music toggle Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
81 lines
2.6 KiB
GDScript
81 lines
2.6 KiB
GDScript
## DragDropComponent — reusable drag-and-drop node; attach to any Character or InteractiveObject.
|
|
class_name DragDropComponent extends Node
|
|
|
|
signal drag_picked_up(global_position: Vector2)
|
|
signal drag_released(global_position: Vector2)
|
|
|
|
const DRAG_Z_INDEX: int = 10
|
|
const DRAG_SCALE: float = 1.1
|
|
|
|
@export var drag_target: Node2D
|
|
|
|
var _is_dragging: bool = false
|
|
var _drag_offset: Vector2 = Vector2.ZERO
|
|
var _original_z_index: int = 0
|
|
var _original_scale: Vector2 = Vector2.ONE
|
|
|
|
|
|
func _ready() -> void:
|
|
if drag_target == null:
|
|
drag_target = get_parent() as Node2D
|
|
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
if event is InputEventScreenTouch:
|
|
_handle_press(event.pressed, event.position)
|
|
elif event is InputEventScreenDrag and _is_dragging:
|
|
_move_to(event.position)
|
|
elif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
|
|
_handle_press(event.pressed, event.position)
|
|
elif event is InputEventMouseMotion and _is_dragging:
|
|
_move_to(event.position)
|
|
|
|
|
|
func _handle_press(pressed: bool, screen_pos: Vector2) -> void:
|
|
if pressed and _is_position_over_target(screen_pos):
|
|
_start_drag(screen_pos)
|
|
elif not pressed and _is_dragging:
|
|
_end_drag(screen_pos)
|
|
|
|
|
|
func _start_drag(screen_pos: Vector2) -> void:
|
|
_is_dragging = true
|
|
var world_pos: Vector2 = _screen_to_world(screen_pos)
|
|
_drag_offset = drag_target.global_position - world_pos
|
|
_original_z_index = drag_target.z_index
|
|
_original_scale = drag_target.scale
|
|
drag_target.z_index = DRAG_Z_INDEX
|
|
drag_target.scale = _original_scale * DRAG_SCALE
|
|
drag_picked_up.emit(drag_target.global_position)
|
|
|
|
|
|
func _end_drag(_screen_pos: Vector2) -> void:
|
|
_is_dragging = false
|
|
drag_target.z_index = _original_z_index
|
|
drag_target.scale = _original_scale
|
|
drag_released.emit(drag_target.global_position)
|
|
|
|
|
|
func _move_to(screen_pos: Vector2) -> void:
|
|
var world_pos: Vector2 = _screen_to_world(screen_pos)
|
|
drag_target.global_position = world_pos + _drag_offset
|
|
|
|
|
|
func _screen_to_world(screen_pos: Vector2) -> Vector2:
|
|
var canvas_transform: Transform2D = drag_target.get_viewport().get_canvas_transform()
|
|
return canvas_transform.affine_inverse() * screen_pos
|
|
|
|
|
|
func _is_position_over_target(screen_pos: Vector2) -> bool:
|
|
if drag_target == null:
|
|
return false
|
|
var world_pos: Vector2 = _screen_to_world(screen_pos)
|
|
var local_pos: Vector2 = drag_target.to_local(world_pos)
|
|
var area: Area2D = drag_target.get_node_or_null("CollisionArea") as Area2D
|
|
if area == null:
|
|
return local_pos.length() < 64.0
|
|
for child in area.get_children():
|
|
if child is CollisionShape2D and child.shape != null:
|
|
return child.shape.get_rect().has_point(local_pos)
|
|
return local_pos.length() < 64.0
|