【Unity】Input Systemでボタンの押しっぱなしを判定する

こじゃらこじゃら

Input Systemでボタンが押されているかどうか判定する方法がよく分からないの…

このはこのは

やり方はいくつかあるけど、判定するだけならIsPressedメソッドを使えば良いわ。

Input Systemでボタンの押しっぱなしを判定する方法の紹介です。例えば、あるボタンを押している間だけUpdate内で何か処理したい場合など役立ちます。

押されているかどうかの判定は、次のように該当するActionのIsPressedメソッドを使えば簡単です。

// PlayerInputコンポーネント取得
PlayerInput playerInput = GetComponent<PlayerInput>();
// 攻撃アクションをPlayerInputから取得
InputAction fireAction = playerInput.actions["Fire"];

// 攻撃ボタンの押下状態を取得
bool isPressed = fireAction.IsPressed();

Input Actionを使用せず、直接低レベルAPI(Keyboard、Gamepadクラスなど)を参照する場合などは、次のコードで押下状態を取得できます。

// isPressedプロパティから取得可能
var isPressed = Keyboard.current.aKey.isPressed;
// IsPressedメソッドでも取得可能(InputControlの拡張メソッド)
var isPressed = Keyboard.current.aKey.IsPressed();

これによって、Updateイベント内などで毎フレームボタンが押されているかをチェックできるようになります。

また、少し面倒ではありますが、Actionのコールバックを利用して判定することも可能です。

本記事では、このようなボタンの押しっぱなし判定をする方法をいくつか紹介していきます。

動作環境
  • Unity 2021.3.2f1
  • Input System 1.3.0

スポンサーリンク

前提条件

事前にInput Systemがインストールされ、有効化されているものとします。ここまでの手順が分からない方は、以下記事をご覧ください。

また、本記事ではPlayer Inputを使用した環境でボタンの押下状態を取得することを想定します。

Player Inputを使った入力値の取得方法については、以下記事をご覧ください。

InputActionのIsPressedメソッドを使う

InputActionインスタンスIsPressedメソッドから押下状態を取得できます。

public bool IsPressed()

押されているならtrueそうでなければfalseが返されます。

参考:Class InputAction| Input System | 1.3.0

サンプルスクリプト

Player Inputから攻撃Actionを取得し、攻撃ボタンの押下状態をログ出力するサンプルです。

PressButtonExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class PressButtonExample : MonoBehaviour
{
    // 入力を受け取るPlayerInput
    [SerializeField] private PlayerInput _playerInput;

    // 攻撃アクション名
    [SerializeField] private string _fireActionName = "Fire";

    // 攻撃アクション
    private InputAction _fireAction;

    private void Awake()
    {
        // 攻撃アクションをPlayerInputから取得
        _fireAction = _playerInput.actions[_fireActionName];
    }

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

        // 攻撃ボタンの押下状態取得
        var isPressed = _fireAction.IsPressed();

        // ボタンの押下状態をログ出力
        print($"[{_fireActionName}] isPressed = {isPressed}");
    }
}

このスクリプトをPressButtonExample.csという名前で保存し、適当なゲームオブジェクトにアタッチし、インスペクターよりPlayer InputとAction名を設定すると機能するようになります。

なお、Player InputのInput Actionを取得するためには、予めPlayer InputおよびActionアセットの設定を行っている必要があります。この手順が分からない方は、以下記事を参考にセットアップを済ませてください。

次の動画は、Player Inputのセットアップからスクリプトを適用するまでの例です。

実行結果

Actionを用いずに押下状態を取得する

Input Actionを使用せず、Keyboardなどの低レイヤーAPIを直接参照する場合は、該当するボタンのButtonControlインスタンスisPressedプロパティから取得できます。

参考:Class ButtonControl| Input System | 1.3.0

サンプルスクリプト

キーボードの特定キーが押されているかを判定する例です。

KeyboardPressExample.cs
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

public class KeyboardPressExample : MonoBehaviour
{
    // 取得したいキー
    [SerializeField] private Key _key = Key.A;
    
    private void Update()
    {
        // 現在のキーボード情報
        var current = Keyboard.current;

        // キーボード接続チェック
        if (current == null)
        {
            // キーボードが接続されていないと
            // Keyboard.currentがnullになる
            return;
        }
        
        // キー入力の取得
        ButtonControl control = current[_key];

        // 指定されたキーが押されているかどうか
        print($"[{_key}] isPressed = {control.isPressed}");
    }
}

