【Unity】Input Systemでボタンの押した瞬間/離した瞬間を判定する

こじゃらこじゃら

Input Systemでボタンが押された瞬間をチェックしようとしているけど上手くいかないみたいなの…

このはこのは

Input Systemでは少し気を付けないといけない点があるわ。この辺を解説していくね。

Input Systemでボタンが押された瞬間や離された瞬間を検知する方法には、主に次の2通りがあります。

判定方法
  • Input ActionのPress InteractionまたはButtonを用い、performedコールバックで検知する
  • Updateイベント等から状態を毎フレームチェックする

特別な理由がない限り、1つ目のperformedコールバックで検知する方法が簡単かつ安全です。

2つ目の方法では、Input ManagerGetButtonDownGetButtonUpメソッドのような使い方でチェックができます。しかし、Input System側の設定次第では正常動作しなくなる事があり注意が必要です。

本記事では、2種類の方法でボタンが押された瞬間、離された瞬間を判定する方法を解説します。使用上における注意点についても触れます。

動作環境
  • Unity 2022.1.23f1
  • Input System 1.4.4

スポンサーリンク

前提条件

事前にInput Systemパッケージがインストールされ、使用可能になっているものとします。ここまでの手順が分からない方は、以下記事を参考にセットアップを済ませてください。

また、本記事ではInput Actionを経由して入力を取得するものとします。Input Actionについて分からない方は、以下記事をご覧ください。

Press Interactionで判定する

Pressという名前のInteractionを使用すると、ボタンが押された瞬間離された瞬間、またはその両方を検知できます。

参考:Interactions | Input System | 1.4.4

これらの瞬間の検知は、通常はInput Actionのコールバック経由で行います。

なお、押された瞬間のみを判定したい場合は、ActionにInteractionを一切登録せずAction TypeをButtonに設定するだけでも可能です。 [1]

参考:Interactions | Input System | 1.4.4

注意

コールバック経由で取得する際は、必ずperformedコールバックのみを拾うようにする必要があります。他にもstarted、canceledコールバックがありますが、それぞれInteractionの開始と終了の際に呼ばれるため意図しないタイミングで呼ばれるなどの不具合の原因となります。

メモ

ボタンが押されたかどうかの判定には、入力値の大きさ閾値で判定するため、アナログスティックなどが倒された瞬間、戻された瞬間を検知することも可能です。 [2]

Press Interactionの適用

押された瞬間や離された瞬間を検知したいActionPress Interactionを適用します。

本記事では、押された瞬間、離された瞬間、両方の瞬間を検知するためのActionを定義し、それぞれ設定するものとします。

Press Interaction設定内容は以下の通りです。

  • Trigger Behaviourどの瞬間を検知するかを指定する。
  • Press Point押された判定とする閾値。未設定の場合はデフォルト設定(Input System Packageの設定側)が適用される。

Trigger Behaviourでは、以下の3種類の設定値から選べます。

  • Press Only押された瞬間を検知する。
  • Release Only離された瞬間を検知する。
  • Press And Release押された瞬間と離された瞬間の両方を検知する。

上記瞬間を検知した時、Input Action側からPerformedコールバックが発火されます。スクリプト側からは、この該当ActionのPerformedコールバックをチェックすれば良いです。

ここまでのPress Interactionの設定の流れは以下動画のようになります。

次に、前述のPress InteractionのPerformedコールバックをスクリプト側から検知できるようにしていきます。

Player Inputの準備

本記事では、Player InputからUnity Event経由でスクリプト側に通知するものとします。Player Inputの使い方がわからない方は、以下記事をご覧ください。

まず、対象となるInput Actionアセットが適用されたPlayer Inputを準備します。

本記事では、空のゲームオブジェクトにPlayer Inputコンポーネントを追加し、Actions項目にInput Actionアセットを適用するものとします。また、BehaviourにInvoke Unity Eventsを設定するものとします。

タイミングを検知するスクリプトの実装

