【Unity/3D】最適化済みタッチ判定&UI上でのタッチ無視

最適化済みタッチ判定

タッチした位置へRayを飛ばして対象の3Dオブジェクトを取得(UI上は無視)するコード。
マウスクリック or シングルタッチのみ対応。RaycastNonAllocを使用して最適化済み。

キャラクターの選択や、地面をタッチで移動等の処理に使えます。

サンプル動画

要らない気もするが、一応のサンプル動画。

UI側の設定

  • 各UI要素は、デフォルト設定でRaycastとの判定が有効になっているので注意。
    (ボタン以外のUIでも)
  • 必要ない場合、UIとRaycastとの判定を無効化するのは、アプリの最適化にも良いらしい。
  • ボタンに付いているImageコンポーネントの判定は無効化しない事。

特定のCanvas下のUIを全て「タッチ無視」

  • CanvasにアタッチされたGraphic Raycasterコンポーネントを無効化。

特定のImageを「タッチ無視」

  • ImageコンポーネントのRaycast Targetを無効化。

特定のTextMeshProを「タッチ無視」

  1. TextMeshProUGUIコンポーネントのExtra Settingsを選択し、オプション項目を表示。
  2. Raycast Targetを無効化。

【コード】GameManager

一応、GameManagerとしましたが、適当に命名しても良いです。


//使わなかった為、一応コメントアウトしているので、使う時は戻してください。
//using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class GameManager : MonoBehaviour
{

//キャッシュしておく。
	Camera mainCamera;

//NonAlloc版では配列が割り当てられないので宣言時に生成しておく。結果を1つしか取得しなくても良いなら、1を指定。複数取得したい場合は任意の最大数を設定。
	RaycastHit[] checkTouchResults = new RaycastHit[1];

//【任意の値】カメラからタッチ判定を飛ばす最大距離。余り大きいと負荷になる。
	float checkTouchMaxDistance = 100.0f;

//選択対象のレイヤー。SerializeFieldを有効にしてインスペクターから設定しても良い。
//    [SerializeField]
	LayerMask targetLayer;

//(EventSystem.currentから使っても負荷は無いかもしれないが)一応キャッシュしておく。EventSystemを複数使う場合は都度入れ替える事。
	EventSystem eventSystem;

//タッチ位置。
	Vector3 touchPoint;

//カメラからタッチ位置へのRay。
	Ray checkTouchRay;

//キャッシュしておく。EventSystemを複数使う場合は都度入れ替える事。
	PointerEventData pointer = new PointerEventData(EventSystem.current);

//タッチ位置のUI要素が返されるList。再確保されないようにキャパシティは、やや多めに指定しておく。
	List<RaycastResult> isPointerOverUIResults = new List<RaycastResult>(10);



	void Awake()
	{
		mainCamera = Camera.main;
//対象のレイヤーを指定。複数を設定する場合は「1 << LayerMask.NameToLayer("Enemy") | 1 << LayerMask.NameToLayer("Ground")」みたいに「|」で区切る。
		targetLayer = 1 << LayerMask.NameToLayer("Enemy");
		eventSystem = EventSystem.current;
	}

	void Update()
	{
		CheckTouch();
	}

	void CheckTouch()
	{
		if (Input.GetMouseButtonDown(0)) {
			touchPoint = Input.mousePosition;
			checkTouchRay = mainCamera.ScreenPointToRay(touchPoint);


//タッチ位置がUI要素と被っていた場合は弾く。
			if (IsPointerOverUI())
				return;


			if (0 < Physics.RaycastNonAlloc(checkTouchRay, checkTouchResults, checkTouchMaxDistance, targetLayer)) {

//ここにタッチ時の処理処理を書く。

//衝突地点の座標はcheckTouchResults[0].pointで取得出来る。
//対象のTransformはcheckTouchResults[0].transformで取得出来るので、そこからGetComponent等する。
			}
		}
	}

	bool IsPointerOverUI()
	{
		pointer.position = touchPoint;
		eventSystem.RaycastAll(pointer, isPointerOverUIResults);
		return (0 < isPointerOverUIResults.Count);
	}
}

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