【Unity】Input SystemのInteractionの仕組みと使い方

こじゃらこじゃら

Input SystemのInteractionの仕組みや基本的な使い方について教えてほしいの。

このはこのは

Interactionはボタンが押された瞬間や、ダブルクリックされたかどうかなど特定の入力パターンを判定する仕組みだわ。中身の動きと共に詳しく見ていくね。

Input Systemの基本機能の一つとして、Interactionがあります。

これは、次のような特定の入力パターンを検知するための仕組みです。

入力パターンの例
  • ボタンが押された
  • 入力値が変化した
  • 長押しされた
  • ダブルタップされた
  • その他、独自に定義した入力パターン

基本的な入力パターンは幾つかプリセットとして用意されていますが、これらで物足りない場合は自作することも可能です。

InteractionはInput Action内で使用され、Actionまたはその中のBindingに対して1つまたは複数指定することが可能です。

本記事では、Input SystemのInteractionの基本概念、仕組み、および使い方について解説していきます。また、Interactionを自作する方法についても触れます。

本記事の対象読者
  • Input Systemの基本的な使い方が分かる方
  • 基本的なC#スクリプトを書ける方
  • Interactionを初めて触れる、または理解に不安のある方
  • Interactionを自作する方法を知りたい方
動作環境
  • Unity 2023.1.0f1
  • Input System 1.6.1

スポンサーリンク

前提条件

Input Systemパッケージがインストールされ、使用可能になっているものとします。

導入方法および基本的な使い方は以下記事で解説しています。

また、InteractionはInput Actionで使われる機能のため、Input Action経由で入力を取得することが前提となります。

Input Actionの基本的な使い方については以下記事で解説しています。

Interactionとは

特定の入力パターンを表現するものです。例えば、HoldというInteractionは「一定時間ボタンが押され続けた」という入力パターンを表します。

Hold Interactionの例

入力が所定のパターンを満たすと、その瞬間にActionがトリガー(performedイベントが発火)されます。

例えば、上記のHold Interactionでは、一定時間以上ボタンが押されたらトリガーされます。

参考:Interactions | Input System | 1.6.3

Interactionの基本的な仕組み

内部的には、次のステートマシンとして管理されます。

Interactionの状態遷移

Interactionでは、この状態はフェーズと呼ばれています。次の5つのフェーズがあります。

  • Waiting – 入力待ち状態
  • Started – Interactionが開始された状態
  • Performed – Interactionが期待する入力を満たした状態
  • Canceled – Interactionがキャンセルされた状態
  • Disabled – Actionが無効な状態

このうち、Interactionが直接扱うのはDisabled以外の4フェーズです。

また、Performedフェーズに限り、同じPerformedフェーズに遷移することが出来ます。

参考:Interactions | Input System | 1.6.3

各フェーズ遷移時に発生するイベント

Interaction内でステートマシンとして管理される各フェーズ間で遷移する時、次のイベントが発生します。

  • started – 入力され始めた時などに呼ばれる
  • performed – 特定の入力があった時などに呼ばれる
  • canceled – 入力が中断された時などに呼ばれる

基本的に、Started、Performed、Canceledフェーズに遷移する時に上記イベントがコールバックとして通知されます。

PerformedフェーズからPerformedフェーズへの遷移でもperformedコールバックは通知されます。

また、Waiting、Disabledフェーズへの遷移ではイベントは発火しません。

参考:Interactions | Input System | 1.6.3

参考:Class InputAction| Input System | 1.6.3

Interactionの適用

Interactionは、各BindingまたはAction単位で1つ以上指定できます。

Bindingに適用した例

Actionに直接指定した場合は、その下のBindingすべてにInteractionが指定されたのと同じになります。

Actionに適用した例

Interactionが未指定の場合は、Default Interactionが暗黙的に指定されます。

Interactionを適用するには、対象のInput Action AssetダブルクリックしてInput Actionsウィンドウを開き、対象ActionまたはBindingを選択した後、Interactions右の+アイコンから追加できます。

例えば、上記メニューからHoldを選択すると、以下のようにInteractions一覧にHold Interactionが追加されます。

設定を終えたら、Input ActionsウィンドウのSave Assetボタンをクリックして、設定内容を保存します。

入力の受取りテスト

Interactionの設定を終えたら、実際に適用されているかスクリプトを用いて確認してみましょう。

以下、指定されたActionのコールバックをログ出力する例です。

InteractionExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class InteractionExample : MonoBehaviour
{
    // 入力を受け取る対象のAction
    [SerializeField] private InputActionReference _actionRef;

    private void Awake()
    {
        // InputActionReferenceのActionに対して、
        // 3つのイベントハンドラを登録する
        _actionRef.action.started += OnAction;
        _actionRef.action.performed += OnAction;
        _actionRef.action.canceled += OnAction;
    }

    private void OnDestroy()
    {
        // 登録したイベントハンドラを解除する
        _actionRef.action.started -= OnAction;
        _actionRef.action.performed -= OnAction;
        _actionRef.action.canceled -= OnAction;
    }

    private void OnEnable()
    {
        _actionRef.action.Enable();
    }

    private void OnDisable()
    {
        _actionRef.action.Disable();
    }

    private void OnAction(InputAction.CallbackContext context)
    {
        // Interactionのフェーズをログに出力する
        print($"OnAction: {context.phase}");
    }
}

上記をInteractionExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチし、インスペクターよりInteractionが適用されたActionを指定してください。

実行結果

Hold Interactionが指定された場合、対象ボタンを一定時間押し込むとPerformedフェーズに遷移してログ出力されます。

一定時間に満たないうちにボタンを離すと、Performedフェーズに遷移しません。

ReadValueで入力値を取得する場合

Updateイベント等でReadValueメソッドを通じて入力値を取得する場合Waitingフェーズでは入力値が常に0になります。

// 独自のInteractionが設定されたAction
InputAction action;

・・・(中略)・・・

private void Update()
{
    // ここでフェーズがWaitingのままだと常に0を返す
    var value = action.ReadValue<float>();
}

プリセットとして提供されているInteractionは、いずれも入力があった時にStartedフェーズに遷移するためReadValueメソッドから入力値を取得できます。

ただし、後述する自作のInteractionを用いる場合、入力があってもStartedフェーズに遷移しない実装になっているとReadValueが返す値はいつまでも0のままなので注意が必要です。

Default Interactionの挙動

Interactionが指定されていないBinding(親のActionも同様)には、暗黙的にDefault Interactionが指定されます。

Default Interactionの挙動は、Action PropertiesのAction項目から設定できます。

基本的な挙動はAction Typeの設定によって決まります。

  • Value
    • 入力値が変化したときにフェーズ遷移する。0から0以外に変化したときはStarted→Performedの順に遷移し、0以外から別の0以外の値に変化したときはPerformedからPerformedに遷移する。0以外から0に変化したときはCanceledに遷移する。
  • Button
    • 入力値が0から0以外に変化したときにはStartedに遷移し、Press Point以上に変化したときにPerformedに遷移する。PerformedフェーズでRelease Point未満に値が変化するとCanceledに遷移する。
  • Pass Through
    • デバイスからの入力がある度に常にPerformedに遷移する。

各設定のおける挙動の詳細は、以下記事をご覧ください。

Interactionのプリセット一覧

ここからは、Input Systemパッケージが予め提供しているInteractionの使い方について一通り紹介していきます。

各フェーズ遷移への詳細については以下記事をご覧ください。

Press

ボタンの押した瞬間離した瞬間、またはその両方を検知(performedコールバックを通知)するInteractionです。

どの瞬間を検知するかは、Trigger Behaviour項目から設定できます。設定内容は次の通りです。

  • Press Only – 押した瞬間
  • Release Only – 離した瞬間
  • Press And Release – 押した瞬間と離した瞬間両方

参考:Interactions | Input System | 1.6.3

これを実行すると次のようなフェーズ遷移(コールバック通知)になります。

上記動画はTrigger BehaviourにPress And Releaseを設定しているため、押した瞬間と離した瞬間両方でperformedコールバックが通知されています。

Hold

一定時間ボタンが押されたことを検知するInteractionです。

Hold Timeに指定された時間(秒)以上押され続けたらPerformedフェーズに遷移し、performedコールバックが通知されます。

参考:Interactions | Input System | 1.6.3

実行結果は以下の通りです。

ボタンを押してからHold Time秒経過する前にボタンを離すと、Performedフェーズに遷移せずにCanceledフェーズに遷移します。

Hold Time秒経過すると、その瞬間にPerformedフェーズに遷移します。

Tap

一定時間以内にボタンを押して離したことを検知するInteractionです。

Max Tap Durationに指定した時間(秒)以内にボタンを押して離すと、離した瞬間にPerformedフェーズに遷移してperformedコールバックが通知されます。

