拓冰建站拓冰建站
首页 / 资讯中心 / 正文

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 VirtualJoystick4.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_pos6.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 true6.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 ?
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门