- 2017/07/06 【Unity】【C#】長押し(ロングタップ)を取得してコールバックする
- 2017/07/05 【Unity】【C#】スワイプ(フリック)を判定、方向を取得してコールバックする
« prev next »
【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のトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
- 関連記事
category: Unity
thread: ゲーム開発
janre: コンピュータ
tag: Unityライブラリ Unityプラグイン InputManager FantomPlugin【Unity】【C#】スワイプ(フリック)を判定、方向を取得してコールバックする 
2017/07/05 Wed [edit]
今回は Unity 上でスワイプ動作を取得してみよう。これと前回の 「一番近い45・90度ごとの角度を求める」 を組み合わせてスマホアプリなどに利用すると、画面をスワイプしてカメラをスイッと回転、なんてことが簡単にできる。
|
(※) Unity 5.6.1p3 / Windows10(x64) で確認
●スワイプを判定して、方向を取得してコールバックする
using System;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// スワイプ方向を取得してコールバックする
/// 2017/7/5 Fantom (Unity 5.6.1p3)
/// http://fantom1x.blog130.fc2.com/blog-entry-250.html
///(使い方)
///・適当な GameObject にアタッチして、インスペクタから OnSwipe(Vector2 を1つ引数にとる)にコールバックする関数を登録すれば使用可。
///・またはプロパティ SwipeInput.Direction をフレーム毎監視しても良い(こちらの場合は無し(Vector2.zero)も含まれる)。
///(仕様説明)
///・タッチの移動量(エディタやスマホ以外の場合はマウス)で判定する。画面幅の Valid Width(%)以上移動したときスワイプとして認識する。
///・ただし、移動が制限時間(Timeout)を超えた時は無視する。
///・複数の指では認識できない(※2つ以上の指の場合はピンチの可能性もあるため無効とする)。
///・タッチデバイスを UNITY_ANDROID, UNITY_IOS としているので、他のデバイスも加えたい場合は #if の条件文にデバイスを追加する(Input.touchCount が取得できるもののみ)。
/// </summary>
public class SwipeInput : MonoBehaviour
{
//設定値
public bool widthReference = true; //画面幅(Screen.width)サイズを比率の基準にする(false=高さ(Screen.height)を基準)
public float validWidth = 0.25f; //スワイプとして認識する移動量の画面比[画面幅に対する比率](0.0~1.0:1.0で端から端まで。この値より長い移動量でスワイプとして認識する)
public float timeout = 0.5f; //スワイプとして認識する時間(これより短い時間でスワイプとして認識する)
//Local Values
Vector2 startPos; //スワイプ開始座標
Vector2 endPos; //スワイプ終了座標
float limitTime; //スワイプ時間制限(この時刻を超えたらスワイプとして認識しない)
bool pressing; //押下中フラグ(単一指のみの取得にするため)
Vector2 swipeDir = Vector2.zero; //取得したスワイプ方向(フレーム毎判定用)[zeroがなしで、left, right, up, downが方向]
//スワイプ方向取得プロパティ(フレーム毎取得用)
public Vector2 Direction {
get { return swipeDir; }
}
//スワイプイベントコールバック(インスペクタ用)
[Serializable]
public class SwipeHandler : UnityEvent<Vector2> {
}
public SwipeHandler OnSwipe; //引数の Vector2 でスワイプ方向を返す
//アクティブになったら、初期化する(アプリの中断などしたときはリセットする)
void OnEnable()
{
pressing = false;
}
// Update is called once per frame
void Update()
{
swipeDir = Vector2.zero; //フレーム毎にリセット
#if !UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS) //タッチで取得したいプラットフォームのみ
if (Input.touchCount == 1) //複数の指は不可とする(※2つ以上の指の場合はピンチの可能性もあるため)
#endif
{
if (!pressing && Input.GetMouseButtonDown(0)) //押したとき(左クリック/タッチが取得できる)
{
pressing = true;
startPos = Input.mousePosition;
limitTime = Time.time + timeout;
}
else if (pressing && Input.GetMouseButtonUp(0)) //既に押されているときのみ(※この関数は2つ以上タッチの場合、どの指か判別できないので注意)
{
pressing = false;
if (Time.time < limitTime) //時間制限前なら認識
{
endPos = Input.mousePosition;
Vector2 dist = endPos - startPos;
float dx = Mathf.Abs(dist.x);
float dy = Mathf.Abs(dist.y);
float requiredPx = widthReference ? Screen.width * validWidth : Screen.height * validWidth;
if (dy < dx) //横方向として認識
{
if (requiredPx < dx) //長さを超えていたら認識
swipeDir = Mathf.Sign(dist.x) < 0 ? Vector2.left : Vector2.right;
}
else //縦方向として認識
{
if (requiredPx < dy) //長さを超えていたら認識
swipeDir = Mathf.Sign(dist.y) < 0 ? Vector2.down : Vector2.up;
}
//コールバックイベント
if (swipeDir != Vector2.zero)
{
if (OnSwipe != null)
OnSwipe.Invoke(swipeDir); //UnityEvent
}
}
}
}
#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 を使ってコードから追加する方法もある。以下のページに色々実装方法を載せてあるので、自由に改造して使って欲しい。
・【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録

適当なスクリプトに OnSwipe(Vector2) のようなメソッドを書いて、インスペクタで登録する。
|
画面をスワイプすると、カメラがキャラの廻りをぐるりと90度ごと回転して
眺められるようになっている(※ただし、このゲームには特に必要ない機能(笑))。
>> PronamaChan in Action Game!(Android 版)
※とりあえず試してみたい方には、最新版をビルドした apk デモをダウンロードできます。動作確認にもどうぞ。


Android 4.2以上
※「提供元不明アプリのインストール」許可が必要です。
(関連記事)
【Unity】【C#】n度ごとの角度を返す(一定角度ごとの切り捨て、切り上げ、四捨五入)
【Unity】【C#】長押し(ロングタップ)を取得してコールバックする
【Unity】【C#】ピンチ操作を取得してコールバックする
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
【Unity】【C#】インスペクタでの UnityEvent のコールバック登録の有無を調べる
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
- 関連記事
category: Unity
thread: ゲーム開発
janre: コンピュータ
tag: Unityライブラリ Unityプラグイン InputManager FantomPlugin