Using InputEvent

それは何ですか?

通常、入力の管理は、OSやプラットフォームに関係なく複雑です。これを少し簡単にするために、特別な組み込み型、InputEvent が提供されています。このデータ型は、いくつかのタイプの入力イベントを含むように構成できます。入力イベントはエンジンを通過し、目的に応じて複数の場所で受信できます。

以下に簡単な例を示します。エスケープキーが押された場合にゲームを終了します:

func _unhandled_input(event):
    if event is InputEventKey:
        if event.pressed and event.scancode == KEY_ESCAPE:
            get_tree().quit()
public override void _UnhandledInput(InputEvent @event)
{
    if (@event is InputEventKey eventKey)
        if (eventKey.Pressed && eventKey.Scancode == (int)KeyList.Escape)
            GetTree().Quit();
}

ただし、提供されているInputMap 機能を使用すると、入力アクションを定義し、それらに異なるキーを割り当てることができます。このようにして、同じアクションに対して複数のキーを定義できます(たとえば、キーボードのエスケープキーとゲームパッドのスタートボタン)。その後、コードを更新せずにプロジェクト設定でこのマッピングをより簡単に変更でき、さらにその上にキーマッピング機能を構築して、実行時にゲームがキーマッピングを変更できるようにすることもできます!

You can set up your InputMap under Project > Project Settings > Input Map and then use those actions like this:

func _process(delta):
    if Input.is_action_pressed("ui_right"):
        # Move right.
public override void _Process(float delta)
{
    if (Input.IsActionPressed("ui_right"))
    {
        // Move right.
    }
}

どのように機能しますか?

すべての入力イベントはユーザー/プレイヤーから発生します(ただし、InputEventを生成してエンジンにフィードバックすることが可能で、ジェスチャーに役立ちます)。各プラットフォームのOSオブジェクトは、デバイスからイベントを読み取り、それらをMainLoopに送ります。SceneTree がデフォルトのMainLoop実装であるため、イベントはそこに送られます。Godot には、現在のSceneTree オブジェクトを取得する、get_tree()関数が用意されています。

しかしSceneTreeはイベントをどう処理するか分からないので、"root" Viewport (シーン ツリーの最初のノード)から始まるビューポートにそれを渡します。ビューポートは、受信した入力に対して次のような非常に多くのことを行います:

../../_images/input_event_flow.png
  1. まず、標準の Node._input() 関数は、それをオーバーライドするノードで呼び出されます(そして、Node.set_process_input() で入力処理を無効化していないなら)。いずれかの関数がイベントを消費する場合は、SceneTree.set_input_as_handled() を呼び出すことができ、イベントはそれ以上他に拡散しません。これにより、GUIの前であっても、関心のあるすべてのイベントをフィルタリングできます。ゲームプレイ入力の場合、GUIがイベントを傍受できるため、一般的に Node._unhandled_input() の方が適しています。

  2. Second, it will try to feed the input to the GUI, and see if any control can receive it. If so, the Control will be called via the virtual function Control._gui_input() and the signal "gui_input" will be emitted (this function is re-implementable by script by inheriting from it). If the control wants to "consume" the event, it will call Control.accept_event() and the event will not spread any more. Use the Control.mouse_filter property to control whether a Control is notified of mouse events via Control._gui_input() callback, and whether these events are propagated further.

  3. これまでに誰もイベントを消費しなかった場合、未処理入力コールバックがオーバーライドされていれば、それが呼び出されます(そして 、Node.set_process_unhandled_input() で無効化していないなら)。関数がイベントを消費する場合、SceneTree.set_input_as_handled() を呼び出すことができ、イベントはそれ以上拡散しません。未処理入力コールバックは、フルスクリーンのゲームプレイイベントに最適であるため、GUIがアクティブな場合は受信されません。

  4. If no one wanted the event so far, and a Camera is assigned to the Viewport with Object Picking turned on, a ray to the physics world (in the ray direction from the click) will be cast. (For the root viewport, this can also be enabled in Project Settings) If this ray hits an object, it will call the CollisionObject._input_event() function in the relevant physics object (bodies receive this callback by default, but areas do not. This can be configured through Area properties).

  5. 最後に、イベントが処理されなかった場合、ツリー内の次のビューポートに渡されます。それ以外の場合は無視されます。

