云计算百科
云计算领域专业知识百科平台

6、输入系统

第6章 输入系统

6.1 Input Map 配置

Input Map 是 Godot 的输入映射系统,将物理输入(按键/手柄按钮)映射为逻辑动作。

设置步骤

项目 → 项目设置 → 输入映射 (Input Map)

添加动作:
1. 输入动作名称(如 "jump")→ 点击"添加"
2. 点击动作旁边的 "+" 按钮
3. 按下要绑定的按键(如空格键)
4. 可以绑定多个按键到同一个动作

推荐的动作命名

移动类:
– move_left (A键 / 左箭头 / 左摇杆)
– move_right (D键 / 右箭头 / 左摇杆)
– move_up (W键 / 上箭头 / 左摇杆)
– move_down (S键 / 下箭头 / 左摇杆)

动作类:
– jump (空格键 / 手柄A)
– attack (J键 / 手柄X)
– dash (Shift键 / 手柄B)
– interact (E键 / 手柄Y)

UI类:
– ui_accept (回车 / 手柄A) ← Godot 内置
– ui_cancel (Esc / 手柄B) ← Godot 内置
– pause (Esc / 开始键)

6.2 Input 单例

# 检查输入状态(在 _process 或 _physics_process 中使用)

# 是否正在按下
if Input.is_action_pressed("move_right"):
velocity.x += speed

# 是否刚刚按下(只触发一次)
if Input.is_action_just_pressed("jump"):
jump()

# 是否刚刚松开
if Input.is_action_just_released("jump"):
# 可变跳跃高度:松开跳跃键时截断上升速度
if velocity.y < 0:
velocity.y *= 0.5

# 获取轴值(-1 到 1)
var horizontal: float = Input.get_axis("move_left", "move_right")
var vertical: float = Input.get_axis("move_up", "move_down")

# 获取方向向量(标准化)
var direction: Vector2 = Input.get_vector("move_left", "move_right", "move_up", "move_down")
# 返回值已经是标准化的,对角线不会更快

6.3 输入传播链

1. Engine 接收原始输入事件

2. _input(event) ← 场景树中所有节点都能收到

3. Control._gui_input() ← UI 控件尝试处理(鼠标点击按钮等)
↓ (如果 UI 没有消费这个事件)
4. _unhandled_input() ← 没有被 UI 消费的事件

5. _unhandled_key_input() ← 专门处理键盘事件

为什么需要这个机制?

# 场景:玩家点击了暂停按钮
# – UI 按钮的 _gui_input 消费了点击事件
# – 游戏场景的 _unhandled_input 不会收到这个点击
# – 这样就不会在点按钮的同时触发游戏中的点击操作

# 推荐:游戏逻辑放在 _unhandled_input 中
func _unhandled_input(event: InputEvent) -> void:
# 这里处理游戏输入(不会被 UI 干扰)
if event.is_action_pressed("attack"):
attack()
if event.is_action_pressed("jump"):
jump()

6.4 VirtualJoystick(4.7 新增)

Godot 4.7 内置了 VirtualJoystick 节点,无需第三方插件。

设置

添加节点 → VirtualJoystick
属性配置:
– texture_base: 底盘图片
– texture_tip: 摇杆图片
– action_left: 左动作名
– action_right: 右动作名
– action_up: 上动作名
– action_down: 下动作名
– mode: Fixed / Dynamic / Following

代码使用

extends Node2D

# VirtualJoystick 自动将输入映射到指定的动作
# 之后使用 Input.get_axis() / Input.get_vector() 获取输入
# 与键盘/手柄输入完全一致

func _physics_process(delta: float) -> void:
# 这段代码同时支持键盘、手柄和虚拟摇杆
var direction := Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity = direction * speed
move_and_slide()

6.5 鼠标输入

func _unhandled_input(event: InputEvent) -> void:
# 鼠标移动
if event is InputEventMouseMotion:
var mouse_pos: Vector2 = event.position
var relative: Vector2 = event.relative # 鼠标移动增量
look_at(mouse_pos)

