@tool extends Node3D class_name Button3D signal on_button_down() signal on_button_up() const IconFont = preload ("res://assets/icons/icons.tres") const ECHO_WAIT_INITIAL = 0.5 const ECHO_WAIT_REPEAT = 0.1 @onready var label_node: Label3D = $Body/Label @onready var finger_area: Area3D = $FingerArea @export var focusable: bool = true: set(value): focusable = value if value == false: add_to_group("ui_focus_stop") else: remove_from_group("ui_focus_stop") @export var font_size: int = 10: set(value): font_size = value if !is_node_ready(): await ready label_node.font_size = value @export var label: String = "": set(value): label = value if !is_node_ready(): await ready label_node.text = value @export var icon: bool = false: set(value): icon = value if !is_node_ready(): await ready if value: label_node.font = IconFont label_node.font_size = 36 label_node.width = 1000 label_node.autowrap_mode = TextServer.AUTOWRAP_OFF else: label_node.font = null label_node.font_size = font_size label_node.width = 50 label_node.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART @export var toggleable: bool = false @export var disabled: bool = false @export var echo: bool = false @export var initial_active: bool = false var external_value: Proxy = null: set(value): if !is_node_ready(): await ready external_value = value if value != null: value.on_set.connect(func(_value): update_animation() ) var active: bool = false: get: if external_value != null: return external_value.value return active set(value): if !is_node_ready(): await ready if external_value != null: external_value.value = value else: active = value update_animation() @onready var animation_player: AnimationPlayer = $AnimationPlayer @onready var console = get_node("/root/Main/Console") var echo_timer: Timer = null func _ready(): if initial_active: active = true if echo: echo_timer = Timer.new() echo_timer.wait_time = ECHO_WAIT_INITIAL echo_timer.one_shot = false echo_timer.timeout.connect(func(): echo_timer.stop() echo_timer.wait_time=ECHO_WAIT_REPEAT echo_timer.start() on_button_down.emit() ) add_child(echo_timer) func update_animation(): var length = animation_player.get_animation("down").length if active&&animation_player.current_animation_position != length: animation_player.play("down") elif !active&&animation_player.current_animation_position != 0: animation_player.play_backwards("down") func _on_press_down(event): if disabled: event.bubbling = false return AudioPlayer.play_effect("click") if toggleable: return if echo: echo_timer.start() active = true on_button_down.emit() func _on_press_up(event): if disabled: event.bubbling = false return if toggleable: active = !active if active: on_button_down.emit() else: on_button_up.emit() else: if echo: echo_timer.stop() echo_timer.wait_time = ECHO_WAIT_INITIAL active = false on_button_up.emit() func _on_touch_enter(event: EventTouch): if event.target != finger_area: return animation_player.stop() animation_player.speed_scale = 0 animation_player.current_animation = "down" AudioPlayer.play_effect("click") _touch_change(event) func _on_touch_move(event: EventTouch): _touch_change(event) func _on_touch_leave(_event: EventTouch): animation_player.stop() animation_player.speed_scale = 1 if toggleable: active = !active if active: on_button_up.emit() else: on_button_down.emit() func _touch_change(event: EventTouch): if disabled: event.bubbling = false return var pos = Vector3(0, 1, 0) for finger in event.fingers: var finger_pos = to_local(finger.area.global_position) if pos.y > finger_pos.y: pos = finger_pos var button_height = 0.2 var button_center = 0.1 var percent = clamp((button_center + button_height / 2 - pos.y) / (button_height / 2), 0, 1) if !active&&percent < 1: on_button_down.emit() elif active&&percent >= 1: on_button_up.emit() animation_player.seek(percent * animation_player.current_animation_length, true) if toggleable: return active = percent < 1