シーン内のすべてのリスニングノードにイベントを送信する場合、ビューポートは逆の深さ優先順で送信します。シーンツリーの一番下のノードから始まり、ルートノードで終わります:

../../_images/input_event_scene_flow.png

GUIイベントもシーンツリーを上に移動しますが、これらのイベントは特定のコントロールを対象とするため、ターゲットコントロールノードの直接の先祖のみがイベントを受け取ります。

In accordance with Godot's node-based design, this enables specialized child nodes to handle and consume particular events, while their ancestors, and ultimately the scene root, can provide more generalized behavior if needed.

InputEventの構造

InputEvent は単なる基本組み込み型であり、何も表さず、イベントID(イベントごとに増加する)、デバイスインデックスなどの基本情報のみを含みます。

There are several specialized types of InputEvent, described in the table below:

イベント

型インデックス

説明

InputEvent

NONE

空の入力イベント。

InputEventKey

KEY

Contains a scancode and Unicode value, as well as modifiers.

InputEventMouseButton

MOUSE_BUTTON

ボタン、モディファイヤなどのクリック情報が含まれます。

InputEventMouseMotion

MOUSE_MOTION

相対位置、絶対位置、速度などのモーション情報が含まれます。

InputEventJoypadMotion

JOYSTICK_MOTION

ジョイスティック/ジョイパッドのアナログ軸情報が含まれています。

InputEventJoypadButton

JOYSTICK_BUTTON

ジョイスティック/ジョイパッドのボタン情報が含まれます。

InputEventScreenTouch

SCREEN_TOUCH

マルチタッチのプレス/リリース情報が含まれています。(モバイルデバイスでのみ使用可能)

InputEventScreenDrag

SCREEN_DRAG

マルチタッチドラッグ情報が含まれています。(モバイルデバイスでのみ使用可能)

InputEventAction

SCREEN_ACTION

汎用アクションが含まれています。これらのイベントは多くの場合、フィードバックとしてプログラマーによって生成されます。(詳細は以下)

アクション

InputEventは、事前定義されたアクションを表す場合と表さない場合があります。アクションは、ゲームロジックをプログラミングするときに入力デバイスを抽象化するので便利です。次の機能が可能になります:

  • 入力が異なるさまざまなデバイス(PCのキーボード、コンソールのJoypadなど)で動作する同じコード。

  • 実行時に再構成される入力。

アクションは、[プロジェクト設定]メニューの[アクション]タブから作成できます。

すべてのイベントには、メソッド InputEvent.is_action()InputEvent.is_pressed() および InputEvent があります。

あるいは、ゲームコードからのアクションをゲームに返すことが望ましい場合があります(この良い例は、ジェスチャの検出です)。Inputシングルトンには、次のメソッドがあります: Input.parse_input_event()。通常は次のように使用します:

var ev = InputEventAction.new()
# Set as move_left, pressed.
ev.action = "move_left"
ev.pressed = true
# Feedback.
Input.parse_input_event(ev)
var ev = new InputEventAction();
// Set as move_left, pressed.
ev.SetAction("move_left");
ev.SetPressed(true);
// Feedback.
Input.ParseInputEvent(ev);

入力マップ

多くの場合、コードからの入力をカスタマイズして再マッピングすることが望まれます。ワークフロー全体がアクションに依存する場合、InputMap シングルトンは、実行時に異なるアクションを再割り当てまたは作成するのに理想的です。このシングルトンは保存されず(手動で変更する必要があります)、そのステートはプロジェクト設定(project.godot)から実行されます。したがって、このタイプの動的システムは、プログラマーが最適と考える方法で設定を保存する必要があります。