强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Godot 4 GDScript 教程 / 输入系统(InputMap)

输入系统(InputMap)

Godot 的输入系统通过 InputMap 将物理输入(键盘、鼠标、手柄)抽象为逻辑动作,实现跨平台输入处理。本章详细介绍 InputMap 配置、Input 类 API、输入事件层次以及高级输入处理模式。

1. 输入映射 InputMap

1.1 编辑器中配置 InputMap

通过 项目 > 项目设置 > 输入映射 配置输入动作:

步骤操作
1在"添加新动作"输入框中输入动作名称
2点击"添加"按钮
3点击动作右侧的"+“按钮添加按键
4按下要绑定的键或按钮
5点击"确定"完成绑定

1.2 推荐的输入动作配置

动作名称键盘鼠标手柄说明
move_leftA, ←左摇杆左向左移动
move_rightD, →左摇杆右向右移动
move_upW, ↑左摇杆上向上移动
move_downS, ↓左摇杆下向下移动
jumpSpace按钮A跳跃
attack鼠标左键按钮X攻击
interactE按钮Y交互
dashShift左摇杆按下冲刺
pauseEscapeStart暂停
inventoryI, Tab按钮B背包

1.3 代码中管理 InputMap

extends Node

func _ready() -> void:
    # 检查动作是否存在
    if not InputMap.has_action("jump"):
        # 创建新动作
        InputMap.add_action("jump")
        
        # 添加键盘映射
        var key_event := InputEventKey.new()
        key_event.keycode = KEY_SPACE
        InputMap.action_add_event("jump", key_event)
        
        # 添加手柄映射
        var joy_event := InputEventJoypadButton.new()
        joy_event.button_index = JOY_BUTTON_A
        InputMap.action_add_event("jump", joy_event)
    
    # 删除动作
    if InputMap.has_action("old_action"):
        InputMap.action_erase("old_action")
    
    # 获取动作列表
    var actions := InputMap.get_actions()
    print("已注册动作: %s" % str(actions))
    
    # 获取动作的事件列表
    var jump_events := InputMap.action_get_events("jump")
    print("Jump 绑定: %d 个事件" % jump_events.size())
    
    # 清除动作的所有映射
    InputMap.action_erase_events("jump")
    
    # 修改死区
    InputMap.action_set_deadzone("move_left", 0.2)

2. Input 类 API

2.1 查询输入状态

extends CharacterBody2D

@export var speed: float = 300.0

func _physics_process(delta: float) -> void:
    # ── 持续按住 ──────────────────────────
    # is_action_pressed: 按住时每帧返回 true
    if Input.is_action_pressed("move_right"):
        velocity.x = speed
    elif Input.is_action_pressed("move_left"):
        velocity.x = -speed
    else:
        velocity.x = 0
    
    # ── 刚按下 ──────────────────────────
    # is_action_just_pressed: 按下的那一帧返回 true
    if Input.is_action_just_pressed("jump"):
        velocity.y = -400.0
    
    # ── 刚释放 ──────────────────────────
    # is_action_just_released: 释放的那一帧返回 true
    if Input.is_action_just_released("jump"):
        if velocity.y < 0:
            velocity.y *= 0.5  # 短按跳跃低一点
    
    move_and_slide()

2.2 获取输入轴

extends CharacterBody2D

func _physics_process(delta: float) -> void:
    # get_axis: 返回 -1 到 1 的浮点值
    # get_axis("负方向", "正方向")
    var horizontal := Input.get_axis("move_left", "move_right")
    var vertical := Input.get_axis("move_up", "move_down")
    
    velocity = Vector2(horizontal, vertical) * 300.0
    move_and_slide()

# 更简洁的写法
func get_movement_vector() -> Vector2:
    return Vector2(
        Input.get_axis("move_left", "move_right"),
        Input.get_axis("move_up", "move_down")
    ).limit_length(1.0)  # 限制对角线移动速度

2.3 鼠标输入

extends Node2D

