Unity ボタンの長押しと時間経過の円形ゲージ表示

Unity解説

長押しボタンを作りたい!
でもButtonコンポーネントにそんな機能がないよ?

標準ボタンでは作れないよ

大丈夫、そんなに難しくないから解説するね!

長押し(ロングタップ)の例

本記事では、UnityのUIで長押し(ロングタップ)機能を実装する方法について解説し、ロングタップ成立時間を表示するUIも併せて紹介します。
標準ボタンではできないのですが、Unityの基本的なUIとスクリプトを組み合わせることで簡単に実装できます。

まずは結論ですが、

イベントトリガーを使う!

Point
  1. PointerDown イベントでカウント開始
  2. カウントが一定時間経過すれば目的の処理を実行
  3. PointerUpイベントでカウント中止

順番に説明します。なお、本記事の方法ではIPointerハンドラ系のインターフェースは使いません。

※本記事にはアフィリエイトが含まれます。ブログのサーバ等の運営費などに充てております。

ロングタップ機能

長押し(ロングタップ)操作は意図しない誤タップを防ぎたい重要なアクションにこの操作を採用することが多いです。例えば、「タイトルへ戻る」ボタンや「内容のリセット」など、プレイヤーが慎重に実行すべき操作のUIボタンに長押しを適用することで、誤操作を防ぎながらユーザー体験を向上させることができます。

ロングタップの実装方法

ロングタップの動作に必要なのは「一定の間ボタンが押され続けている」ことの検知です。残念ながらこれを実現する標準コンポーネントはありませんので自作します。

必要なのは下記3つの要素です。

  1. ボタンが押された瞬間を検知
  2. 押され続けている時間をカウント(ロングタップ成立)
  3. 離された瞬間を検知(ロングタップ不成立)

これらの要素を実現するためにEventTriggerを使います。長押ししたいオブジェクトにアタッチしましょう!

EventTriggerはタップ操作などのイベントに対してスクリプトで反応することができるようになるコンポーネントで、たとえば「このボタンが押されたときに何かしらをする」ようなことをできるようになります。

取り扱えるイベントはたくさんありますが、ロングタップを実装するのに必要なイベントは、PointerDownとPointerUpです。あと、ボタンから指がズレていってもはやボタンを押していない状態を検知するためにPointerExitも使います。

イベントトリガーをセットしたら、イベントに対するスクリプトを書いていきましょう。

ボタンが押された瞬間を検知

まず最初に必要なのは、ボタンが押されたときにカウントを開始する部分です。

homeButton.cs

using UnityEngine;

public class homeButton : MonoBehaviour
{
    // カウント用
    private float _startTime = 0;
    // ロングタップ操作中かどうか
    private bool _isTapping = false;

    public void PointEnter()
    {
        _isTapping = true;
        _startTime = Time.time;
    }
}

やっていることは単純で、押された瞬間に「今押されているよ」フラグをOnにし、その瞬間の時刻を記録しています。イベントトリガのAdd New Event TypeでPointer Downを追加し、スクリプトをセットしましょう。スクリプト自体をボタンにアタッチしておくのを忘れずに!

※画像では後で説明する部分も全部セットしています

押され続けている時間をカウント(ロングタップ成立)

次に、押されている間は時間をカウントします。
一定時間が経てばロングタップ成立と判定して、目的の動作を実行しましょう。

homeBUtton.cs

using UnityEngine;

public class homeButton : MonoBehaviour
{
    // ロングタップ成立までの時間
    [SerializeField] float th = 2.0f;

    // ロングタップ成立のときの処理(インスペクタセット)
    [SerializeField] UnityEvent OnLongTap;

    // カウント用
    private float _startTime = 0;
    // ロングタップ操作中かどうか
    private bool _isTapping = false;

    public void PointEnter()
    {
        _isTapping = true;
        _startTime = Time.time;
    }

    private void FixedUpdate()
    {
        if (_isTapping)
        {
            float left_time = th - (Time.time - _startTime);

            // ロングタップの成立
            if (left_time < 0)
            {
                // 初期化しておく
                _isTapping = false;
                _startTime = 0f;

                // ロングタップの目的の処理を実行
                OnLongTap?.Invoke();
            }
        }
    }
}

ボタン内に目的の処理があるケースは稀だと思うので、外部から処理をセットできるようにUnityEventを使うようにしました。インスペクタでセットしてください。

UnityEventが謎でしたら、ボタンのスクリプト(ここではhomeButton.cs)に外部の処理を呼び出すメソッドを作っておいて直接実行してもいいです

    private void FixedUpdate()
    {
        if (_isTapping)
        {
                // 省略

                // ロングタップされたときの処理
                LongTapSaretaToki();
            }
        }
    }

    public void LongTapSaretaToki()
    {
        SceneManager.LoadScene("Title"); // ボタン外部の処理を実行
    }

離された瞬間を検知(ロングタップ不成立)

ここまでで、ロングタップできるところまでは実装できました。最後に、ロングタップが途中で離されて成立しなかったときの処理を作りこみましょう。

といっても、フラグをOffにするだけですが・・・。

homeButton.cs

using UnityEngine;

public class homeButton : MonoBehaviour
{
    // ロングタップ成立までの時間
    [SerializeField] float th = 2.0f;

    // ロングタップ成立のときの処理(インスペクタでセット)
    [SerializeField] UnityEvent OnLongTap;

    // カウント用
    private float _startTime = 0;
    // ロングタップ操作中かどうか
    private bool _isTapping = false;

    public void PointEnter()
    {
        _isTapping = true;
        _startTime = Time.time;
    }