以下、Player InputからUnity Event経由でコールバックを受け取る例です。

PressInteractionExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class PressInteractionExample : MonoBehaviour
{
    // 押された瞬間のコールバック
    public void OnPress(InputAction.CallbackContext context)
    {
        // 押された瞬間でPerformedとなる
        if (!context.performed) return;

        Debug.Log("Press");
    }

    // 離された瞬間のコールバック
    public void OnRelease(InputAction.CallbackContext context)
    {
        // 離された瞬間でPerformedとなる
        if (!context.performed) return;

        Debug.Log("Release");
    }

    // 押された瞬間と離された瞬間のコールバック
    public void OnBoth(InputAction.CallbackContext context)
    {
        // 両方の瞬間でPerformedとなる
        if (!context.performed) return;

        Debug.Log("Both");
    }
}

押された瞬間、離された瞬間、その両方をコールバックで受け取るためのメソッドを定義しています。

上記スクリプトをPressInteractionExample.csという名前で保存しておきます。

コールバックの登録

前述のスクリプトを適当なゲームオブジェクトにアタッチします。

そして、Player InputEvents項目より上記スクリプトのメソッドを該当するActionに登録します。

これでスクリプト側がコールバックを受け取ってログ表示できるようになりました。

実行結果

ボタンが押された瞬間、離された瞬間それぞれでデバッグログ表示されます。

両方のタイミングを検知するActionも期待通りの挙動になっています。

Updateイベントでチェックする

Press Interactionを使わないもう1つの方法として、従来のInput Managerのように、Updateイベントなどでタイミングをチェックする方法があります。

Input Action経由のほか、低レベルAPI(Keyboardクラス、Mouseクラスなど)から直接状態を見る方法があります。

ただし、この方法には1つ落とし穴があり、取り扱いには注意する必要があります(後述)

Input Action経由で判定する

Input Action経由で判定したい場合、該当するInput Actionをスクリプトから取得し、Updateイベント内で次のメソッドを用いることで判定できます。

  • 押された瞬間InputAction.WasPressedThisFrameメソッド
  • 離された瞬間InputAction.WasReleasedThisFrameメソッド

サンプルスクリプト

以下、Updateメソッドから指定された名前のAction(ボタン)が押された瞬間、離された瞬間にデバッグログを出力するサンプルです。

UpdateExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class UpdateExample : MonoBehaviour
{
    // PlayerInputコンポーネント
    [SerializeField] private PlayerInput _playerInput;

    // ボタンのAction名
    [SerializeField] private string _buttonActionName;

    private InputAction _buttonAction;

    private void Awake()
    {
        if (_playerInput == null) return;

        // 指定された名前のAction取得
        _buttonAction = _playerInput.actions.FindAction(_buttonActionName);
    }

    private void Update()
    {
        if (_buttonAction == null) return;

        // 押された瞬間かどうか
        if (_buttonAction.WasPressedThisFrame())
        {
            Debug.Log("Press");
        }

        // 離された瞬間かどうか
        if (_buttonAction.WasReleasedThisFrame())
        {
            Debug.Log("Release");
        }
    }
}

上記をUpdateExample.csという名前で保存し、適当なゲームオブジェクトにアタッチし、インスペクターより各種設定を行うと機能するようになります。

実行結果

期待通りの動作になりました。

スクリプトの説明

まず、PlayerInputから該当するInput Actionを予め取得しておきます。

// 指定された名前のAction取得
_buttonAction = _playerInput.actions.FindAction(_buttonActionName);

FindActionメソッドを使用すると、Actionの取得失敗時にnullが返されます。

参考:Class InputActionAsset Input System | 1.4.4

そして、実際に押された判定はUpdateメソッド内の以下コードで行っています。

// 押された瞬間かどうか
if (_buttonAction.WasPressedThisFrame())
{
    Debug.Log("Press");
}

// 離された瞬間かどうか
if (_buttonAction.WasReleasedThisFrame())
{
    Debug.Log("Release");
}

