タッチした位置へRayを飛ばして対象の3Dオブジェクトを取得(UI上は無視)するコード。
マウスクリック or シングルタッチのみ対応。RaycastNonAllocを使用して最適化済み。
キャラクターの選択や、地面をタッチで移動等の処理に使えます。
サンプル動画
要らない気もするが、一応のサンプル動画。
UI側の設定
- 各UI要素は、デフォルト設定でRaycastとの判定が有効になっているので注意。
(ボタン以外のUIでも) - 必要ない場合、UIとRaycastとの判定を無効化するのは、アプリの最適化にも良いらしい。
- ボタンに付いているImageコンポーネントの判定は無効化しない事。
特定のCanvas下のUIを全て「タッチ無視」
- CanvasにアタッチされたGraphic Raycasterコンポーネントを無効化。
特定のImageを「タッチ無視」
- ImageコンポーネントのRaycast Targetを無効化。
特定のTextMeshProを「タッチ無視」
- TextMeshProUGUIコンポーネントのExtra Settingsを選択し、オプション項目を表示。
- 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);
}
}