输入示例

前言

在本教程中, 您将学习如何使用Godot的 输入事件 系统捕获玩家输入. 您的游戏可以使用多种不同类型的输入——键盘, 游戏手柄, 鼠标等等. 还有许多不同的方法, 将这些输入转化为游戏中的动作. 本文将向您展示一些最常见的场景, 您可以将其作为您自己项目的起点.

注解

要详细了解 Godot 的输入事件系统是如何工作的,请参阅 使用 InputEvent

事件和轮询

有时你想让你的游戏对某一输入事件作出反应--例如按下 "跳跃" 按钮. 在其他情况下, 你可能希望只要有一个键被按下就会发生一些事情, 比如移动. 在第一种情况下, 你可以使用 _input() 函数, 只要有输入事件发生就会调用该函数. 在第二种情况下,Godot提供了 Input 单例, 你可以用它来查询一个输入的状态.

示例:

func _input(event):
    if event.is_action_pressed("jump"):
        jump()


func _physics_process(delta):
    if Input.is_action_pressed("move_right"):
        # Move as long as the key/button is pressed.
        position.x += speed * delta
public override void _Input(InputEvent inputEvent)
{
    if (inputEvent.IsActionPressed("jump"))
    {
        Jump();
    }
}

public override void _PhysicsProcess(float delta)
{
    if (Input.IsActionPressed("move_right"))
    {
        // Move as long as the key/button is pressed.
        position.x += speed * delta;
    }
}

这使您能够灵活地混合和匹配所执行输入的处理类型.

对于本教程的其余部分, 我们将专注于使用 _input () 捕捉单个事件.

输入事件

输入事件是继承自 输入事件 的对象. 根据事件类型, 对象将包含与该事件相关的特定属性. 为了了解事件的实际情况, 添加一个节点并附加以下脚本:

extends Node


func _input(event):
    print(event.as_text())
using Godot;
using System;

public class Node : Godot.Node
{
    public override void _Input(InputEvent inputEvent)
    {
        GD.Print(inputEvent.AsText());
    }
}

当你按下按键, 移动鼠标, 并执行其他输入, 您会在输出窗口中看到每个事件滚动. 下面是输出的一个例子:

A
InputEventMouseMotion : button_mask=0, position=(108, 108), relative=(26, 1), speed=(164.152496, 159.119843), pressure=(0), tilt=(0, 0)
InputEventMouseButton : button_index=BUTTON_LEFT, pressed=true, position=(108, 107), button_mask=1, doubleclick=false
InputEventMouseButton : button_index=BUTTON_LEFT, pressed=false, position=(108, 107), button_mask=0, doubleclick=false
S
F
Alt
InputEventMouseMotion : button_mask=0, position=(108, 107), relative=(0, -1), speed=(164.152496, 159.119843), pressure=(0), tilt=(0, 0)

如你所见, 对于不同类型的输入, 结果是非常不同的. 按键事件甚至被打印为按键符号. 例如, 让我们考虑 鼠标按钮输入事件 . 它继承自以下类:

小技巧

在处理事件时, 打开类引用是一个好主意, 这样可以检查事件类型的可用属性和方法.

如果您尝试访问不包含属性的输入类型上的属性, 则可能会遇到错误-例如, 对 InputEventKey 调用 Position . 要避免这种情况, 请确保首先测试事件类型:

func _input(event):
    if event is InputEventMouseButton:
        print("mouse button event at ", event.position)
public override void _Input(InputEvent inputEvent)
{
    if (inputEvent is InputEventMouseButton mouseEvent)
    {
        GD.Print("mouse button event at ", mouseEvent.Position);
    }
}

InputMap

事件表 是处理各种输入的最灵活的方法. 您可以通过创建命名的输入 动作 来使用它, 可以为它分配任意数量的输入事件, 例如按键或鼠标点击. 一个新的Godot项目已经包含许多默认定义操作. 看看它们, 然后添加你自己的, 打开项目->项目设置, 并选择事件表选项卡:

../../_images/inputs_inputmap.png

捕捉动作

一旦你定义了动作, 可以在脚本中使用 is_action_pressed()is_action_released () 处理它们. 通过名称查找响应的动作:

func _input(event):
    if event.is_action_pressed("my_action"):
        print("my_action occurred!")
public override void _Input(InputEvent inputEvent)
{
    if (inputEvent.IsActionPressed("my_action"))
    {
        GD.Print("my_action occurred!");
    }
}

键盘事件

键盘事件在 InputEventKey 中被捕获. 虽然建议使用输入动作来代替, 但在某些情况下, 你可能会想专门查看按键事件. 对于当前示例, 来检查一下 T :

func _input(event):
    if event is InputEventKey and event.pressed:
        if event.scancode == KEY_T:
            print("T was pressed")