func _unhandled_input(event: InputEvent) -> void:
    # 鼠标按键
    if event is InputEventMouseButton:
        var mouse_event := event as InputEventMouseButton
        match mouse_event.button_index:
            MOUSE_BUTTON_LEFT:
                if mouse_event.pressed:
                    _on_left_click(mouse_event.position)
            MOUSE_BUTTON_RIGHT:
                if mouse_event.pressed:
                    _on_right_click(mouse_event.position)
            MOUSE_BUTTON_WHEEL_UP:
                _zoom_in()
            MOUSE_BUTTON_WHEEL_DOWN:
                _zoom_out()
    
    # 鼠标移动
    if event is InputEventMouseMotion:
        var motion := event as InputEventMouseMotion
        _on_mouse_move(motion.position, motion.relative)
    
    # 触摸输入(移动端)
    if event is InputEventScreenTouch:
        var touch := event as InputEventScreenTouch
        if touch.pressed:
            _on_touch(touch.position, touch.index)

func _on_left_click(pos: Vector2) -> void:
    print("左键点击: %s" % str(pos))

func _on_right_click(pos: Vector2) -> void:
    print("右键点击: %s" % str(pos))

func _on_mouse_move(pos: Vector2, relative: Vector2) -> void:
    # relative 是鼠标移动的相对距离
    pass

func _zoom_in() -> void:
    print("放大")

func _zoom_out() -> void:
    print("缩小")

2.4 键盘输入

extends Node

func _unhandled_key_input(event: InputEvent) -> void:
    if event is InputEventKey:
        var key_event := event as InputEventKey
        if key_event.pressed and not key_event.echo:
            # 获取按键
            var key := key_event.keycode
            var physical_key := key_event.physical_keycode
            var key_string := OS.get_keycode_string(key)
            
            match key:
                KEY_F1:
                    _toggle_debug()
                KEY_F5:
                    _quick_save()
                KEY_F9:
                    _quick_load()
                KEY_ESCAPE:
                    _toggle_pause()
                KEY_F11:
                    _toggle_fullscreen()
                _:
                    print("按键: %s" % key_string)

func _toggle_fullscreen() -> void:
    if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN:
        DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
    else:
        DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)

func _toggle_debug() -> void:
    # 切换调试信息显示
    var debug_overlay := get_node_or_null("/root/DebugOverlay")
    if debug_overlay:
        debug_overlay.visible = !debug_overlay.visible

3. 手柄输入

3.1 手柄配置

extends Node

func _ready() -> void:
    # 连接手柄连接/断开信号
    Input.joy_connection_changed.connect(_on_joy_connection_changed)
    
    # 列出已连接的手柄
    for joy_id in Input.get_connected_joypads():
        var joy_name := Input.get_joy_name(joy_id)
        var joy_guid := Input.get_joy_guid(joy_id)
        print("手柄 %d: %s (%s)" % [joy_id, joy_name, joy_guid])

func _on_joy_connection_changed(device_id: int, connected: bool) -> void:
    if connected:
        print("手柄已连接: %s" % Input.get_joy_name(device_id))
    else:
        print("手柄已断开: 设备 %d" % device_id)

3.2 手柄震动

extends Node

# 手柄震动反馈
func trigger_vibration(
    joy_id: int = 0,
    weak_magnitude: float = 0.5,
    strong_magnitude: float = 0.5,
    duration: float = 0.2
) -> void:
    Input.start_joy_vibration(joy_id, weak_magnitude, strong_magnitude, duration)

# 受伤震动
func _on_damage_taken() -> void:
    trigger_vibration(0, 0.3, 0.7, 0.15)

# 爆炸震动
func _on_explosion() -> void:
    trigger_vibration(0, 0.5, 1.0, 0.3)

# 停止震动
func stop_vibration(joy_id: int = 0) -> void:
    Input.stop_joy_vibration(joy_id)

3.3 手柄轴输入

extends CharacterBody2D

@export var move_speed: float = 300.0
@export var deadzone: float = 0.2

func _physics_process(delta: float) -> void:
    # 左摇杆
    var left_stick := Vector2(
        Input.get_joy_axis(0, JOY_AXIS_LEFT_X),
        Input.get_joy_axis(0, JOY_AXIS_LEFT_Y)
    )
    
    # 应用死区
    if left_stick.length() < deadzone:
        left_stick = Vector2.ZERO
    else:
        left_stick = left_stick.normalized() * ((left_stick.length() - deadzone) / (1.0 - deadzone))
    
    # 右摇杆(通常用于瞄准)
    var right_stick := Vector2(
        Input.get_joy_axis(0, JOY_AXIS_RIGHT_X),
        Input.get_joy_axis(0, JOY_AXIS_RIGHT_Y)
    )
    
    # 扳机键(LT/RT)
    var left_trigger := (Input.get_joy_axis(0, JOY_AXIS_TRIGGER_LEFT) + 1.0) / 2.0
    var right_trigger := (Input.get_joy_axis(0, JOY_AXIS_TRIGGER_RIGHT) + 1.0) / 2.0
    
    velocity = left_stick * move_speed
    move_and_slide()

