【Unity】円スワイプジェスチャーチェッカーを実装

スターフォックス・ライクな3Dシューティングを作っている時に、ローリングボタンを設置するスペースがなくなったので考案。
中々の精度で、円スワイプジェスチャーの判定が取れます。
(勿論、PC/スマホ可)

サンプル動画

以下の通り、一通りの図形ジェスチャーを試した。

  • 円: 判定
  • 渦巻き: 判定
  • 反時計回り: 判定
  • 三角: 無判定
  • 四角: 無判定
  • バツ: 無判定
  • 直線: 無判定
  • ジグザグ: 無判定

注意点

  • 通常のゲーマーのスワイプ速度なら大丈夫だが、ゆっくり円を描くと円判定に失敗する場合がある。*
  • MouseButtonUp(指を離した判定)は、毎フレームチェックしないといけない為、同コルーチンにまとめられない。

*(前スワイプ地点のベクトルと、現在スワイプ地点のベクトルの外積で、同じ回転方向にスワイプしているかチェックするのが、ゆっくりだとなんか上手くいっていない)

【コード】CircleSwipeGestureChecker

  • 同名の新規スクリプトを作成して、コード全文貼っ付けてください。

GameManager等に混ぜ込んでも大丈夫です。
(シンプルな変数名にしているが、多分、被らないと思う)


using System.Collections;
using UnityEngine;

public class CircleSwipeGestureChecker : MonoBehaviour
{

	float angle;
	float angleSum;
	Vector3 swipePosition;
	Vector3 previousSwipePosition;
	Vector3 vector;
	Vector3 previousVector;
	int sign;
	int previousSign;


//スワイプ地点の角度の総和が、この値を超した場合は円を描いたと見なす。
	static readonly float AngleSumThreshold = 330.0f;

//前回からのタッチ地点の差が、この距離未満だと無視する(sqrMagnitudeなので、2乗。ワールド座標ではなく、スクリーン座標なので大きめで)。
	static readonly float SwipeDeltaSqrThreshold = 15.0f * 15.0f;

	Coroutine checkCircleSwipeGesture;
	Coroutine checkCircleSwipeGestureMouseButtonUp;
//スワイプジェスチャーのチェック間隔。
	static readonly WaitForSeconds CheckCircleSwipeGestureWait = new WaitForSeconds(0.05f);



//ゲーム冒頭等、ジェスチャーチェック受付開始時に、1回だけコレを呼ぶ。
	public void StartCheckCircleSwipeGesture()
	{
//一応、重複チェックしている。
		if (checkCircleSwipeGesture != null) {
			StopCoroutine(checkCircleSwipeGesture);
		}

		checkCircleSwipeGesture = StartCoroutine(CheckCircleSwipeGesture());


		if (checkCircleSwipeGestureMouseButtonUp != null) {
			StopCoroutine(checkCircleSwipeGestureMouseButtonUp);
		}

		checkCircleSwipeGestureMouseButtonUp = StartCoroutine(CheckCircleSwipeGestureMouseButtonUp());
	}

//停止時には、コレを呼ぶ。
	public void StopCheckCircleSwipeGesture()
	{
		if (checkCircleSwipeGesture != null) {
			StopCoroutine(checkCircleSwipeGesture);
			checkCircleSwipeGesture = null;
		}

		if (checkCircleSwipeGestureMouseButtonUp != null) {
			StopCoroutine(checkCircleSwipeGestureMouseButtonUp);
			checkCircleSwipeGestureMouseButtonUp = null;
		}
	}


//計算に使用する値の初期化。
	void ResetCheckCircleSwipeGesture()
	{
		previousSwipePosition = Input.mousePosition;
		angleSum = 0;

		vector = Vector3.zero;
		previousVector = Vector3.zero;

		sign = 0;
		previousSign = 0;
	}


	IEnumerator CheckCircleSwipeGesture()
	{
		ResetCheckCircleSwipeGesture();

		yield return CheckCircleSwipeGestureWait;


		while (true) {
			if (Input.GetMouseButton(0)) {

				swipePosition = Input.mousePosition;


				if ((swipePosition - previousSwipePosition).sqrMagnitude < SwipeDeltaSqrThreshold) {
					yield return CheckCircleSwipeGestureWait;
					continue;
				}
				

				angle = Vector3.Angle(previousVector, vector);

				if (vector != Vector3.zero)
					previousVector = vector;

				vector = swipePosition - previousSwipePosition;

				previousSwipePosition = swipePosition;  



				if (previousVector != Vector3.zero && previousSign == 0)
					previousSign = Vector3.Cross(previousVector, vector).z < 0 ? 1 : -1;


				if (previousVector != Vector3.zero) {
					sign = Vector3.Cross(previousVector, vector).z < 0 ? 1 : -1;


//逆回転になっているので、角度の総和をリセット。
					if (previousSign != sign) {
						previousSign = sign;
						angleSum = 0;
					} else {
						angleSum += angle;
					}
				} else {
					angleSum += angle;
				}


				if (AngleSumThreshold <= angleSum) {



//円を描いたと判定された時の処理をココに記述。
//previousSignの値を見ると、時計回りか反時計回りか分かる(-1 == 反時計回り、1 == 時計回り)。



					ResetCheckCircleSwipeGesture();
				}

			}

			yield return CheckCircleSwipeGestureWait;
		}
	}


//スワイプが中断された判定は、毎フレームしないといけないので、別コルーチンに分ける。
	IEnumerator CheckCircleSwipeGestureMouseButtonUp()
	{
		while (true) {
			if (Input.GetMouseButtonUp(0)) {
				ResetCheckCircleSwipeGesture();
			}

			yield return null;
		}
	}

}

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