逆に、ボタンを押してからMax Tap Duration秒以上押され続けたらCanceledフェーズに遷移してcanceledコールバックが通知されます。

参考:Interactions | Input System | 1.6.3

SlowTap

ボタンが押されてから一定時間以上経過して離されたことを検知するInteractionです。ゆっくりとタップされたかどうかを判定するのに使います。

Min Tap Duration秒以上ボタンが押され続けるとPerformedフェーズに遷移します。

その前にボタンが離されるとCanceledフェーズに遷移します。

参考:Interactions | Input System | 1.6.3

MultiTap

ある時間内に指定回数タップされたことを検知するInteractionです。ダブルクリックやダブルタップなどを判定する場合に使います。

Tap Countには、必要なタップ回数を指定します。ダブルタップなら2、トリプルタップなら3という値を指定すれば良いです。

Max Tap Spacingには、ボタンが離されてから次のボタンが押されるまでの最大許容時間(秒)を指定します。この時間を超えてボタンが離されたままだとキャンセル扱いになります。

Max Tap Durationには、ボタンが押されてから離されるまでの最大許容時間(秒)を指定します。ボタンが押されてからこの時間を超えるとキャンセル扱いになります。

参考:Interactions | Input System | 1.6.3

Interactionを自作する

プリセットでは物足りない場合、Interactionを自作することも可能です。

以下、カスタムInteractionを実装する方法の解説です。

Interactionの基本形

カスタムInteractionは、IInputInteractionインタフェースを実装したクラスとして作成できます。

public class CustomInteraction : IInputInteraction

参考:Interface IInputInteraction| Input System | 1.6.3

また、特定の型の入力値に限定したい場合IInputInteraction<T>インタフェースを実装して作成することも可能です。

public class CustomInteraction : IInputInteraction<float>

参考:Interface IInputInteraction<TValue>| Input System | 1.6.3

また、カスタムInteractionは上記クラスを実装したままでは使えずアプリケーション初期化などのタイミングで次の処理を呼ぶ必要があります。

// 初回にInteractionを登録する必要がある
InputSystem.RegisterInteraction<CustomInteraction>();

InputSystem.RegisterInteraction<T>メソッドにより、Tに指定したInteractionをInput System側に登録することが出来ます。

参考:Class InputSystem| Input System | 1.6.3

Interactionの基本形は以下の通りになります。

CustomInteraction.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class CustomInteraction : IInputInteraction
{
#if UNITY_EDITOR
    [UnityEditor.InitializeOnLoadMethod]
#else
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
#endif
    public static void Initialize()
    {
        // 初回にInteractionを登録する必要がある
        InputSystem.RegisterInteraction<CustomInteraction>();
    }

    public void Process(ref InputInteractionContext context)
    {
        // TODO : Interactionの処理
    }
    
    public void Reset()
    {
        // TODO : Interactionの状態をリセットする処理
    }
}

上記は何もしないInteractionです。ずっとWaitingフェーズに留まっているため、入力値は0のままで、コールバックも一切通知しません。

次に、Interactionの実装に使用するAPI群について解説します。

入力の評価

プリセットのInteractionでは、ボタンが押されたか、離されたかどうかの判定が何度か出てきました。

このような判定は、入力値の大きさがある閾値以上か否かを調べています。これは、InputInteractionContext.ControlIsActuatedメソッドで行います。

public bool ControlIsActuated(float threshold = 0F)

引数には閾値を指定します。入力値の大きさが閾値以上ならtrue、そうでなければfalseを返します。ただし、引数に指定された閾値が0の時に限り、入力値の大きさが0より大きければtrue、0ならfalseを返す挙動に変化します。

参考:Struct InputInteractionContext| Input System | 1.6.3

InputInteractionContext構造体は、Processメソッドの引数として渡されるため、Processメソッド内で使います。

