2017/02/13

[Unity] TouchScriptの覚え書き



TouchScriptって

Unityで個人的な物を作るとき、入力周りにはTouchScriptというアセットをよく使います。
TouchScriptというのは、マルチプラットフォームに対応したタッチ入力やジェスチャ入力を簡単に扱えるようになるアセットです。現在絶讃Asset Storeにて配布中です。無料です。
なんでもPokemonをGOするアプリにも使われていたとかなんとか。
簡単に使えるようになる、はずなのですが、よくセットアップ時に何か1つ抜けてたりするので備忘録ついでにブログに残してみます。

セットアップ

この記事はUnity5.4f3+Touchscript 8.2基準で書いています。

アセットをインポートしたら空のGameObjectを作り適当に「TouchManager」とか名前をつけます。
それにTouchScript/TouchManagerとTouchScript/InputSouces/Standard Inputのコンポーネントを追加します。Standard Inputは無くても自動で追加されるんですが、あった方が後々便利かなと思います。
ついでにプロジェクトから「TouchScript/Prefabs/Touch Visualizer」をhierarchyに放り込むとタッチの状態が視覚化できて便利です。

ジェスチャの計画

ゲームの種類や用途に応じてコンポーネントを追加していきます。
2DゲームならカメラにTouchScript/Layers/CameraLayer2Dを割りあてます。
3DならCameraLayerをカメラに割り当てます。
 
fullscreen Layerは全画面で反応するようにするもので、UI LayerはUI要素に反応するもの。こちらは後ほど説明します。

オブジェクトにジェスチャを割り当て

入力に反応して欲しいオブジェクトに対してジェスチャのコンポーネントを追加していきます。
TouchScriptにあるジェスチャコンポーネントで主要なものは以下の通り。
  • タップに反応する「TapGesture」
  • フリックに反応する「FlickGesture」
  • 長押しに反応する「LongPressGesture」
  • ドラッグ、ピンチ(拡大縮小)や回転に反応する「TransformGesture」
  • TransformGestureと基本同じだけどスクリーン座標を返す「ScreenTransformGesture」
オブジェクトを中心として一本指で回転やピンチをする「PinnedTransFormGesutre」
Transform系のGestureはpositionのみとかscaleのみとか振る舞いを絞り込むことが出来ます。

他にも指を離したときに特化したRelease Gestureとかありますが割愛です。
ちなみにScreen Transform Gestureは使ったことがないのでよく分かりません。FullscreenLayerと組み合わせてカメラ移動とかに使うのでしょうか?
これらから欲しい挙動のコンポーネントを反応させたいGameObjectに割り当ててます。
Colliderを追加するのも忘れずに。

特定のオブジェクトのタップを得る場合

反応させたいGameObjectにTap Gestureを割り当てます。
そして次のようなコードを書きます。
usingは必要に応じてTouchScript.Gesturesなどを追加して下さい。
void OnEnable()
{
    // TapGestureのdelegateに登録
    GetComponent<TapGesture>().Tapped += tappedHandle;
}

void OnDisable()
{
    UnsubscribeEvent();
}

void OnDestroy()
{
    UnsubscribeEvent();
}

void UnsubscribeEvent()
{
    // 登録を解除
    GetComponent<TapGesture>().Tapped -= tappedHandle;
}

void tappedHandle(object sender, System.EventArgs e)
{
    //処理したい内容
}

特定のオブジェクトのフリックを得る場合


反応させたいGameObjectにFlick Gestureを割り当てます。
そして次のようなコードを書きます。
usingは必要に応じてTouchScript.Gesturesなどを追加して下さい。

void OnEnable()
{
    // FlickGestureのdelegateに登録
    GetComponent<FlickGesture>().Tapped += FlickedHandle;
}

void OnDisable()
{
    UnsubscribeEvent();
}

void OnDestroy()
{
    UnsubscribeEvent();
}

void UnsubscribeEvent()
{
    // 登録を解除
    GetComponent<FlickGesture>().Tapped -= FlickedHandle;
}

