Godot 的输入系统强大而灵活,不仅支持键盘、鼠标、手柄,还可以很方便地自定义输入映射和处理多种输入事件。本教程将从以下几个方面展开:

1️⃣ 输入系统基础:Input 类和 InputEvent

2️⃣ 使用输入映射 (Input Map)

3️⃣ 处理复杂输入:组合键、连击、按住检测

4️⃣ 多点触控与手柄输入

5️⃣ 实践示例:移动角色、瞄准射击、UI 输入分离


1️⃣ 输入系统基础

Godot 通过两种核心机制处理输入:

  • Input 单例类:用于轮询输入状态(如 Input.is_action_pressed()

  • InputEvent:用于捕获事件驱动输入(如 _input(event)_unhandled_input(event)


🚩 使用 Input 轮询输入(C#)

using Godot;

public partial class InputExample : Node
{
    public override void _Process(double delta)
    {
        if (Input.IsActionPressed("ui_right"))
        {
            GD.Print("Right key is held down");
        }

        if (Input.IsActionJustPressed("ui_accept"))
        {
            GD.Print("Accept key was just pressed");
        }
    }
}

is_action_pressedis_action_just_pressed 的区别:

  • 前者用于持续检测是否按住。

  • 后者只在按下那一帧触发一次。


🚩 使用 _Input 捕获输入事件(C#)

using Godot;

public partial class InputEventExample : Node
{
    public override void _Input(InputEvent @event)
    {
        if (@event is InputEventKey keyEvent && keyEvent.Pressed)
        {
            GD.Print($"Key pressed: {keyEvent.Keycode}");
        }

        if (@event is InputEventMouseButton mouseEvent && mouseEvent.ButtonIndex == MouseButton.Left)
        {
            if (mouseEvent.Pressed)
                GD.Print($"Left mouse button down at {mouseEvent.Position}");
        }
    }
}

_input 会捕获所有输入事件,通常用于需要精准事件信息的场景。


2️⃣ 使用输入映射 (Input Map)

Godot 的输入映射允许你为动作(Action)分配多个物理按键或设备输入,方便跨平台和自定义按键。

👉 配置位置Project > Project Settings > Input Map

例如添加:

  • move_left:A 键、左箭头

  • jump:Space 键

在脚本里使用:

using Godot;

public partial class InputMapExample : Node
{
    public override void _Process(double delta)
    {
        if (Input.IsActionPressed("jump"))
        {
            GD.Print("Jumping!");
        }
    }
}

💡 热更新输入映射

using Godot;

public partial class AddInputMapExample : Node
{
    public override void _Ready()
    {
        InputMap.AddAction("dash");
        var dashEvent = new InputEventKey();
        dashEvent.Keycode = Key.Shift;
        InputMap.ActionAddEvent("dash", dashEvent);
    }
}

3️⃣ 处理复杂输入

✅ 组合键

例如,按住 Shift + 方向键:

using Godot;

public partial class ComboKeyExample : Node
{
    public override void _Process(double delta)
    {
        if (Input.IsActionPressed("ui_up") && Input.IsKeyPressed(Key.Shift))
        {
            GD.Print("Running up with Shift");
        }
    }
}

✅ 检测长按

using Godot;

public partial class LongPressExample : Node
{
    private double _holdTime = 0.0;

    public override void _Process(double delta)
    {
        if (Input.IsActionPressed("jump"))
        {
            _holdTime += delta;
            if (_holdTime > 1.0)
            {
                GD.Print("Jump held for over 1 second");
            }
        }
        else
        {
            _holdTime = 0.0;
        }
    }
}

✅ 双击/连击

using Godot;

public partial class DoubleClickExample : Node
{
    private double _lastClickTime = 0.0;
    private double _doubleClickThreshold = 0.3;

    public override void _Input(InputEvent @event)
    {
        if (@event is InputEventMouseButton mouseEvent && mouseEvent.ButtonIndex == MouseButton.Left && mouseEvent.Pressed)
        {
            double now = Time.GetTicksMsec() / 1000.0;
            if (now - _lastClickTime < _doubleClickThreshold)
            {
                GD.Print("Double click!");
            }
            _lastClickTime = now;
        }
    }
}

4️⃣ 多点触控 & 手柄输入

✅ 多点触控

using Godot;

public partial class MultiTouchExample : Node
{
    public override void _Input(InputEvent @event)
    {
        if (@event is InputEventScreenTouch touchEvent)
        {
            if (touchEvent.Pressed)
            {
                GD.Print($"Finger {touchEvent.Index} touched at {touchEvent.Position}");
            }
        }
    }
}

Godot 自动区分多点触控,每个手指对应不同的 index


✅ 手柄输入

using Godot;

public partial class JoystickExample : Node
{
    public override void _Process(double delta)
    {
        float leftStickX = Input.GetJoyAxis(0, JoyAxis.LeftX);
        float leftStickY = Input.GetJoyAxis(0, JoyAxis.LeftY);

        GD.Print($"Left Stick: {new Vector2(leftStickX, leftStickY)}");

        if (Input.IsJoyButtonPressed(0, JoyButton.A))
        {
            GD.Print("A button pressed");
        }
    }
}
  • 0 是手柄 ID。

  • JOY_AXIS_*JOY_BUTTON_* 是内置常量。


5️⃣ 实践示例:移动、射击、UI 输入分离

✅ 角色移动与瞄准

using Godot;

public partial class Player : CharacterBody2D
{
    [Export] public float Speed = 200.0f;

    public override void _PhysicsProcess(double delta)
    {
        Vector2 direction = Vector2.Zero;

        if (Input.IsActionPressed("move_right"))
            direction.X += 1;

        if (Input.IsActionPressed("move_left"))
            direction.X -= 1;

        if (Input.IsActionPressed("move_up"))
            direction.Y -= 1;

        if (Input.IsActionPressed("move_down"))
            direction.Y += 1;

        if (direction != Vector2.Zero)
            Velocity = direction.Normalized() * Speed;
        else
            Velocity = Vector2.Zero;

        MoveAndSlide();
    }
}

✅ UI 输入分离

对于 UI,使用 _unhandled_input 来防止与游戏输入冲突:

using Godot;

public partial class UIInputExample : Node
{
    public override void _UnhandledInput(InputEvent @event)
    {
        if (@event is InputEventKey keyEvent && keyEvent.Keycode == Key.Escape)
        {
            GD.Print("Escape pressed, open pause menu");
        }
    }
}

当输入被 UI 节点消费(如 Button),它不会传递到 _unhandled_input


🔑 小结

Input:轮询式,适合持续状态检测

_input:事件驱动,适合捕获精准信息

✅ 输入映射:跨平台兼容 + 可自定义

✅ 手柄/多点触控:跨设备支持

✅ 分离 UI 与游戏输入,防止冲突

功能

方法

持续检测

Input.IsActionPressed / IsKeyPressed

事件捕获

_Input / _UnhandledInput

输入映射

InputMap

手柄 / 触控

InputEventJoypad* / InputEventScreenTouch

组合键 / 长按

通过逻辑组合


📂 进阶:自动化输入录制与回放

Godot 支持记录输入事件保存为 JSON,方便重放或制作测试用例:

using Godot;
using System.Collections.Generic;

public partial class InputRecorder : Node
{
    private List<Godot.InputEvent> recording = new List<Godot.InputEvent>();

    public override void _Input(InputEvent @event)
    {
        // 录制事件,存进列表
        recording.Add(@event.Duplicate() as InputEvent); // 建议 Duplicate,避免引用问题
    }

    public void SaveInput()
    {
        // 将输入事件序列化成 JSON
        var data = new Godot.Collections.Array();
        foreach (var evt in recording)
        {
            data.Add(evt);
        }

        string json = JSON.Stringify(data);

        using var file = FileAccess.Open("res://input.json", FileAccess.ModeFlags.Write);
        file.StoreString(json);
        file.Close();
        

🚀 结语

掌握输入系统是成为 Godot 高阶用户的必经之路,尤其是跨平台项目、多人游戏、复杂 UI,输入设计尤为重要。