# 鼠标按钮
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
shoot()
if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
secondary_attack()
# 滚轮
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
zoom_in()
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
zoom_out()

# 获取鼠标全局位置
func _process(delta: float) -> void:
var mouse_pos := get_global_mouse_position()
crosshair.position = mouse_pos

6.6 平台差异化输入策略

# 检测当前输入类型
func get_input_type() -> String:
# 可以通过最后输入的事件类型判断
return "keyboard" # 或 "touch" 或 "gamepad"

# 检测平台
func is_mobile() -> bool:
return OS.get_name() in ["Android", "iOS"]

# 根据平台显示不同提示
func show_control_hints() -> void:
if is_mobile():
$TouchControls.visible = true
$KeyboardHints.visible = false
else:
$TouchControls.visible = false
$KeyboardHints.visible = true

6.7 输入缓冲与连招

# 输入缓冲:在角色还未准备好时缓存输入,准备好后立即执行
# 这样玩家不需要精确时机,手感更好

var input_buffer: Array[String] = []
const BUFFER_TIME: float = 0.15 # 150ms 缓冲窗口
var buffer_timers: Dictionary = {}

func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("attack"):
buffer_action("attack")
if event.is_action_pressed("jump"):
buffer_action("jump")

func buffer_action(action: String) -> void:
input_buffer.append(action)
buffer_timers[action] = BUFFER_TIME

func _process(delta: float) -> void:
# 更新缓冲计时器
var expired: Array[String] = []
for action in buffer_timers:
buffer_timers[action] -= delta
if buffer_timers[action] <= 0:
expired.append(action)
for action in expired:
buffer_timers.erase(action)
input_buffer.erase(action)

func has_buffered(action: String) -> bool:
return action in input_buffer

func consume_buffer(action: String) -> void:
input_buffer.erase(action)
buffer_timers.erase(action)

# 使用:在状态机转换时检查缓冲
func _on_attack_finished() -> void:
if has_buffered("attack"):
consume_buffer("attack")
attack() # 立即执行缓冲的攻击
else:
state_machine.travel("idle")

6.8 运行时重映射按键

# 保存自定义按键映射
func save_key_bindings() -> void:
var config := ConfigFile.new()

for action in InputMap.get_actions():
var events := InputMap.action_get_events(action)
if not events.is_empty():
config.set_value("keybindings", action, events[0])

config.save("user://keybindings.cfg")

# 加载自定义按键映射
func load_key_bindings() -> void:
var config := ConfigFile.new()
if config.load("user://keybindings.cfg") != OK:
return

for action in config.get_section_keys("keybindings"):
var event: InputEvent = config.get_value("keybindings", action)
InputMap.action_erase_events(action)
InputMap.action_add_event(action, event)

# 重新绑定按键
func rebind_action(action: String, new_event: InputEvent) -> void:
InputMap.action_erase_events(action)
InputMap.action_add_event(action, new_event)

实操练习

练习:完整输入管理器

# input_manager.gd – Autoload
extends Node

signal input_device_changed(device_type: String)

enum DeviceType { KEYBOARD, GAMEPAD, TOUCH }
var current_device: DeviceType = DeviceType.KEYBOARD

func _input(event: InputEvent) -> void:
if event is InputEventKey or event is InputEventMouse:
if current_device != DeviceType.KEYBOARD:
current_device = DeviceType.KEYBOARD
input_device_changed.emit("keyboard")
elif event is InputEventJoypadButton or event is InputEventJoypadMotion:
if current_device != DeviceType.GAMEPAD:
current_device = DeviceType.GAMEPAD
input_device_changed.emit("gamepad")

func get_action_hint(action: String) -> String:
match current_device:
DeviceType.KEYBOARD:
return get_key_name(action)
DeviceType.GAMEPAD:
return get_gamepad_button_name(action)
_:
return action

func get_key_name(action: String) -> String:
var events := InputMap.action_get_events(action)
for event in events:
if event is InputEventKey:
return OS.get_keycode_string(event.keycode)
return "?"

赞(0)
未经允许不得转载:网硕互联帮助中心 » 6、输入系统
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!