4. _input 事件层次

4.1 事件传播顺序

输入事件传播路径:
1. _input()         → 场景树根节点开始,逐级向下传播
2. _unhandled_input() → 未被 _input 处理的事件
3. _unhandled_key_input() → 未处理的键盘事件

GUI 事件优先级更高:
- Control 节点先处理输入
- 如果 GUI 消费了事件,不会传播到 _input

4.2 完整的输入处理

extends Node

# 1. _input: 最先处理,可以过滤/修改事件
func _input(event: InputEvent) -> void:
    # 可以在这里阻止事件传播
    if event.is_action_pressed("ui_accept"):
        # 如果是 UI 事件,标记为已处理
        get_viewport().set_input_as_handled()
    
    # 全局输入处理(如调试快捷键)
    if event is InputEventKey:
        var key_event := event as InputEventKey
        if key_event.pressed and key_event.keycode == KEY_F1:
            _toggle_debug()
            get_viewport().set_input_as_handled()

# 2. _unhandled_input: 未被 GUI 或 _input 处理的事件
func _unhandled_input(event: InputEvent) -> void:
    # 游戏逻辑输入处理
    if event.is_action_pressed("attack"):
        _perform_attack()
    elif event.is_action_pressed("interact"):
        _interact()

# 3. _unhandled_key_input: 专门处理键盘
func _unhandled_key_input(event: InputEvent) -> void:
    if event is InputEventKey and event.pressed:
        match event.keycode:
            KEY_TAB:
                _toggle_inventory()
            KEY_M:
                _toggle_map()

# 4. 处理鼠标输入(放在 _unhandled_input 中避免被 UI 拦截)
# func _unhandled_input(event: InputEvent) -> void:
#     if event is InputEventMouseButton:
#         pass

# ⚠️ 不要同时使用 _input 和 _unhandled_input 处理同一个动作

⚠️ 注意: 游戏逻辑输入应使用 _unhandled_input,这样当 UI 打开时输入会被 UI 消费,不会触发游戏操作。

5. 触摸输入

5.1 基本触摸

extends Node2D

var touch_points: Dictionary = {}
var is_touching: bool = false

func _input(event: InputEvent) -> void:
    if event is InputEventScreenTouch:
        var touch := event as InputEventScreenTouch
        if touch.pressed:
            # 触摸按下
            touch_points[touch.index] = touch.position
            _on_touch_start(touch.index, touch.position)
        else:
            # 触摸释放
            touch_points.erase(touch.index)
            _on_touch_end(touch.index, touch.position)
    
    elif event is InputEventScreenDrag:
        var drag := event as InputEventScreenDrag
        if touch_points.has(drag.index):
            var old_pos: Vector2 = touch_points[drag.index]
            touch_points[drag.index] = drag.position
            _on_touch_drag(drag.index, drag.position, drag.position - old_pos)

func _on_touch_start(finger: int, pos: Vector2) -> void:
    print("触摸开始: 手指 %d, 位置 %s" % [finger, str(pos)])

func _on_touch_end(finger: int, pos: Vector2) -> void:
    print("触摸结束: 手指 %d" % finger)

func _on_touch_drag(finger: int, pos: Vector2, delta: Vector2) -> void:
    print("触摸拖动: 手指 %d, 位移 %s" % [finger, str(delta)])

5.2 手势识别

extends Node

## 简单手势识别器

signal swipe_detected(direction: Vector2)
signal pinch_detected(scale_factor: float)
signal tap_detected(pos: Vector2)
signal long_press_detected(pos: Vector2)

@export var swipe_threshold: float = 50.0
@export var long_press_time: float = 0.5

var _touch_start: Dictionary = {}
var _touch_time: float = 0.0
var _is_touching: bool = false

