第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 "?"
网硕互联帮助中心


![2019年下半年网络管理员[案例分析]考试下午真题(答案+ 解析)-网硕互联帮助中心](https://www.wsisp.com/helps/wp-content/uploads/2026/09/20260909153309-6aa17c359df88-220x150.png)




评论前必须登录!
注册