    public void PointExit()
    {
        if (_isTapping)
        {
            _isTapping = false;
        }
    }

    private void FixedUpdate()
    {
        if (_isTapping)
        {
            float left_time = th - (Time.time - _startTime);

            // ロングタップの成立
            if (left_time < 0)
            {
                // 初期化しておく
                _isTapping = false;
                _startTime = 0f;

                // ロングタップの目的の処理を実行
                OnLongTap?.Invoke();
            }
        }
    }
}

このメソッドもイベントトリガにセットします。セットするのは、ボタンが離されたときのPointerUpと、押してはいるもののボタンから指が離れている状態になったときのPointerExitです。イベントが違うので対応するメソッドを別にしてもいいのですが処理が同じなのでここでは省略しました。

これでロングタップができました!やったね!

ロングタップ経過時間表示

ここまでで機能上はロングタップができるようになったのですが、何も表示がないので一見するとボタンが反応しないようにしか見えません。ロングタップしているという表示をしましょう。

ロングタップ表示の実装

ここでは、一般的と思われる円形のインジケータを表示する方法を紹介します。
長方形のゲージもほぼ同様にできます。

ボタンの子にロングタップ表示の円を2つ置きます。
1つは背景で、もう1つが時間経過で増えていく前景の円です。

この例では円の画像にアセットを使ってますが、こだわりなければペイントで書いた丸でいいと思います。

前景の円はImage TypeをSimpleからFilledに変更します。これによりFill Amountを指定すれば徐々に表示がされるようになります。Fill Amountは画像を表示する割合のパラメータで、これが0だと表示しない、1.0だと全部表示、0.5だと半分表示のような設定ができます。今回のようにFill MethodがRadial 360の場合は、Fill Amountの値に応じて円形に徐々に表示されていきます。

ちなみに、ここのFill MethodをRadialからHorizontalやVerticalにすることで直線型のゲージにもできます(その場合は画像も長方形にしてくださいね)。

次に、前景の円を操作するスクリプトを作ります。LongTapIndicatorのGameObjectにアタッチします。

LongTapIndicator.cs

using MoreMountains.Feedbacks;
using UnityEngine;
using UnityEngine.UI;

public class LongTapIndicator : MonoBehaviour, iPopable
{
    [Tooltip("前景(経過時間表示のサークル)")]
    [SerializeField] Image fore;

    /// <summary>
    /// 表示を更新します。
    /// </summary>
    /// <param name="amount"></param>
    public void SetFillAmount(float amount)
    {
        fore.fillAmount = amount;
    }
}

ロングタップするボタンの方では、カウンタをまわしている部分で上記のスクリプトに作った表示更新のメソッドを呼び出します。そのために、ボタンを押した瞬間に表示、ボタンを離したら非表示という処理もします。

homeButton.cs

using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;

public class homeButton : MonoBehaviour
{

    // ロングタップ成立までの時間
    [SerializeField] float th = 2.0f;

    // カウント用
    private float _startTime = 0;
    // ロングタップ操作中かどうか
    private bool _isTapping = false;

    /// <summary>
    /// ロングタップの経過時間の表示(インスペクタから設定)
    /// </summary>
    [SerializeField] LongTapIndicator LongTapIndicator;

    // ロングタップ成立のときの処理
    [SerializeField] UnityEvent OnLongTap;


    public void OnPointDown()
    {
        _isTapping = true;
        _startTime = Time.time;

        LongTapIndicator.gameObject.SetActive(true);
        LongTapIndicator.SetFillAmount(0f);
    }

    public void OnPointUp()
    {
        if (_isTapping)
        {
            _isTapping = false;
            LongTapIndicator.gameObject.SetActive(false);
        }
    }

    private void FixedUpdate()
    {
        if (_isTapping)
        {
            float left_time = th - (Time.time - _startTime);

            // 全体タイムに対する経過時間の割合
            float time_circle = (Time.time - _startTime) / th;

            // 表示
            LongTapIndicator.SetFillAmount(time_circle);

            // ロングタップの成立
            if (left_time < 0)
            {
                // 初期化しておく
                _isTapping = false;
                _startTime = 0f;

                // 表示を消す
                LongTapIndicator.gameObject.SetActive(false);

                // ロングタップの目的の処理を実行
                OnLongTap?.Invoke();

                // ロングタップされたときの処理
                LongTapSaretaToki();
            }
        }
    }

    public void LongTapSaretaToki()
    {
        OnLongTap?.Invoke();
    }
}

表示の部分では、成立までの時間の変数”th”に対する現在の経過時間の割合をセットしています。

これで、ロングタップ表示の実装ができました!

(補足) 表示のツイーン

こまかいところなのですが、冒頭の動画では経過時間表示のUIの出現と消滅のときにツイーンしています。急に出てきてもいいのですが、一瞬のピョンがあることで高級感が増すようです。

本記事では解説はしませんが、ツイーンの実装には無料の超有名アセットDoTweenなどを使ってピョンとするといいと思います。今回の例ではFEELを使いました。

まとめ

本記事では、Unityで長押し(ロングタップ)機能を実装する方法について説明しました。

EventTriggerコンポーネントを活用し、PointerDown、PointerUp、PointerExitイベントを用いて長押し操作を検出します。

さらに長押し成立時に時間経過を示す円形ゲージUIを表示する方法も紹介しました。

これらの技術によりユーザーが直感的に操作できるUIを作成することができると思います。

かわいい我が子にオリジナルアプリを!

コメント

タイトルとURLをコピーしました