func _input(event: InputEvent) -> void:
    if event is InputEventScreenTouch:
        var touch := event as InputEventScreenTouch
        if touch.pressed:
            _touch_start[touch.index] = touch.position
            _touch_time = Time.get_ticks_msec() / 1000.0
            _is_touching = true
        else:
            if _is_touching and _touch_start.has(touch.index):
                var start_pos: Vector2 = _touch_start[touch.index]
                var delta: Vector2 = touch.position - start_pos
                var elapsed := Time.get_ticks_msec() / 1000.0 - _touch_time
                
                if delta.length() < 10 and elapsed < long_press_time:
                    tap_detected.emit(touch.position)
                elif delta.length() >= swipe_threshold:
                    swipe_detected.emit(delta.normalized())
                elif elapsed >= long_press_time:
                    long_press_detected.emit(touch.position)
            
            _touch_start.erase(touch.index)
            if _touch_start.is_empty():
                _is_touching = false

# 使用手势
func _ready() -> void:
    swipe_detected.connect(_on_swipe)
    tap_detected.connect(_on_tap)

func _on_swipe(direction: Vector2) -> void:
    if direction.x > 0.7:
        print("向右滑动")
    elif direction.x < -0.7:
        print("向左滑动")
    elif direction.y < -0.7:
        print("向上滑动")
    elif direction.y > 0.7:
        print("向下滑动")

func _on_tap(pos: Vector2) -> void:
    print("点击: %s" % str(pos))

6. InputEventAction

6.1 创建自定义输入事件

extends Node

# 动态创建并发送输入事件
func simulate_action(action_name: String, pressed: bool = true) -> void:
    var event := InputEventAction.new()
    event.action = action_name
    event.pressed = pressed
    event.strength = 1.0 if pressed else 0.0
    Input.parse_input_event(event)

# 模拟按键
func simulate_key(keycode: Key, pressed: bool = true) -> void:
    var event := InputEventKey.new()
    event.keycode = keycode
    event.pressed = pressed
    event.physical_keycode = keycode
    Input.parse_input_event(event)

# 模拟鼠标点击
func simulate_mouse_click(pos: Vector2, button: MouseButton = MOUSE_BUTTON_LEFT) -> void:
    var event := InputEventMouseButton.new()
    event.button_index = button
    event.position = pos
    event.pressed = true
    Input.parse_input_event(event)
    
    # 释放
    await get_tree().create_timer(0.05).timeout
    event.pressed = false
    Input.parse_input_event(event)

# 使用示例:AI 控制器
func _ai_control() -> void:
    # AI 模拟玩家输入
    simulate_action("move_right", true)
    await get_tree().create_timer(1.0).timeout
    simulate_action("move_right", false)
    simulate_action("jump", true)
    await get_tree().create_timer(0.1).timeout
    simulate_action("jump", false)

7. 虚拟摇杆

7.1 虚拟摇杆实现

# virtual_joystick.gd
extends Control

signal joystick_input(direction: Vector2)

@export var radius: float = 80.0
@export var dead_zone: float = 10.0
@export var clamp_zone: float = 70.0
@export var use_input_actions: bool = true
@export var action_left: String = "move_left"
@export var action_right: String = "move_right"
@export var action_up: String = "move_up"
@export var action_down: String = "move_down"

var _is_pressed: bool = false
var _touch_index: int = -1
var _output: Vector2 = Vector2.ZERO

@onready var background: TextureRect = $Background
@onready var handle: TextureRect = $Background/Handle

func _ready() -> void:
    # 确保可以接收输入
    mouse_filter = Control.MOUSE_FILTER_STOP

func _input(event: InputEvent) -> void:
    if event is InputEventScreenTouch:
        var touch := event as InputEventScreenTouch
        if touch.pressed and _is_point_inside(touch.position):
            _is_pressed = true
            _touch_index = touch.index
            _update_handle(touch.position)
            get_viewport().set_input_as_handled()
        elif not touch.pressed and touch.index == _touch_index:
            _reset()
            get_viewport().set_input_as_handled()
    
    elif event is InputEventScreenDrag:
        var drag := event as InputEventScreenDrag
        if drag.index == _touch_index:
            _update_handle(drag.position)
            get_viewport().set_input_as_handled()

func _is_point_inside(point: Vector2) -> bool:
    var center := global_position + size / 2
    return point.distance_to(center) <= radius * 2