上記スクリプトをKeyboardPressExample.csという名前で保存し、適当なゲームオブジェクトにアタッチすると機能します。必要に応じてKeyを設定してください。

実行結果

実行すると、押下状態がログ出力されます。

Actionのコールバック経由で押下状態を判定する

Player Input等のコールバックを用いて押下状態を判定する方法の紹介です。

前述の方法とは違い、フラグを保持しておく必要がある点で面倒ですが、InputActionを直接参照しなくても良いメリットもあります。

サンプルスクリプト

コールバック経由で押下状態をログ出力するサンプルです。Player Inputからコールバック呼び出しする場面を想定しています。Player InputのBehaviour項目にInvoke Unity Eventsが設定されていることを想定しています。

CallbackPressExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class CallbackPressExample : MonoBehaviour
{
    // ボタンの押下状態
    private bool _isPressed;

    // PlayerInput側から呼ばれるコールバック
    // BehaviourにInvoke Unity Eventsが設定されていることを想定
    public void OnFire(InputAction.CallbackContext context)
    {
        switch (context.phase)
        {
            case InputActionPhase.Performed:
                // ボタンが押された時の処理
                _isPressed = true;
                break;

            case InputActionPhase.Canceled:
                // ボタンが離された時の処理
                _isPressed = false;
                break;
        }
    }

    private void Update()
    {
        // 現在のボタンの押下状態をログ出力
        print($"isPressed = {_isPressed}");
    }
}

上記スクリプトをCallbackPressExample.csという名前で保存し、適当なゲームオブジェクトにアタッチし、Player Inputにイベントの登録を行うと機能するようになります。

次は、スクリプトのアタッチからFireイベントにOnFireメソッドを登録するまでの操作例です。

Player Inputからコールバック呼び出しする方法が分からない方は、以下記事をご覧ください。

実行結果は、先ほどの例と一緒のため、割愛させていただきます。

押下状態の遷移について

Input Systemでは、0と1の入力しか返さないボタンであっても、内部的にはfloat型の入力値として扱われます。

ボタンが押されたかどうかは、値の大きさが閾値(Press)以上になったかどうかで判定されます。2軸スティック入力(Vector2型)の場合は、ベクトルの大きさが閾値以上になったかどうかで判定されます。

また、押された状態から離された状態への遷移は、入力値の大きさが閾値Release以下になった時となります。

上記の一連の挙動は、内部的には次のような状態遷移になります。

ボタンが離されているときはWaiting、ボタンが押された瞬間(入力値がPress以上になった時)にPerformedに遷移します。ボタンが離される(入力値がRelease以下になった時)にはCanceledに遷移し、直後にWaitingに遷移します。

ボタンが閾値Press以上にならない限り、Performed(押された状態)にはなりません。

参考:Interactions | Input System | 1.3.0

また、閾値PressとReleaseは次のように値が決定されます。

PressとReleaseの値
  • Press
    • Input SettingsのDefault Button Press Point。ただしInteractionPress Pointが設定されている場合はこちらが優先される
  • Release
    • Press × Input SettingsのButton Release Threshold。

Releaseの計算が少し複雑で、Pressとの掛け算をしている点に注意する必要があります。

例えば、Pressが0.5、Button Release Thresholdが0.75の場合、Releaseの値は0.5 × 0.75 = 0.375となります。

参考:Class InputSettings| Input System | 1.3.0

Interactionを適用した場合の挙動の違い

Interactionを適用した場合、Pressの値はInteraction側の設定値が優先されます。

参考:Class InputAction| Input System | 1.3.0

また、長押し(Hold)などのInteractionが指定された場合でも、押された瞬間に押された瞬間にIsPressedメソッドがtrueを返す挙動となることに注意する必要があります。

ただし、コールバックを受け取る実装をしている場合はこの限りではありません。理由はPerformedが呼ばれるまでフラグを見ない動作になっているためです。

さいごに

ボタンが押されているかどうかの判定は、Input ActionのIsPressedメソッドから知ることができます。

Interactionが適用されている場合は少し挙動が変化するので、使用の際は留意すると良いでしょう。

関連記事

参考サイト

スポンサーリンク