低レベルAPIから参照する

Input Actionを用いずに、KeyboardクラスMouseクラスなどから直接ボタン入力を参照する際も、ほぼ同様の流れで実装できます。

本記事では、キーボードのAキーの押された瞬間、離された瞬間を判定する例を紹介します。

サンプルスクリプト

Keyboardクラスから直接Aキーの押された/離されたタイミングをチェックする例です。

KeyboardExample.cs
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

public class KeyboardExample : MonoBehaviour
{
    private void Update()
    {
        var current = Keyboard.current;

        // キーボード接続チェック
        if (current == null) return;

        // Aキーのコントロール取得
        ButtonControl aKey = current.aKey;
        if (aKey == null) return;

        // 押された瞬間かどうか
        if (aKey.wasPressedThisFrame)
        {
            Debug.Log("Press");
        }

        // 離された瞬間かどうか
        if (aKey.wasReleasedThisFrame)
        {
            Debug.Log("Release");
        }
    }
}

上記をKeyboardExample.csという名前で保存し、適当なゲームオブジェクトにアタッチすると機能します。

実行結果は先の例と同じのため、割愛させていただきます。

スクリプトの説明

まず、該当するボタンのインスタンスを取得します。型はButtonControlクラスです。

// Aキーのコントロール取得
ButtonControl aKey = current.aKey;
if (aKey == null) return;

参考:Class ButtonControl | Input System | 1.4.4

キーボードの押された瞬間、離された瞬間を判定する部分は以下コードです。

// 押された瞬間かどうか
if (aKey.wasPressedThisFrame)
{
    Debug.Log("Press");
}

// 離された瞬間かどうか
if (aKey.wasReleasedThisFrame)
{
    Debug.Log("Release");
}

Input Action経由の時とは違い、次のプロパティで判定しています。

  • 押された瞬間ButtonControl.wasPressedThisFrameプロパティ
  • 離された瞬間ButtonControl.wasReleasedThisFrameプロパティ

使用上の注意点

この方法で押された瞬間/離された瞬間を判定する場合、Input Systemパッケージの設定に注意する必要があります。

影響する項目は、Project Settings > Input System PackageのUpdate Mode項目です。

スクリプトからチェックする際は、このUpdate Modeの更新タイミングに取得タイミングを合わせる必要があります。

Update Modeには次の3種類を設定可能です。

  • Process Events In Dynamic UpdateUpdateイベントのタイミングでInput Systemの状態を更新する。
  • Process Events In Fixed UpdateFixedUpdateイベントのタイミングでInput Systemの状態を更新する。
  • Process Events Manually – スクリプト等からマニュアルでInput Systemの状態を更新する。

参考:Input settings | Input System | 1.4.4

例えば、Update ModeにProcess Events In Fixed Updateが設定されている場合、次のようにFixedUpdateイベント内で押されている/離されている判定を行わなければなりません。

// FixedUpdateイベントでチェックしなければいけない
private void FixedUpdate()
{
    if (_buttonAction == null) return;

    // 押された瞬間かどうか
    if (_buttonAction.WasPressedThisFrame())
    {
        Debug.Log("Press");
    }

    // 離された瞬間かどうか
    if (_buttonAction.WasReleasedThisFrame())
    {
        Debug.Log("Release");
    }
}

注意

Update Modeの設定とスクリプトの取得タイミングが合っていないと正しく判定できません。

例えば、Input Systemの更新タイミングがFixedUpdateイベントなのに対し、スクリプトからの取得タイミングがUpdateイベントである場合、正しく取得できません。

さいごに

ボタンの押された/離されたタイミングを検知する2種類の方法を解説しました。

通常はPress Interactionを判定する方法を推奨しますが、やむを得ずUpdate等でチェックしなければいけない状況でも可能です。

Updateイベント、FixedUpdateイベントでチェックする際は、Input SystemパッケージのUpdate Modeの設定に注意しながら実装すると良いでしょう。

関連記事

参考サイト

スポンサーリンク