「クリックした敵を選びたい」「マウスの位置にオブジェクトを置きたい」。マウスで操作するゲームでは、画面のどこをクリックしたかを、ゲームの世界の位置やオブジェクトに変換する必要があります。今回はマウスの位置とクリックの受け取り方と、クリックした先を調べるRaycastをご紹介します!
この記事で学べること
- Input Systemでマウスの位置とクリックを受け取る方法
- スクリーン座標とワールド座標の違い
- ScreenToWorldPointで、マウスの位置をゲームの世界の位置にする方法(2D)
- Raycastで、クリックした先にあるオブジェクトを調べる方法(3D・2D)
- レイヤーで、調べる相手を絞り込む方法
前提知識
- Input Systemで入力を受け取ろう
- 当たり判定とPhysics:ColliderとRigidbody:Raycastが当たるのはColliderです
マウスの位置とクリックを受け取る
マウスを直接調べる
using UnityEngine;
using UnityEngine.InputSystem;
public class MouseCheckExample : MonoBehaviour
{
void Update()
{
// マウスがつながっていないときはnullになる
if (Mouse.current == null)
{
return;
}
if (Mouse.current.leftButton.wasPressedThisFrame)
{
Vector2 mousePosition = Mouse.current.position.ReadValue();
Debug.Log("クリックした位置: " + mousePosition);
}
}
}
using UnityEngine.InputSystem;が必要ですMouse.current.position.ReadValue():マウスの位置(画面の左下が(0, 0)、単位はピクセル)Mouse.current.leftButton.wasPressedThisFrame:左クリックした瞬間(右クリックはrightButton)
InputSystem_Actionsを使う場合
6-7の記事のInputSystem_Actionsでは、Attackアクションにマウスの左クリックが割り当てられています。ゲームパッドのボタンでも同じ操作にしたい場合は、こちらを使います。
if (inputActions.Player.Attack.WasPressedThisFrame())
{
// 左クリック(またはゲームパッドの攻撃ボタン)
}
マウスの位置は、どちらの場合もMouse.current.position.ReadValue()で読むのが簡単です。
スクリーン座標とワールド座標
マウスの位置はスクリーン座標(画面のピクセルの位置)です。ゲームの世界の位置(ワールド座標)とは別物なので、変換が必要です。
| 座標 | 基準 | 例 |
|---|---|---|
| スクリーン座標 | 画面の左下が(0, 0)、単位はピクセル | マウスの位置 |
| ワールド座標 | シーンの原点(0, 0, 0)、単位はUnityの1マス | transform.position |
画面の大きさを変えると、同じ場所をクリックしてもスクリーン座標は変わります。
2D:クリックした位置にオブジェクトを動かす
2Dゲーム(カメラがOrthographic)では、Camera.main.ScreenToWorldPoint()でワールド座標に変換できます。
using UnityEngine;
using UnityEngine.InputSystem;
public class MouseClickMover2D : MonoBehaviour
{
void Update()
{
if (Mouse.current == null || !Mouse.current.leftButton.wasPressedThisFrame)
{
return;
}
Vector3 screenPosition = Mouse.current.position.ReadValue();
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(screenPosition);
// カメラのZ位置(-10など)が入ってしまうので、0にそろえる
worldPosition.z = 0f;
transform.position = worldPosition;
}
}
Camera.main:タグが「MainCamera」のカメラ(最初からあるMain Camera)- zを0に戻すのを忘れない:変換した位置のzはカメラと同じ位置になるので、そのままだとカメラに重なって見えなくなります
Raycast:クリックした先のオブジェクトを調べる
3Dゲームでは、画面の点がゲームの世界のどこに当たるかは、奥行きによって変わります。そこで、カメラからマウスの方向にまっすぐ線(レイ)を飛ばして、最初に当たったColliderを調べます。これがRaycastです。
using UnityEngine;
using UnityEngine.InputSystem;
public class MouseRaycastPicker : MonoBehaviour
{
[SerializeField] private float maxDistance = 100f;
void Update()
{
if (Mouse.current == null || !Mouse.current.leftButton.wasPressedThisFrame)
{
return;
}
// カメラからマウスの位置に向かうレイを作る
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
// レイが何かのColliderに当たったら、その情報がhitに入る
if (Physics.Raycast(ray, out RaycastHit hit, maxDistance))
{
Debug.Log(hit.collider.gameObject.name + "をクリックした");
Debug.Log("当たった場所: " + hit.point);
}
}
}
空のゲームオブジェクトにアタッチして、シーンにCubeやSphereを置いて再生し、クリックしてみましょう。
Camera.main.ScreenPointToRay(マウスの位置):カメラからマウスの方向へ向かうレイPhysics.Raycast(レイ, out RaycastHit hit, 距離):当たればtrue。当たった情報がhitに入るhit.collider:当たったCollider(.gameObjectでオブジェクト)hit.point:当たった場所(ワールド座標)hit.distance:カメラからの距離
- Colliderが付いていないオブジェクトには当たりません
レイを見えるようにする
Debug.DrawRay(ray.origin, ray.direction * maxDistance, Color.red, 1f);
Raycastの前にこの1行を足すと、シーンビューに赤い線が1秒間表示されます。「どこに向かってレイを飛ばしているか」を確認するのに便利です。
2Dの場合
2DのCollider(Box Collider 2Dなど)には、Physics.Raycastは当たりません。2Dでは次のように書きます。
Vector2 worldPoint = Camera.main.ScreenToWorldPoint(Mouse.current.position.ReadValue());
// その位置に2DのColliderがあるか調べる
Collider2D hitCollider = Physics2D.OverlapPoint(worldPoint);
if (hitCollider != null)
{
Debug.Log(hitCollider.gameObject.name + "をクリックした");
}
- クリックした点に何があるかを調べるなら
Physics2D.OverlapPointが簡単です - 「ある点から、ある方向に線を飛ばす」ときは
Physics2D.Raycast(開始位置, 方向, 距離)を使います
レイヤーで調べる相手を絞り込む
床と敵が重なっているとき、敵だけに当てたい、ということがあります。そんなときはレイヤーで絞り込みます。
レイヤーを作る
- オブジェクトを選び、Inspector右上のLayerのプルダウンを開く
- 一番下のAdd Layer…を選ぶ
- 空いている欄(User Layer 6以降)に名前(例:Enemy)を入力
- もう一度オブジェクトを選び、Layerのプルダウンから作ったレイヤーを選ぶ
タグ(6-10の記事)は「何のオブジェクトか」の目印、レイヤーは「Raycastや当たり判定、カメラで、まとめて扱うグループ」と考えると分かりやすいです。
LayerMaskで絞り込む
using UnityEngine;
using UnityEngine.InputSystem;
public class MouseLayerPicker : MonoBehaviour
{
[SerializeField] private LayerMask targetLayers; // Inspectorで当てたいレイヤーを選ぶ
[SerializeField] private float maxDistance = 100f;
void Update()
{
if (Mouse.current == null || !Mouse.current.leftButton.wasPressedThisFrame)
{
return;
}
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
if (Physics.Raycast(ray, out RaycastHit hit, maxDistance, targetLayers))
{
Debug.Log("ねらったレイヤーの " + hit.collider.gameObject.name + " に当たった");
}
}
}
LayerMask型の変数を[SerializeField]にすると、Inspectorでチェックボックス付きのプルダウンになり、当てたいレイヤーを選べます- 選んだレイヤー以外のオブジェクトは、レイが通り抜けます
スライム床をクリックすると、奥の敵じゃなくて床が選ばれちゃう・・・



