【Unity】Input Systemで長押しを実現する

こじゃらこじゃら

Input Systemでボタンの長押し操作の実装方法を知りたいの。

このはこのは

Hold Interactionを使えば簡単に実現できるわ。

Input Systemでボタンなどの長押し操作を実現する方法の解説記事です。

結論を述べると、Hold InteractionというInteractionの一種を使えば実現できます。

Interactionとは特定の入力パターンを検知するための機能で、長押しのほか、ダブルタップなどの操作を扱いたい場面で役立ちます。

Hold Interactionはその数あるInteractionのうちの一つです。

参考:Interactions | Input System | 1.5.1

Hold Interactionを用いると、長押しの判定処理をInput System側に任せることができ、ゲーム側ロジックと入力ロジックを綺麗に分離できるメリットがあります。

本記事では、このような長押し操作を実現する方法を解説します。また、応用編として長押しゲージの簡単な実装方法も紹介します。

動作環境
  • Unity 2022.2.13f1
  • Input System 1.5.1

スポンサーリンク

前提条件

事前にInput Systemがインストールされ、有効化されているものとします。

ここまでの手順が分からない方は、以下記事を参考にセットアップを済ませてください。

また、Interactionの機能を使うためにはInput Action経由で入力を取得する必要があります。

Input Actionの使い方は以下記事で解説しています。

本記事では、次のような Actionに対して長押し操作を適用するものとします。

Hold Interactionの概要

Hold Interactionは、一定時間以上入力があったら操作を受け付けるInteractionです。

参考:Interactions | Input System | 1.5.1

長押しと判定された瞬間にperformedコールバックが発火されます。

スクリプト側からは、このperformedコールバックをフックして長押しされた瞬間を検知できます。

Hold Interactionの適用方法

まず、対象となるActionが含まれているInput Action Assetファイル(拡張子が.inputactionsのファイル)をダブルクリックで開き、対象のActionを選択します。

そして、ウィンドウ右のAction Properties > Interaction右の+アイコン > Holdを選択します。

すると、以下のようにHold Interactionが適用された状態になります。

必要に応じて画面の項目の値を設定します。

  • Press Point
    • 入力あり判定となる閾値。この値より大きな入力値が入力されていると入力ありと判定される。デフォルトではInput Systemパッケージの設定内容が反映される。
  • Hold Time
    • 長押し判定されるまでの時間(秒)。この時間以上の入力があると「長押し」判定となる。デフォルトではInput Systemパッケージの設定内容が反映される。

入力を取得するサンプル

以下、長押しされたらログ出力するサンプルスクリプトです。

GetHoldExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

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

    private void Awake()
    {
        if (_hold == null) return;
        
        // performedコールバックのみを受け取る
        // 長押し判定になったらこのコールバックが呼ばれる
        _hold.action.performed += OnHold;
        
        // 入力を受け取るためには必ず有効化する必要がある
        _hold.action.Enable();
    }

    // 長押しされたときに呼ばれるメソッド
    private void OnHold(InputAction.CallbackContext context)
    {
        Debug.Log("長押しされた!");
    }
}

上記スクリプトをGetHoldExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチし、インスペクターの項目に長押しActionを指定すると機能します。

実行結果

一定時間以上入力があると、ログ出力されるようになりました。

Hold Interactionの内部挙動について

下図のように入力値の大きさが閾値Press Point以上の状態がHold Time秒以上続いたらperformedコールバックを実行します。

入力値の大きさは、入力型によって例えば以下のように計算されます。

  • ボタン入力、1軸アナログ入力(float型)入力値の絶対値。
  • 2軸アナログ入力(Vector2型)入力(ベクトル)の大きさ。Vector2.magnitudeとなる。

内部的には次のような状態遷移を行うステートマシンとして管理されています。

Hold Interactionの状態遷移

長押しゲージを実装する

長押しの応用例として、次のように長押し中にゲージを表示したい場合を考えます。

長押し中の進捗は、次のようなコードで取得できます。

InputAction holdAction;

・・・(中略)・・・

// 長押しの進捗を取得
var progress = holdAction.GetTimeoutCompletionPercentage();

GetTimeoutCompletionPercentageメソッドは、Interactionのタイムアウト時間までの進捗率を0~1の範囲で返します。

Hold Interactionの場合、タイムアウト時間は長押し判定に必要な時間Hold Timeとなります。

参考:Class InputAction| Input System | 1.5.1

長押しの進捗を取得する

以下、長押し操作の進捗率をログ出力するサンプルスクリプトです。

HoldProgressExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

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

    private InputAction _holdAction;
    
    private void Awake()
    {
        if (_hold == null) return;

        _holdAction = _hold.action;
        
        // 入力を受け取るためには必ず有効化する必要がある
        _holdAction.Enable();
    }

    private void Update()
    {
        if (_holdAction == null) return;
        
        // 長押しの進捗を取得
        var progress = _holdAction.GetTimeoutCompletionPercentage();

        // 進捗をログ出力
        Debug.Log($"Progress : {progress * 100}%");
    }
}

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

実行結果

百分率でログ出力されていることが確認できます。

ボタンを離すと進捗率は0%になり、ボタンを押し続けると進捗が上がり長押し判定になった後はボタンを離すまで100%になります。

ゲージUIに反映する

次のようなスクリプトになります。

HoldGauge.cs
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

internal class HoldGauge : MonoBehaviour
{
    // 入力を受け取る対象のAction
    [SerializeField] private InputActionReference _hold;
    
    // ゲージのUI
    [SerializeField] private Image _gaugeImage;

    private InputAction _holdAction;
    
    private void Awake()
    {
        if (_hold == null) return;

        _holdAction = _hold.action;
        
        // 入力を受け取るためには必ず有効化する必要がある
        _holdAction.Enable();
    }

    private void Update()
    {
        if (_holdAction == null) return;
        
        // 長押しの進捗を取得
        var progress = _holdAction.GetTimeoutCompletionPercentage();

        // 進捗をゲージに反映
        _gaugeImage.fillAmount = progress;
    }
}

上記をHoldGauge.csとしてUnityプロジェクトに保存し、ゲームオブジェクトにアタッチし、インスペクターより必要な参照を指定してください。

また、ゲージとして使用するImage側のImage TypeをFilledに設定しておく必要があります。

参考:Visual Components | Unity UI | 1.0.0

実行結果

ボタンを押すと徐々にゲージが増えていくことが確認できました。

スクリプトの説明

ゲージへの反映処理は以下部分です。

// 長押しの進捗を取得
var progress = _holdAction.GetTimeoutCompletionPercentage();

// 進捗をゲージに反映
_gaugeImage.fillAmount = progress;

Image.fillAmountプロパティに進捗を直接代入すれば良いです。

参考:Class Image| Unity UI | 1.0.0

さいごに

Input Systemでのボタンの長押し判定は、Hold Interactionを使用すると簡単に実装できます。

入力の受け取り側では、長押しのロジックを書く必要がなく、Input Actionのperformedコールバックを拾えば良いです。

また、ゲージなどのUI更新についても、InputAction.GetTimeoutCompletionPercentageメソッドから進捗を取得できるので、複雑なロジックを書くことなく実装できるでしょう。

関連記事

参考サイト

スポンサーリンク