func _update_handle(touch_pos: Vector2) -> void:
    var center := global_position + size / 2
    var delta: Vector2 = touch_pos - center
    
    # 限制在半径内
    if delta.length() > clamp_zone:
        delta = delta.normalized() * clamp_zone
    
    # 更新手柄位置
    handle.position = Vector2(radius, radius) + delta - handle.size / 2
    
    # 计算输出
    if delta.length() > dead_zone:
        _output = delta / clamp_zone
    else:
        _output = Vector2.ZERO
    
    # 发送信号
    joystick_input.emit(_output)
    
    # 模拟输入动作
    if use_input_actions:
        _simulate_actions()

func _reset() -> void:
    _is_pressed = false
    _touch_index = -1
    _output = Vector2.ZERO
    handle.position = Vector2(radius - handle.size.x / 2, radius - handle.size.y / 2)
    joystick_input.emit(Vector2.ZERO)
    
    if use_input_actions:
        _release_actions()

func _simulate_actions() -> void:
    _set_action(action_left, _output.x < -0.3)
    _set_action(action_right, _output.x > 0.3)
    _set_action(action_up, _output.y < -0.3)
    _set_action(action_down, _output.y > 0.3)

func _release_actions() -> void:
    _set_action(action_left, false)
    _set_action(action_right, false)
    _set_action(action_up, false)
    _set_action(action_down, false)

func _set_action(action: String, pressed: bool) -> void:
    var event := InputEventAction.new()
    event.action = action
    event.pressed = pressed
    event.strength = _output.length() if pressed else 0.0
    Input.parse_input_event(event)

func get_output() -> Vector2:
    return _output

func get_output_angle() -> float:
    return _output.angle() if _output.length() > 0 else 0.0

8. 自定义输入管理器

# autoload/input_manager.gd
extends Node

## 全局输入管理器

signal input_scheme_changed(scheme: String)

enum InputScheme { KEYBOARD_MOUSE, GAMEPAD, TOUCH }

var current_scheme: InputScheme = InputScheme.KEYBOARD_MOUSE
var is_using_gamepad: bool = false
var vibration_enabled: bool = true

func _ready() -> void:
    # 监听输入设备切换
    Input.joy_connection_changed.connect(_on_joy_connection_changed)

func _input(event: InputEvent) -> void:
    # 自动检测输入设备
    if event is InputEventKey or event is InputEventMouse:
        if current_scheme != InputScheme.KEYBOARD_MOUSE:
            current_scheme = InputScheme.KEYBOARD_MOUSE
            is_using_gamepad = false
            input_scheme_changed.emit("keyboard_mouse")
    
    elif event is InputEventJoypadButton or event is InputEventJoypadMotion:
        if not is_using_gamepad:
            current_scheme = InputScheme.GAMEPAD
            is_using_gamepad = true
            input_scheme_changed.emit("gamepad")
    
    elif event is InputEventScreenTouch or event is InputEventScreenDrag:
        if current_scheme != InputScheme.TOUCH:
            current_scheme = InputScheme.TOUCH
            input_scheme_changed.emit("touch")

func get_input_icon(action: String) -> Texture2D:
    """根据当前输入方案返回对应的图标"""
    match current_scheme:
        InputScheme.KEYBOARD_MOUSE:
            return _get_keyboard_icon(action)
        InputScheme.GAMEPAD:
            return _get_gamepad_icon(action)
        InputScheme.TOUCH:
            return _get_touch_icon(action)
    return null

func _get_keyboard_icon(action: String) -> Texture2D:
    var events := InputMap.action_get_events(action)
    for event in events:
        if event is InputEventKey:
            # 返回键盘按键图标
            return load("res://assets/ui/keys/%s.png" % OS.get_keycode_string(event.keycode))
    return null

func _get_gamepad_icon(action: String) -> Texture2D:
    # 返回手柄按钮图标
    return null

func _get_touch_icon(action: String) -> Texture2D:
    # 返回触摸图标
    return null

func trigger_vibration(weak: float, strong: float, duration: float) -> void:
    if vibration_enabled and is_using_gamepad:
        Input.start_joy_vibration(0, weak, strong, duration)

func _on_joy_connection_changed(device: int, connected: bool) -> void:
    if connected:
        print("手柄已连接: %s" % Input.get_joy_name(device))

