Unityでスワイプ検知してドラッグ位置を表示する

2021年6月4日金曜日

UniRx Unity アローディフェンス

t f B! P L

はじめに

8年ぶりにUnityを使ってカジュアルゲームを作ってAndroid向けにリリースしました。

画面上をスワイプして弓を撃つゲームで、アングリーバードみたいな感じです。
アングリーバードとちょっとだけ違うのは、画面全体どこでもスワイプすれば撃てることでしょうか?
このスワイプ検知機能を今回のアプリでどうやって実装したかを残しておこうと思います。

↑スワイプ開始位置の表示なども簡単に実装できます

開発環境

  • Unity 2019.4.20f1
  • MacBook Air
  • UniRx

画面全体検知の方法の概要

やり方の概要としては以下の通りです。
  1. 透明のパネルを全画面で追加
  2. パネルに検知用スクリプトをアタッチする
このやり方であればUnityエディタ上でもスマホ実機上でも同じように操作でき、他のボタンの制御も問題なくタッチを検知できます(他のボタン類はPanelよりも下に置く必要があります)

1. 透明のパネルを全画面で追加

  1. パネルの追加
    1. ヒエラルキーの右クリックからUI > Panel
  2. パネルの設定
    1. 追加されたPanelのインスペクタから以下の通り設定
      1. Canvas Rendererコンポーネント
        1. Cull Transparent Meshのチェックをオンする
      2. Imageコンポーネント
        1. Source Image を None (Sprite)に変更
        2. Colorを透明に変更(A値を0にする)
Cull Transparent Meshのチェックをオンしているのは透明なオブジェクトの描画をスキップすることで処理負荷や意図しない描画を避けるためなので重要です。

2. パネルに検知用スクリプトをアタッチする

  1. 以下のスクリプトを用意してPanelアタッチする
using UnityEngine;
using UnityEngine.EventSystems;

public class TouchScreen : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    public void OnBeginDrag(PointerEventData eventData)
    {
        Debug.Log("OnBeginDrag pos=" + eventData.position);
    }

    public void OnDrag(PointerEventData eventData)
    {
        Debug.Log("OnDrag pos=" + eventData.position);
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        Debug.Log("OnEndDrag pos=" + eventData.position);
    }
}

これでエディタ上で実行してマウスで押下→移動→開放をするとコンソールにログが出るはずです。

複数のスクリプトで検知結果を受け取れるようにする

実際にゲームに組み込むときはUniRxを使って情報を外部スクリプトに通知するようにしました。もっと汎用的に使いませるもにできると思うのですが、そのへんは要改良ですね。
通知を受けるスクリプトでは以下のようなことをしています
  • スローモーション開始/停止
  • キャラクターの弓構えアニメ開始/終了
  • キャラクターの手をドラッグしたベクトルに応じて向ける
  • 弓の向きをドラッグしたベクトルに応じて回転
  • 弓矢の発射
  • ドラッグ開始位置と終了位置を画面に表示
using UnityEngine;
using UniRx;
using UnityEngine.EventSystems;
using System;

public class ScreenInput : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    private static Subject<Vector2> beginDragSubject = new Subject<Vector2>();
    private static Subject<Vector2> dragSubject = new Subject<Vector2>();
    private static Subject<Vector2> dragPosSubject = new Subject<Vector2>();
    private static Subject<Vector2> endDragSubject = new Subject<Vector2>();
    private Vector2 beginPosition;

    public IObservable<Vector2> OnBeginDragging => beginDragSubject;
    public IObservable<Vector2> OnDragging => dragSubject;
    public IObservable<Vector2> OnDragPosChanged => dragPosSubject;
    public IObservable<Vector2> OnEndDragging => endDragSubject;

    public void OnBeginDrag(PointerEventData eventData)
    {
        beginPosition = eventData.position;
        beginDragSubject.OnNext(beginPosition);

    }

    public void OnDrag(PointerEventData eventData)
    {
        dragSubject.OnNext(eventData.position - beginPosition);
        dragPosSubject.OnNext(eventData.position);

    }

    public void OnEndDrag(PointerEventData eventData)
    {
        endDragSubject.OnNext(eventData.position - beginPosition);
    }
}

ドラッグ開始位置と終了位置を画面に表示する例

受け取り側のコードの例として、ドラッグ位置にマーカーを表示する方法を示します。
  1. 透明なPanelに↑のScreenInputスクリプトと↓のSwipeVizをアタッチ
  2. Panelの子要素にUI > Imageを2つ追加
    1. 名前を適当につける(今回はBeginMarkerとEndMarkerとした)
    2. 適当に画像を登録する(今回は最初から入っているKnobとして色を変えた)
    3. インスペクタからActiveのチェックを外し非表示にしておく
      1. ※インスペクタの一番上のオブジェクト名左のチェックを外す
  3. PanelのインスペクタからSwipeVizスクリプトに各オブジェクトを登録
    1. Screen Input:Panelをドラッグして登録
    2. BeginMarker:子要素に追加したImage1
    3. EndMarker:子要素に追加したImage2

using UnityEngine;
using UniRx;

public class SwipeViz : MonoBehaviour
{
    [SerializeField] ScreenInput screenInput;
    [SerializeField] GameObject beginMarker;
    [SerializeField] GameObject endMarker;

    void Start()
    {
        // ドラッグ開始位置情報を受け取ったら開始位置マーカーを表示
        screenInput.OnBeginDragging
            .Subscribe(p =>
            {
                beginMarker.SetActive(true);
                beginMarker.transform.position = p;
            })
            .AddTo(this.gameObject);

        // ドラッグ中は終了位置マーカーを表示して位置を更新し続ける
        screenInput.OnDragPosChanged
            .Subscribe(p =>
            {
                endMarker.SetActive(true);
                endMarker.transform.position = p;
            })
            .AddTo(this.gameObject);

        // ドラッグ終了時は各マーカーを非表示にする
        screenInput.OnEndDragging
            .Subscribe(p =>
            {
                beginMarker.SetActive(false);
                endMarker.SetActive(false);
            })
            .AddTo(this.gameObject);
    }
}

おわりに

画面全体のドラッグ入力の受け取り方法を紹介しました。
もう少し汎用的なモジュールにできそうなので、もっとよい方法やコードの例知っているよ!という方はコメントください。

上記のドラッグ検知を実装した実際のゲームは以下から遊べますので、興味があればぜひ遊んでみてください。

Translate

このブログを検索

  • ()
  • ()
もっと見る

QooQ