Files
whale-town/scripts/InputHandler.gd
2025-12-05 19:00:14 +08:00

268 lines
7.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends Node
class_name InputHandler
## 输入处理器
## 处理多平台输入(键盘、触摸、虚拟摇杆)
# 设备类型枚举
enum DeviceType {
DESKTOP,
MOBILE,
UNKNOWN
}
var current_device: DeviceType = DeviceType.UNKNOWN
# 信号
signal move_input(direction: Vector2)
signal interact_input()
signal device_detected(device: DeviceType)
# signal ui_input(action: String) # 预留用于 UI 导航(暂未使用)
# 虚拟控制器引用
var virtual_joystick: Control = null
var virtual_interact_button: Control = null
# 输入状态控制
var movement_enabled: bool = true
var dialogue_box_active: bool = false
func _ready():
"""初始化输入处理器"""
# 检测设备类型
_detect_device()
# 根据设备类型设置虚拟控制器
if current_device == DeviceType.MOBILE:
setup_virtual_controls()
func _process(_delta: float):
"""每帧处理输入"""
# 检查对话框状态
_update_dialogue_box_status()
# 获取移动输入(只有在移动启用且对话框未激活时)
var direction = Vector2.ZERO
if movement_enabled and not dialogue_box_active:
direction = get_move_direction()
move_input.emit(direction)
# 检查交互输入(只有在对话框未激活时才处理)
if not dialogue_box_active and is_interact_pressed():
interact_input.emit()
## 获取移动方向
func get_move_direction() -> Vector2:
"""
获取当前的移动方向输入
@return: 归一化的方向向量
"""
var direction = Vector2.ZERO
if current_device == DeviceType.DESKTOP:
# 键盘输入
direction = _get_keyboard_direction()
elif current_device == DeviceType.MOBILE:
# 触摸/虚拟摇杆输入
direction = _get_virtual_joystick_direction()
return direction.normalized() if direction.length() > 0 else Vector2.ZERO
## 检查交互键是否按下
func is_interact_pressed() -> bool:
"""
检查交互键是否刚被按下
@return: 是否按下
"""
if current_device == DeviceType.DESKTOP:
return Input.is_action_just_pressed("interact")
elif current_device == DeviceType.MOBILE:
return _is_virtual_interact_pressed()
return false
## 设置虚拟控制器
func setup_virtual_controls() -> void:
"""
设置移动端虚拟控制器
创建虚拟摇杆和交互按钮
"""
# 查找或创建 UI 容器
var ui_layer = get_tree().root.get_node_or_null("Main/UILayer")
if not ui_layer:
push_warning("UILayer not found, cannot setup virtual controls")
return
# 创建虚拟摇杆
if not virtual_joystick:
virtual_joystick = VirtualJoystick.new()
virtual_joystick.name = "VirtualJoystick"
virtual_joystick.position = Vector2(50, get_viewport().get_visible_rect().size.y - 150)
ui_layer.add_child(virtual_joystick)
# 创建虚拟交互按钮
if not virtual_interact_button:
virtual_interact_button = VirtualButton.new()
virtual_interact_button.name = "VirtualInteractButton"
virtual_interact_button.button_text = "E"
virtual_interact_button.position = Vector2(
get_viewport().get_visible_rect().size.x - 130,
get_viewport().get_visible_rect().size.y - 130
)
ui_layer.add_child(virtual_interact_button)
print("Virtual controls setup complete")
## 检测设备类型
func _detect_device() -> void:
"""
检测当前设备类型(桌面或移动)
"""
# 检查是否在移动平台上运行
var os_name = OS.get_name()
if os_name in ["Android", "iOS"]:
current_device = DeviceType.MOBILE
elif os_name in ["Windows", "macOS", "Linux", "FreeBSD", "NetBSD", "OpenBSD", "BSD"]:
current_device = DeviceType.DESKTOP
else:
# 默认为桌面
current_device = DeviceType.DESKTOP
# 也可以通过触摸屏检测
if DisplayServer.is_touchscreen_available():
current_device = DeviceType.MOBILE
device_detected.emit(current_device)
print("Device detected: ", DeviceType.keys()[current_device])
## 获取键盘方向输入
func _get_keyboard_direction() -> Vector2:
"""
从键盘获取方向输入
@return: 方向向量
"""
var direction = Vector2.ZERO
# 使用独立的移动动作(不影响 UI 输入)
if Input.is_action_pressed("move_right"):
direction.x += 1
if Input.is_action_pressed("move_left"):
direction.x -= 1
if Input.is_action_pressed("move_down"):
direction.y += 1
if Input.is_action_pressed("move_up"):
direction.y -= 1
return direction
## 获取虚拟摇杆方向
func _get_virtual_joystick_direction() -> Vector2:
"""
从虚拟摇杆获取方向输入
@return: 方向向量
"""
# 将在子任务 6.3 中实现
# 现在返回零向量
if virtual_joystick and virtual_joystick.has_method("get_direction"):
return virtual_joystick.get_direction()
return Vector2.ZERO
## 检查虚拟交互按钮
func _is_virtual_interact_pressed() -> bool:
"""
检查虚拟交互按钮是否按下
@return: 是否按下
"""
# 将在子任务 6.3 中实现
if virtual_interact_button and virtual_interact_button.has_method("is_pressed"):
return virtual_interact_button.is_pressed()
return false
## 获取当前设备类型
func get_device_type() -> DeviceType:
"""
获取当前检测到的设备类型
@return: 设备类型
"""
return current_device
## 启用/禁用输入处理
func set_input_enabled(enabled: bool) -> void:
"""
启用或禁用输入处理
@param enabled: 是否启用
"""
set_process(enabled)
# 如果禁用,清除所有输入状态
if not enabled:
move_input.emit(Vector2.ZERO)
## 启用/禁用移动输入
func set_movement_enabled(enabled: bool) -> void:
"""
启用或禁用移动输入
@param enabled: 是否启用移动
"""
movement_enabled = enabled
# 如果禁用移动,立即发送零向量停止角色移动
if not enabled:
move_input.emit(Vector2.ZERO)
## 更新对话框状态
func _update_dialogue_box_status() -> void:
"""检测对话框是否处于活动状态"""
var was_active = dialogue_box_active
# 使用更通用的方法检测UI焦点状态
dialogue_box_active = _is_ui_focused()
# 如果对话框状态发生变化,立即停止移动
if was_active != dialogue_box_active:
# 无论是激活还是关闭对话框,都强制停止移动并清除输入状态
_clear_all_movement_state()
if dialogue_box_active:
print("UI focused - movement disabled and cleared")
else:
print("UI unfocused - movement cleared and reset")
## 检查是否有UI控件获得焦点
func _is_ui_focused() -> bool:
"""
检查是否有UI控件获得焦点如输入框或对话框是否可见
@return: 是否有UI控件获得焦点
"""
# 首先检查对话框是否可见
var main_scene = get_tree().root.get_node_or_null("Main")
if main_scene:
var ui_layer = main_scene.get_node_or_null("UILayer")
if ui_layer:
var dialogue_box = ui_layer.get_node_or_null("DialogueBox")
if dialogue_box and dialogue_box.visible:
return true
# 然后检查是否有输入控件获得焦点
var focused_control = get_viewport().gui_get_focus_owner()
if focused_control:
return focused_control is LineEdit or focused_control is TextEdit
return false
## 清除所有移动状态
func _clear_all_movement_state() -> void:
"""清除所有移动相关的状态和输入"""
# 发送零向量停止移动
move_input.emit(Vector2.ZERO)
# 清除所有可能的输入状态
Input.action_release("move_up")
Input.action_release("move_down")
Input.action_release("move_left")
Input.action_release("move_right")
print("All movement state cleared")