9. 无障碍输入适配

# 无障碍功能实现
extends Node

## 输入辅助设置
var auto_run: bool = false
var hold_to_toggle: Dictionary = {}  # 将"按住"改为"切换"
var input_remap: Dictionary = {}
var colorblind_mode: int = 0  # 0=正常, 1=红色盲, 2=绿色盲, 3=蓝色盲

func toggle_auto_run() -> void:
    auto_run = !auto_run

func toggle_action(action: String) -> bool:
    """切换模式:按一次开启,再按一次关闭"""
    if not hold_to_toggle.has(action):
        hold_to_toggle[action] = false
    hold_to_toggle[action] = !hold_to_toggle[action]
    return hold_to_toggle[action]

func is_action_active(action: String) -> bool:
    """检查动作是否激活(考虑切换模式)"""
    if hold_to_toggle.has(action):
        return hold_to_toggle[action]
    return Input.is_action_pressed(action)

# 按键重映射
func remap_action(action: String, new_key: Key) -> void:
    InputMap.action_erase_events(action)
    var event := InputEventKey.new()
    event.keycode = new_key
    InputMap.action_add_event(action, event)
    input_remap[action] = new_key
    _save_remap()

func _save_remap() -> void:
    var config := ConfigFile.new()
    for action in input_remap:
        config.set_value("input", action, input_remap[action])
    config.save("user://input.cfg")

func _load_remap() -> void:
    var config := ConfigFile.new()
    if config.load("user://input.cfg") == OK:
        for action in config.get_section_keys("input"):
            var key: Key = config.get_value("input", action)
            remap_action(action, key)

10. 游戏开发场景

场景:双人本地对战输入

extends Node

## 双人输入配置

var player1_device: int = -1  # -1 = 键盘, 0+ = 手柄ID
var player2_device: int = 0

func get_player_input(player: int, action: String) -> float:
    var device: int = player1_device if player == 1 else player2_device
    
    if device == -1:
        # 键盘玩家
        return Input.get_axis(
            action + "_keyboard_negative",
            action + "_keyboard_positive"
        )
    else:
        # 手柄玩家
        match action:
            "move_horizontal":
                return Input.get_joy_axis(device, JOY_AXIS_LEFT_X)
            "move_vertical":
                return Input.get_joy_axis(device, JOY_AXIS_LEFT_Y)
            "aim_horizontal":
                return Input.get_joy_axis(device, JOY_AXIS_RIGHT_X)
            "aim_vertical":
                return Input.get_joy_axis(device, JOY_AXIS_RIGHT_Y)
    return 0.0

func is_player_action_pressed(player: int, action: String) -> bool:
    var device: int = player1_device if player == 1 else player2_device
    
    if device == -1:
        return Input.is_action_pressed(action + "_p1")
    else:
        return Input.is_action_pressed(action + "_p2")

func get_player_movement(player: int) -> Vector2:
    return Vector2(
        get_player_input(player, "move_horizontal"),
        get_player_input(player, "move_vertical")
    )

场景:输入缓冲系统

extends Node

## 输入缓冲 - 改善游戏手感

@export var buffer_time: float = 0.15  # 缓冲时间(秒)

var _input_buffer: Dictionary = {}

func buffer_action(action: String) -> void:
    """记录动作按下时间"""
    _input_buffer[action] = Time.get_ticks_msec() / 1000.0

func consume_action(action: String) -> bool:
    """消费缓冲中的动作,返回是否有缓冲"""
    if _input_buffer.has(action):
        var timestamp: float = _input_buffer[action]
        var current_time := Time.get_ticks_msec() / 1000.0
        if current_time - timestamp <= buffer_time:
            _input_buffer.erase(action)
            return true
    return false

func _input(event: InputEvent) -> void:
    # 记录所有按下事件
    if event.is_pressed():
        for action in InputMap.get_actions():
            if event.is_action(action):
                buffer_action(action)

# 使用示例:在角色着陆时检查跳跃缓冲
func check_jump_buffer() -> bool:
    if is_on_floor() and consume_action("jump"):
        return true
    return false

11. 扩展阅读


上一章: 09 - 2D 渲染与 Sprite 下一章: 11 - 角色控制器(CharacterBody) (即将发布)