Примеры ввода

Введение

В этом уроке вы узнаете, как использовать систему InputEvent 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().

Входящие события

События ввода - это объекты, которые наследуются от InputEvent. В зависимости от типа события объект будет содержать определенные свойства, связанные с этим событием. Чтобы увидеть, как на самом деле выглядят события, добавьте Node и прикрепите следующий скрипт:

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)

Как вы можете видеть, результаты очень различны для разных типов входных данных. Ключевые события даже печатаются как их ключевые символы. Например, рассмотрим InputEventMouseButton. Он наследуется от следующих классов:

  • InputEvent - базовый класс для всех входных событий
  • InputEventWithModifiers - добавляет возможность проверять, нажаты ли модификаторы, такие как Shift или Alt.
  • InputEventMouse - добавляет свойства события мыши, такие как position
  • InputEventMouseButton - содержит индекс кнопки, которая была нажата, был ли это двойной щелчок и т. д.

Совет

Рекомендуется держать ссылку на класс открытой во время работы с событиями, чтобы вы могли проверить доступные свойства и методы типа события.

Вы можете столкнуться с ошибками, если попытаетесь получить доступ к свойству типа ввода, которое его не содержит , например, вызывая position на InputEventKey. Чтобы избежать этого, сначала проверьте тип события:

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

InputMap - это наиболее гибкий способ обработки различных входных данных. Вы используете это, создавая именованные actions, которым можно назначить любое количество событий ввода, таких как нажатия клавиш или щелчки мыши. Новый проект Godot включает в себя ряд уже определенных действий по умолчанию. Чтобы увидеть их и добавить свои собственные, откройте Project -> Project Settings и выберите вкладку InputMap:

../../_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.Scancode == KeyList.T)
        {
            GD.Print("T was pressed");
        }
    }
}

Совет

См. @GlobalScope_KeyList для получения списка констант сканкода.

Модификаторы клавиатуры

Свойства модификатора наследуются от 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;
        }
    }
}

Совет

См. @GlobalScope_KeyList для получения списка констант сканкода.

События мыши

События мыши происходят из класса InputEventMouse и разделяются на два типа: InputEventMouseButton и InputEventMouseMotion. Обратите внимание, что это означает, что все события мыши будут содержать свойство 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 as 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 работает почти так же, как движение мыши.

Совет

Чтобы протестировать сенсорные события на устройстве без сенсорного экрана, откройте Project Settings и перейдите в раздел "Input Devices/Pointing". Включите функцию "Emulate Touch From Mouse", и ваш проект будет интерпретировать щелчки мыши и движение как сенсорные события.