public void Process(ref InputInteractionContext context)
{
    // 入力値の大きさ0.5以上かどうか
    if (context.ControlIsActuated(0.5f)) {
        // ボタンが押された時の処理
    }
メモ

ControlIsActuatedメソッドで評価される入力値の大きさは、ボタン入力のようなfloat型のほか、Vector2、Vector3などから計算できます。

例えば、Vector2やVector3などの入力値は内部的にVector2.magnitudeやVector3.magnitudeプロパティにより計算された結果です。

参考:Class InputControlExtensions| Input System | 1.6.3

参考:Class InputControl| Input System | 1.6.3

各フェーズへの遷移

Interactionの各フェーズへの遷移は、InputInteractionContext構造体の次のメソッドにより行います。

  • Started() – Startedフェーズへ遷移する
  • Performed() – Performedフェーズへ遷移し、その後Waitingフェーズへ遷移する
  • PerformedAndStayPerformed() – Performedフェーズへ遷移し、そのままPerformedフェーズに留まる
  • PerformedAndStayStarted() – Performedフェーズへ遷移し、その後Startedフェーズへ遷移する
  • Canceled() – Canceledフェーズへ遷移し、その後Waitingフェーズへ遷移する
  • Waiting() – Waitingフェーズへ遷移し、そのままWaitingフェーズに留まる

参考:Struct InputInteractionContext| Input System | 1.6.3

また、現在のフェーズは、InputInteractionContext.phaseプロパティから取得できます。

参考:Struct InputInteractionContext| Input System | 1.6.3

実際の使用例は以下の通りになります。

public void Process(ref InputInteractionContext context)
{
    switch (context.phase)
    {
        case InputActionPhase.Waiting:
            if (context.ControlIsActuated(0.5f))
            {
                // ボタンが押された時の処理
                context.Started();
                context.PerformedAndStayPerformed();
            }

            break;

        case InputActionPhase.Performed:
            if (!context.ControlIsActuated(0.375f))
            {
                // ボタンが押された時の処理
                context.Canceled();
            }

            break;
    }
}

上記は、ボタンが押された瞬間にStarted→Performedの順に即座にフェーズ遷移し、ボタンが離された瞬間にCanceled→Waitingフェーズの順に遷移する処理の例です。

タイムアウトの設定

Interactionの処理には、タイムアウトを設けることも可能です。

設定にはInputInteractionContext.SetTimeoutメソッドを使います。

public void SetTimeout(float seconds)

引数に指定された時間(秒)経過したら、強制的にCanceledフェーズに遷移します。

参考:Struct InputInteractionContext| Input System | 1.6.3

また、タイムアウトになったかどうかを判定するInputInteractionContext.timerHasExpiredプロパティもあります。

public bool timerHasExpired { get; }

必要に応じて活用すると良いでしょう。

参考:Struct InputInteractionContext| Input System | 1.6.3

リセット処理

フェーズがキャンセルされると、Resetメソッドが実行されます。

public void Reset()
{
    // TODO : Interactionの状態をリセットする処理
}

Interaction側で独自に使用している状態変数などを初期化したい場合に使えます。

参考:Interface IInputInteraction| Input System | 1.6.3

Interactionの実装例

以下、Interaction全体の実装例です。ボタンが押されたらStarted→Performedフェーズの順に遷移し、ボタンが離されるとCanceled→Waitingフェーズの順に遷移します。

MyButtonInteraction.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class MyButtonInteraction : IInputInteraction
{
#if UNITY_EDITOR
    [UnityEditor.InitializeOnLoadMethod]
#else
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
#endif
    public static void Initialize()
    {
        // 初回にInteractionを登録する必要がある
        InputSystem.RegisterInteraction<MyButtonInteraction>();
    }

    public void Process(ref InputInteractionContext context)
    {
        switch (context.phase)
        {
            case InputActionPhase.Waiting:
                // ボタンが押されたらStarted→Performedフェーズの順に遷移
                if (context.ControlIsActuated(InputSystem.settings.defaultButtonPressPoint))
                {
                    // ボタンが押された時の処理
                    context.Started();
                    context.PerformedAndStayPerformed();
                }

                break;

            case InputActionPhase.Performed:
                // ボタンが離されたらCanceledフェーズに遷移
                if (!context.ControlIsActuated(InputSystem.settings.buttonReleaseThreshold))
                {
                    // ボタンが押された時の処理
                    context.Canceled();
                }

                break;
        }
    }

    public void Reset()
    {
    }
}

上記をMyButtonInteraction.csという名前でUnityプロジェクトに保存すると、Input System側にInteractionとして登録され、使用できるようになります。

実行結果

ボタンが押されるとstarted、performedコールバックが呼ばれ、ボタンが離されるとcanceledコールバックが呼ばれていることが確認できました。

さいごに

Input SystemのInteractionは、特定の入力パターンを検知する仕組みを備えています。

Interactionを指定しない場合でもDefault Interactionが暗黙的に指定され、入力値の変化やボタンが押された瞬間などの入力パターンを検知します。

また、Interactionにはプリセットのほか独自実装したものを適用することも可能です。

関連記事

参考サイト

スポンサーリンク