void FlickedHandle(object sender, System.EventArgs e)
{

    var gesture = sender as FlickGesture;
    // ジェスチャが適切かチェック
    if (gesture.State != FlickGesture.GestureState.Recognized)
    return;
    // 処理したい内容
    // gesture.ScreenFlickVectorにフリック方向が入るので
    // if (gesture.ScreenFlickVector.y < 0)としたら下方向へのフリックを検知できる
}

ScreenFlickVectorを使ってフリック方向を得る事が出来ます。
その他に、反応する方向、距離や時間なども設定出来ます。インスペクタからも設定出来ますが、コードで設定する場合は こちらを確認すると良いです。

ドラッグ、拡大や回転を得る場合

反応させたいGameObjectにTransform Gestureを割り当てます。
TransformGestureについてはTransformerというコンポーネントを加えると、コードを書かずに移動や拡大を実現出来ます。もちろんイベントハンドラを使って自分でtransformを設定すればtransformerは使わないでもいいですが、回転や拡大縮小を使う場合は、ぐっと楽になります。

今回はTransformerを使う方法で書きます。
コンポーネント追加後はTransform GestureでPositionやScaleなど有効にしたいtransformを指定したら、これでとりあえずドラッグに合わせてオブジェクトが動きます。

あとはイベントハンドラを使って必要な処理を追加していきます。
usingは必要に応じてTouchScript.Gesturesなどを追加して下さい。

void OnEnable()
{
    // Transform Gestureのdelegateに登録
    GetComponent<TransformGesture>().TransformStarted+= TransformStartedHandle; // 変形開始
    GetComponent<TransformGesture>().StateChanged+= StateChangedHandle; // 状態変化
    GetComponent<TransformGesture>().TransformCompleted+= TransformCompletedHandle; // 変形終了
    GetComponent<TransformGesture>().Cancelled+= CancelledHandle; // キャンセル
}

void OnDisable()
{
    UnsubscribeEvent();
}

void OnDestroy()
{
    UnsubscribeEvent();
}

void UnsubscribeEvent()
{
    // 登録を解除
    GetComponent<TransformGesture>().TransformStarted -= TransformStartedHandle;
    GetComponent<TransformGesture>().StateChanged -= StateChangedHandle;
    GetComponent<TransformGesture>().TransformCompleted -= TransformCompletedHandle;
    GetComponent<TransformGesture>().Cancelled -= CancelledHandle;
}

void TransformStartedHandle(object sender, System.EventArgs e)
{
// 変形開始のタッチ時の処理
}

void StateChangedHandle(object sender, System.EventArgs e)
{
// 変形中のタッチ時の処理
}

void TransformCompletedHandle(object sender, System.EventArgs e)
{
// 変形終了のタッチ時の処理
}
void CancelledHandle(object sender, System.EventArgs e)
{
// 変形終了のタッチ時の処理
}
transfromerコンポーネントを使うとこれだけで2本指での拡大、移動、回転が可能になります。

PC上でマウスを使う場合は二本指ジェスチャは出来ないので、そのときはaltキーを押してクリックすると、そのクリックは「押しっぱなし」と見なされマルチタッチジェスチャを入力することが出来ます。

transformGestureのインスペクタ上にある「transform Type」の項目は以下のようにするとスクリプトで設定することが出来ます。
// using TouchScript.Gestures.Base;がある前提で
// 値には以下の値を入れる
// 回転ならTransformGestureBase.TransformType.Rotation
// 移動ならTransformGestureBase.TransformType.Translation
// 拡大ならTransformGestureBase.TransformType.Scaling
// 例
GetComponent<TransformGesture>().Type = TransformGestureBase.TransformType.Rotation | TransformGestureBase.TransformType.Scaling; 

画面全体のタッチを取得する場合

画面全体つまりオブジェクトが無いところで起きたタッチイベントを取得することが出来ます。

カメラの移動などに使ったり、画面を切り替えるとき、とにかく画面をタップしてほしい時などに使うのだと思います。