public override void _Input(InputEvent inputEvent)
{
    if (inputEvent is InputEventKey keyEvent && keyEvent.Pressed)
    {
        if ((KeyList)keyEvent.Keycode == KeyList.T)
        {
            GD.Print("T was pressed");
        }
    }
}

小技巧

详见 全局范围_键位列表 获取扫描代码常量列表.

键盘修饰键

修饰键属性继承自 InputEventWithModifiers ,可使用布尔属性检查修饰键的组合。试想,如果需要在按下 T 时发生一件事,而按下 Shift + T 时发生不同的事:

func _input(event):
    if event is InputEventKey and event.pressed:
        if event.scancode == KEY_T:
            if event.shift:
                print("Shift+T was pressed")
            else:
                print("T was pressed")
public override void _Input(InputEvent inputEvent)
{
    if (inputEvent is InputEventKey keyEvent && keyEvent.Pressed)
    {
        switch ((KeyList)keyEvent.Scancode)
        {
            case KeyList.T:
                GD.Print(keyEvent.Shift ? "Shift+T was pressed" : "T was pressed");
                break;
        }
    }
}

小技巧

详见 全局范围_键位列表 获取扫描代码常量列表.

鼠标事件

鼠标事件继承自 InputEventMouse 并被分成 InputEventMouseButtonInputEventMouseMotion 两种类型。注意,这意味着所有鼠标事件都包含 position 属性。

鼠标按钮

捕获鼠标按钮与处理按键事件非常相似。@GlobalScope_ButtonList 包含每个可能按钮的 BUTTON_* 常量列表,它将在事件的 button_index 属性中报告。注意,鼠标滚轮也可以算作一个按钮——准确地说是两个按钮,BUTTON_WHEEL_UP(滚轮键向上)和 BUTTON_WHEEL_DOWN(滚轮键向上)都是独立的事件。

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == BUTTON_LEFT and event.pressed:
            print("Left button was clicked at ", event.position)
        if event.button_index == BUTTON_WHEEL_UP and event.pressed:
            print("Wheel up")
public override void _Input(InputEvent inputEvent)
{
    if (inputEvent is InputEventMouseButton mouseEvent && mouseEvent.Pressed)
    {
        switch ((ButtonList)mouseEvent.ButtonIndex)
        {
            case ButtonList.Left:
                GD.Print("Left button was clicked at ", {mouseEvent.Position});
                break;
            case ButtonList.WheelUp:
                GD.Print("Wheel up");
                break;
        }
    }
}

鼠标行为

InputEventMouseMotion 只要鼠标移动就会发生事件. 可以通过 relative 属性找到移动的距离.

下面是一个使用鼠标事件拖放 Sprite 节点的例子:

extends Node


var dragging = false
var click_radius = 32 # Size of the sprite.


func _input(event):
    if event is InputEventMouseButton and event.button_index == BUTTON_LEFT:
        if (event.position - $Sprite.position).length() < click_radius:
            # Start dragging if the click is on the sprite.
            if not dragging and event.pressed:
                dragging = true
        # Stop dragging if the button is released.
        if dragging and not event.pressed:
            dragging = false

    if event is InputEventMouseMotion and dragging:
        # While dragging, move the sprite with the mouse.
        $Sprite.position = event.position
using Godot;
using System;

public class Node2D : Godot.Node2D
{
    private bool dragging = false;
    private int clickRadius = 32; // Size of the sprite.

    public override void _Input(InputEvent inputEvent)
    {
        Sprite sprite = GetNodeOrNull<Sprite>("Sprite");
        if (sprite == null)
        {
            return; // No suitable node was found.
        }

        if (inputEvent is InputEventMouseButton mouseEvent && (ButtonList)mouseEvent.ButtonIndex == ButtonList.Left)
        {
            if ((mouseEvent.Position - sprite.Position).Length() < clickRadius)
            {
                // Start dragging if the click is on the sprite.
                if (!dragging && mouseEvent.Pressed)
                {
                    dragging = true;
                }
            }
            // Stop dragging if the button is released.
            if (dragging && !mouseEvent.Pressed)
            {
                dragging = false;
            }
        }
        else
        {
            if (inputEvent is InputEventMouseMotion motionEvent && dragging)
            {
                // While dragging, move the sprite with the mouse.
                sprite.Position = motionEvent.Position;
            }
        }
    }
}

触摸事件

如果你使用的是触摸屏设备,就可以生成触摸事件。InputEventScreenTouch 相当于鼠标点击事件,而 InputEventScreenDrag 的工作原理与鼠标移动一致。

小技巧

要在非触摸屏设备上测试触摸事件,请打开“项目设置”,进入“Input Devices/Pointing”(输入设备/触点)部分。启用“Emulate Touch From Mouse”(根据鼠标模拟触摸),您的项目将把鼠标单击和移动解释为触摸事件。