【Unity】【C#】長押し(ロングタップ)を取得してコールバックする 
2017/07/06 Thu [edit]
今回は Unity 上で長押し動作を取得してみよう。といっても実は前回の 「スワイプの検出」 と基本的には変わらない。方向ではなく、一定領域内で一定時間押しっぱなしか否かで判別してるだけだ。ただ今回は UI 上での検出と、画面全域での検出の2パターンを載せておいた。ほとんどの場合、UI 上(画像など)で取得するケースの方が利用し易いと思うが、用途に応じて使い分けると良いだろう。
|
(※) Unity 5.6.1p3 / Windows10(x64) で確認
●UI 上で長押しを検出する
こちらは画像やテキスト、ボタンなど UI 上での長押しを検出するのに向いている。標準の UI(UnityEngine.UI)を使用して、その UI の領域内(大抵は RectTransform の範囲)でだけ認識したい場合に有効だ。
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
/// <summary>
/// 長押しを取得してコールバックする(UI上での判定に向いている。EventSystem と Graphics Raycaster が必要)
/// 2017/7/6 Fantom (Unity 5.6.1p3)
/// http://fantom1x.blog130.fc2.com/blog-entry-251.html
///(使い方)
///・Image や Text, Button などの UI を持つ GameObject にアタッチして、インスペクタから OnLongClick(引数なし)にコールバックする関数を登録すれば使用可。
///・シーンに EventSystem、(ルート)Canvas に Graphics Raycaster がアタッチされている必要がある。
///(仕様説明)
///・EventSystem からのイベント(OnPointerDown, OnPointerUp, OnPointerExit)を取得し、一定時間(Valid Time)押下され続けていたら長押しと認識する。
///・途中で有効領域外(UIから外れる)へ出たり、指を離したりしたときは無効。
///・はじめの指のみ認識(複数指の場合、ピンチの可能性があるため無効とする)。
///※スマホだとUIを完全透過にしていると、上手く認識できないようなので注意(可視できる画像等ならOK)。
/// </summary>
public class LongClickEventTrigger : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
public float validTime = 1.0f; //長押しとして認識する時間(これより長い時間で長押しとして認識する)
//Local Values
float requiredTime; //長押し認識時刻(この時刻を超えたら長押しとして認識する)
bool pressing = false; //押下中フラグ(単一指のみの取得としても利用)
//長押しイベントコールバック(インスペクタ用)
public UnityEvent OnLongClick;
// Update is called once per frame
void Update()
{
if (pressing) //はじめに押した指のみとなる
{
if (requiredTime < Time.time) //一定時間過ぎたら認識
{
//コールバックイベント
if (OnLongClick != null)
OnLongClick.Invoke(); //UnityEvent
pressing = false; //長押し完了したら無効にする
}
}
}
//UI領域内で押下
public void OnPointerDown(PointerEventData data)
{
if (!pressing) //ユニークにするため
{
pressing = true;
requiredTime = Time.time + validTime;
}
else
{
pressing = false; //2本以上の指の場合、ピンチの可能性があるため無効にする
}
}
//※スマホだとUIを完全透過にしていると、指を少し動かしただけでも反応してしまうので注意
public void OnPointerUp(PointerEventData data)
{
if (pressing) //はじめに押した指のみとなる
pressing = false;
}
//UI領域から外れたら無効にする
public void OnPointerExit(PointerEventData data)
{
if (pressing) //はじめに押した指のみとなる
pressing = false; //領域から外れたら無効にする
}
}
注意点としては EventSystem からイベントを取得しているのでシーンに必要なことと(無ければ、メニューから GameObject>UI>EventSystem で作る)、UI のあるルート Canvas に Graphics Raycaster がアタッチされている必要があるということだ。これらはメニューから UI オブジェクトをヒエラルキーに追加した場合は自動で付いてくる。自作 UI などの時は、無いと取得できないので気をつけよう。
あと画像など UI が透過率(アルファ)が低いときは、スマホなどでは上手く取得できないようだ(指を少し動かしただけで OnPointerUp() が発生する)。可視できるくらいの透過率なら問題ないようだ。
あと、コールバックイベントはインスペクタで設定(UnityEvent)するようにしてあるが、コードだけにしたいなら、Action を使ったコールバックや、直接 delegate でコールバックするように書き換えても良いだろう。もちろんそのままで UnityEvent.AddListener を使ってコードから追加する方法もある。以下のページに色々実装方法を載せてあるので、自由に改造して使って欲しい。
・【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
●画面全域、または画面の一部領域上で長押しを検出する
こちらは画面全体や一部領域(画面の半分とか、%指定しやすい大きさ)に向いている。標準のマウスやタッチの取得:Input.GetMouseButton() を用いているので、EventSystem や Graphics Raycaster は必要ない。
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 長押しを取得してコールバックする(画面全域や画面の一部領域などでの判定に向いている)
/// 2017/7/6 Fantom (Unity 5.6.1p3)
/// http://fantom1x.blog130.fc2.com/blog-entry-251.html
///(使い方)
///・適当な GameObject にアタッチして、インスペクタから OnLongClick(引数なし)にコールバックする関数を登録すれば使用可。
///・またはプロパティ LongClickInput.IsLongClick をフレーム毎監視しても良い(こちらの場合は false も含まれる)。
///(仕様説明)
///・画面全体を(0,0)-(1,1)とし、有効領域内(Valid Area)で一定時間(Valid Time)押下されていたら認識する。
///・途中で有効領域外へ出たり、指を離したりしたときは無効。
///・はじめの指のみ認識(複数の指の場合、ピンチの可能性があるため無効とする)。
///・タッチデバイスを UNITY_ANDROID, UNITY_IOS としているので、他のデバイスも加えたい場合は #if の条件文にデバイスを追加する(Input.touchCount が取得できるもののみ)。
/// </summary>
public class LongClickInput : MonoBehaviour
{
//設定値
public float validTime = 1.0f; //長押しとして認識する時間(これより長い時間で長押しとして認識する)
//認識する画面上の領域
public Rect validArea = new Rect(0, 0, 1, 1); //長押しとして認識する画面領域(0.0~1.0)[(0,0):画面左下, (1,1):画面右上]
//Local Values
Vector2 minPos = Vector2.zero; //長押し認識ピクセル最小座標
Vector2 maxPos = Vector2.one; //長押し認識ピクセル最大座標
float requiredTime; //長押し認識時刻(この時刻を超えたら長押しとして認識する)
bool pressing; //押下中フラグ(単一指のみの取得にするため)
bool isValid = false; //フレーム毎判定用
//長押検出プロパティ(フレーム毎取得用)
public bool IsLongClick {
get { return isValid; }
}
//長押しイベントコールバック(インスペクタ用)
public UnityEvent OnLongClick; //引数なし
//アクティブになったら、初期化する(アプリの中断などしたときはリセットする)
void OnEnable()
{
pressing = false;
}
// Update is called once per frame
void Update()
{
isValid = false; //フレーム毎にリセット
#if !UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS) //タッチで取得したいプラットフォームのみ
if (Input.touchCount == 1) //複数の指は不可とする(※2つ以上の指の場合はピンチの可能性もあるため)
#endif
{
if (!pressing && Input.GetMouseButtonDown(0)) //押したとき(左クリック/タッチが取得できる)
{
Vector2 pos = Input.mousePosition;
minPos.Set(validArea.xMin * Screen.width, validArea.yMin * Screen.height);
maxPos.Set(validArea.xMax * Screen.width, validArea.yMax * Screen.height);
if (minPos.x <= pos.x && pos.x <= maxPos.x && minPos.y <= pos.y && pos.y <= maxPos.y) //認識エリア内
{
pressing = true;
requiredTime = Time.time + validTime;
}
}
if (pressing) //既に押されている
{
if (Input.GetMouseButton(0)) //押下継続(※この関数は2つ以上タッチの場合、どの指か判別できない)
{
if (requiredTime < Time.time) //一定時間過ぎたら認識
{
Vector2 pos = Input.mousePosition;
if (minPos.x <= pos.x && pos.x <= maxPos.x && minPos.y <= pos.y && pos.y <= maxPos.y) //認識エリア内
{
isValid = true;
//コールバックイベント
if (OnLongClick != null)
OnLongClick.Invoke(); //UnityEvent
}
pressing = false; //長押し完了したら無効にする
}
}
else //MouseButtonUp, MouseButtonDown
{
pressing = false;
}
}
}
#if !UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS) //タッチで取得したいプラットフォームのみ
else //タッチが1つでないときは無効にする(※2つ以上の指の場合はピンチの可能性もあるため)
{
pressing = false;
}
#endif
}
}
判定方法は「スワイプの検出」とほぼ同じで、有効領域内で一定時間押しっぱなしか否かを判定にしただけだ。
また、できればエディタ上での動作確認やマウスでのロングタップ認識もできたら便利と考えたため、Touch 系での取得や判定は極力避け、Input.GetMouseButtonDown() のようなマウス操作の取得(タッチも取得できる)で書いてある。本来ならタッチの指のID(Touch.fingerId)でユニークな指の判定をするべきだと思うが、押下中フラグ(pressing)と Input.touchCount を1つでユニーク動作にしてるため、指の判定は省略してある。なのでPCなどで複数のマウスを使ったりすれば誤認識するかもしれないが、まぁ、普通に使う分には大丈夫だろう。必要あれば指ID(ポインタID)での判別を追加しても良いかも知れない。
プラットフォームに関しては、タッチデバイスとして Android と iOS しか考えてないので、必要あればプリプロセッサディレクティブ(#if 文)に条件を追加して欲しい。これらは Input.touchCount が取得できるものに限られる。
あと、コールバックイベントはインスペクタで設定(UnityEvent)するようにしてあるが、コードだけにしたいなら、Action を使ったコールバックや、直接 delegate でコールバックするように書き換えても良いだろう。もちろんそのままで UnityEvent.AddListener を使ってコードから追加する方法もある。以下のページに色々実装方法を載せてあるので、自由に改造して使って欲しい。
・UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
プラグインデモをダウンロード


Android 4.2以上
※「提供元不明アプリのインストール」許可が必要です。
(関連記事)
【Unity】【C#】スワイプ(フリック)を判定、方向を取得してコールバックする
【Unity】【C#】ピンチ操作を取得してコールバックする
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
【Unity】【C#】インスペクタでの UnityEvent のコールバック登録の有無を調べる
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
- 関連記事
トラックバック
トラックバックURL
→http://fantom1x.blog130.fc2.com/tb.php/251-b948783f
この記事にトラックバックする(FC2ブログユーザー)
| h o m e |