使い方はまずHierarchyに空のgameObjectを作り、touchManagerとfullScreenLayerコンポーネントをセットします。すると、TouchManagerコンポーネントに「FullScreen」レイヤーが認識された旨追加されます。

 後は作成したgameObjectにタップなどのジェスチャを割り当てていきます。内容は上のタップやフリックなどと同じです。

uGUIにジェスチャを割り当てる

やり方はFullScreenLayerとほぼ同じです。
FullScreenLayerの代わりにUILayerをGameObjectに割り当てます。
割り当てるのはべつにCanvasなどで無くともTouchManagerをセットしたGameObjectでも良いみたいです。

標準のuGUIとTouchScriptを混在させる

標準のuGUIとTouchscriptを混在して使う場合、例えばキャラクターの移動にはTouchScriptを使ってアイテムの選択にはuGUIを使う場合は、EventSystemにあるStandaloneInputModuleをTouchScriptInputModuleに置き換えます。
これをしないとuGUI側が無効になります。

タッチされた位置を知る

タッチの情報は、イベントハンドラ内のobjectから得ることができます。
例の場合、senderからタッチされた情報を得るには以下のようにします。

// transformGestureの場合

   var gesture = sender as TransformGesture;
   TouchHit hit;

   gesture.GetTargetHitResult(out hit);

これでhitにはタッチされた場所などの情報が渡されます。あとはhit.Pointで位置が取得で見ます。

hit.NomalにはタッチされたColliderとの法線が得られます。
例えば床に当たったときは(0f,1f,0f)が、壁等の場合は(1f,0f,0f)などが得られますので、必要に応じて組み合わせます。

シーン上のタップの状況を知る

TouchScriptにはタッチジェスチャごとにSendMassageを送ることが出来ますが、シーン全体のタッチを管理するTouchManagerのイベントハンドラからタッチ情報を得ることも出来ます。

// using TouchScript; として

// touchはTouchPointが入る
// touch.Idはタッチした順の値

   TouchManager.Instance.TouchesBegan +=
       (sender, args) => {
           foreach (var touch in args.Touches)
               Debug.Log("Began: " + touch.Id);
       };

    TouchManager.Instance.TouchesEnded +=
       (sender, args) => {
       foreach (var touch in args.Touches)
               Debug.Log("Ended: " + touch.Id);
       };

例ではtouchから TouchPointの情報を得ることが出来ます。具体的にはタップした順や位置、transform、入力方法などです。

ただこのイベントだと「どのオブジェクトがタッチされたか」しか分からないのです。(もしジェスチャまで分かる方法があるなら教えて下さい)
なので、ジェスチャごとの挙動は各ジェスチャのイベントハンドラに書いた方が良いでしょう。

複数のジェスチャを認識する

TouchScriptは一つのGameObjectに複数のジェスチャコンポーネントが割り当てられていると、排他的に登録順に処理していきます。

そのため、一つのジェスチャが認識中は他のジェスチャを認識しません。
例えば、とあるボタンは長押しをすると技の詳細表示、そのままフリックすると技を発動したい、とします。普通に割り当てたままだと、LongPressGestureがタッチ情報を掴んでしまいflickGestureへ流れなくなります。

そんな時はfriendlyGesturesというのを割り当てます。
やり方は、インスペクタから同時に認識させたいジェスチャのコンポーネントへジェスチャを含んだGameObjectをD&Dするか、スクリプトで割り当てます。

 
GetComponent<LongPressGesture>().AddFriendlyGesture(GetComponent<FlickGesture>());
 これで長押しとフリックが同時に認識するようになります。

まとめ

TouchScriptはやりたいことをイベントハンドラに登録すれば出来てしまうと言うお手軽さと、マウスでもマルチタッチジェスチャがテストできるのが素敵なフレームワークです。

なのですけど、多機能なだけに「これ、どこにセットするんだっけ」と毎回ググる羽目になるので自分宛の備忘録のつもりに書いてみました。

この記事からstackEditで書いて投稿しています。今更感満載ですが、markdownでかけるのは楽です。

Blogger標準のエディタも悪くは無いんですけど、WYSIWYGなエディタの宿命なのかよく分からない見出しタグが入るのが残念でした。
しばらくこれを試してみようと思います。