そんなときこそLayerMask!敵にEnemyレイヤーを付けて、Raycastで絞り込めばOKだよ
UIの上をクリックしたとき
Raycastは、ボタンなどのUI(Canvas)には当たりません。UIのボタンを押したつもりが、後ろのオブジェクトまでクリックされてしまうことがあります。気になる場合は、UIの上にマウスがあるかどうかをEventSystem.current.IsPointerOverGameObject()(using UnityEngine.EventSystems;が必要)で調べて、UIの上ならRaycastしないようにします。
よくあるエラーと対処法
エラー:NullReferenceException(Camera.mainの行)
- 原因:シーンのカメラのTagが「MainCamera」になっていない、またはカメラがない
- 対処法:カメラのInspectorでTagをMainCameraにする
エラー:クリックしても何も当たらない
- 原因:クリックしたいオブジェクトにColliderが付いていない。2DのColliderなのに
Physics.Raycastを使っている。LayerMaskで何も選んでいない - 対処法:Colliderを付ける。2Dは
Physics2Dを使う。Debug.DrawRayでレイの向きを確認する
エラー:2Dでクリックした位置にオブジェクトが見えない
- 原因:ScreenToWorldPointで変換した位置のzが、カメラと同じ位置になっている
- 対処法:
worldPosition.z = 0f;にする
エラー:The name ‘Mouse’ does not exist in the current context
- 原因:
using UnityEngine.InputSystem;を書いていない - 対処法:スクリプトの一番上に足す
まとめ
- マウスの位置は
Mouse.current.position.ReadValue()、クリックはleftButton.wasPressedThisFrame(またはInputSystem_ActionsのAttack) - マウスの位置はスクリーン座標。2Dは
ScreenToWorldPointで変換してzを0に - 3Dでクリックした先を調べるのは
ScreenPointToRay+Physics.Raycast - 2Dは
Physics2D.OverlapPointやPhysics2D.Raycast - 調べる相手はレイヤーと
LayerMaskで絞り込む
次のステップ
プレハブを使ってクリックした場所にオブジェクトを出したい場合は、第7章のスクリプトからプレハブを生成しようと組み合わせてみてください。
コメント