「今、プレイヤーが見ているものは何か?」「マウスでクリックした先に何があるか?」「キャラクターは地面に立っているか?」——ゲーム開発では、こうした「空間への問いかけ」が頻繁に必要になります。
これに答えてくれるのが、Unityの物理エンジンが提供する Physics.Raycast です。ゲーム空間のある地点から、ある方向に向かって 目に見えない光線(Ray) を飛ばし、その光線が Collider(コライダー) を持つオブジェクトに当たったかどうか、当たったなら「何に・どこで・どれくらいの距離で」当たったかを教えてくれます。
この記事では、Physics.Raycastの基本的な使い方から、クリック選択・接地判定という2大定番レシピ、そして初心者が最も躓きやすいレイヤーマスクの活用法までを、実践的なC#コードを交えて解説します。
この記事でわかること
- Ray(始点+方向)と
RaycastHit(衝突情報)の基本概念- もっともシンプルなRaycastの書き方と
Debug.DrawRayでの可視化- 定番レシピ2つ: マウスクリックでオブジェクト選択・接地判定
- つまずきの元凶「レイヤーマスク」の指定方法と反転イディオム
- 敵・壁・空振りを1本のRayで撃ち分ける射撃の完成例
Raycastの基本概念:光線と衝突情報
Physics.Raycastは、文字通り「光線(Ray)」を投射して、その光線がColliderを持つオブジェクトと交差するかどうかを調べます。登場人物は2つだけです。

Ray(光線)とは
Rayは、「始点(Origin)」 と 「方向(Direction)」 の2つの要素で定義されます。「カメラの位置から、カメラの前方へ」「キャラクターの足元から、真下へ」のように、どこから・どちらへを決めれば1本のRayになります。
RaycastHit(衝突情報)とは
Rayがオブジェクトに衝突した場合、その詳細がRaycastHitという構造体に格納されます。主に使うのは次の情報です。
hit.collider: 衝突した相手のCollider(そこからgameObjectや他のコンポーネントへアクセス)hit.point: 衝突したワールド座標(着弾エフェクトを出す位置など)hit.normal: 衝突した面の法線ベクトル(弾痕を面に沿って向けるなど)hit.distance: 始点から衝突点までの距離
最もシンプルなRaycastの実装
最も基本的な使い方は、カメラの中心から前方にRayを飛ばし、何かに当たったか どうかを判定するものです。FPSの「照準が合っているものを調べる」がまさにこれです。
using UnityEngine;
public class SimpleRaycaster : MonoBehaviour
{
void Update()
{
// 始点: メインカメラの位置
Vector3 origin = Camera.main.transform.position;
// 方向: メインカメラの前方
Vector3 direction = Camera.main.transform.forward;
// 最大距離: 100メートル
float maxDistance = 100f;
// Raycastを実行し、何かに当たったかどうかを判定
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance))
{
Debug.Log("オブジェクトに衝突しました: " + hit.collider.gameObject.name);
}
// シーンビューにRayの軌跡を描画(デバッグ用)
Debug.DrawRay(origin, direction * maxDistance, Color.red);
}
}
Physics.Raycastはブール値を返します。trueは衝突があったことを意味し、その際にout hitで指定した変数に衝突情報が書き込まれます。
tips: 「Raycastが当たらない」バグの調査は、まず
Debug.DrawRayでRayを見える化するのが最短ルートです。向きが逆だった・長さが足りなかった・そもそも別の場所から飛んでいた——のような原因が一目で分かります。エディタでの可視化全般は Gizmos入門 を参照してください。
定番レシピ1:マウスクリックでオブジェクトを選択する
Raycastの実用で最も出番が多いのが「クリックしたものを取得する」です。ポイントは、マウスのスクリーン座標から3D空間へのRayをScreenPointToRayで作ることです。画面は2D・世界は3Dなので、「画面上のこの点の奥に伸びる線」に変換してもらうイメージです。

using UnityEngine;
public class ClickSelector : MonoBehaviour
{
void Update()
{
// 左クリックの瞬間だけ判定
if (Input.GetMouseButtonDown(0))
{
// マウスのスクリーン座標から、3D空間へ伸びるRayを作る
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, 100f))
{
Debug.Log("クリックしたオブジェクト: " + hit.collider.gameObject.name);
// hit.point にエフェクトを出す、選択状態にする、などの処理
}
}
}
}
RTSのユニット選択、パズルのピースをつまむ、3D画面のボタンを押す——応用範囲は無限大です。マウス入力の基本は 入力処理の記事 で整理しています。
定番レシピ2:接地判定
「ジャンプは地面にいるときだけ」を実現する接地判定も、Raycastの定番です。キャラクターの足元から真下に短いRayを飛ばし、地面レイヤーに当たるかどうかで判定します。

using UnityEngine;
public class GroundChecker : MonoBehaviour
{
[SerializeField] private LayerMask groundLayer; // 地面のレイヤーだけを対象に
[SerializeField] private float checkDistance = 0.2f;
public bool IsGrounded()
{
// 足元の少し上から、真下に短いRayを飛ばす
Vector3 origin = transform.position + Vector3.up * 0.1f;
return Physics.Raycast(origin, Vector3.down, checkDistance + 0.1f, groundLayer);
}
void Update()
{
if (IsGrounded() && Input.GetKeyDown(KeyCode.Space))
{
Debug.Log("ジャンプ!");
}
}
}
ここでgroundLayerを指定しているのがポイントです。指定しないと自分自身のColliderにRayが当たって「常に接地している」ことになる——これが次のセクションの主役、レイヤーマスクの話です。
初心者が躓きやすいポイント:レイヤーマスクの活用
Raycastを使用する際、初心者が最も混乱しやすいのが 「レイヤーマスク(LayerMask)」 の扱いです。
問題点:意図しないオブジェクトへの衝突
デフォルトのPhysics.Raycastは、シーン内のすべてのColliderを対象とします。プレイヤー自身、透明なトリガー、エフェクト——対象にしたくないものにRayが当たると、「銃弾が自分に当たる」「接地判定が常にtrue」のような意図しない動作を引き起こします。
解決策:LayerMaskで対象を絞り込む
Physics.Raycastには、オプションの引数としてlayerMaskを指定できます。Rayに「このレイヤーのものにだけ当たってね」というフィルタを付けるイメージです。

using UnityEngine;
public class LayerMaskRaycaster : MonoBehaviour
{
// Inspectorから設定できるようにSerializeFieldで定義
[SerializeField]
private LayerMask targetLayer;
void Update()
{
Vector3 origin = Camera.main.transform.position;
Vector3 direction = Camera.main.transform.forward;
float maxDistance = 100f;
// 最後の引数にLayerMaskを指定
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, targetLayer))
{
Debug.Log("ターゲットレイヤーのオブジェクトに衝突: " + hit.collider.gameObject.name);
}
}
}
LayerMask型の変数を[SerializeField]にしておけば、対象レイヤーをInspectorのドロップダウンで選ぶだけで済みます。コードにレイヤー名を書き込まずに済むため、まずはこの形を基本にしましょう(レイヤーの仕組み自体は レイヤーとタグの記事 で解説しています)。
ゲームでの実用例:ジャンル別のレイヤー設計
実際のゲームでは、レイヤーマスクは次のように働きます。自分のジャンルに置き換えてみてください。

- FPS・TPSの射撃: 弾のRayは
EnemyとWallだけに当てます。味方・回復アイテム・演出用のパーティクルが射線上にいても素通りさせないと、「見えない何かに弾が吸われる」ゲームになります - クリック移動(RPG・RTS): 移動先の指定は
Groundレイヤーだけに当てます。敵やNPCの頭上をクリックしても、Rayはキャラクターを素通りしてその足元の地面に当たるため、正しい移動先が取れます - 敵AIの視線判定(ステルスゲーム): 敵か らプレイヤーへRayを飛ばし、
Wallレイヤーを含めて判定します。Rayが先に壁に当たれば「障害物で見えていない」、プレイヤーに届けば「発見」——隠密の成立条件そのものです
どの例も共通するのは、「Rayに当たってほしいもの」はゲームのルールが決めるということです。レイヤー設計は当たり判定のルール設計そのものだと考えると、迷いにくくなります。
特定のレイヤーを無視する方法
逆に、「特定のレイヤー(例:プレイヤー自身や透明な壁など)以外のすべて」を対象としたい場合もあります。この場合、ビット演算子を使ってレイヤーマスクを反転させます。
例えば、「Ignore Raycast」レイヤーを無視したい場合は、以下のコードを使用します。
using UnityEngine;
public class IgnoreLayerRaycaster : MonoBehaviour
{
void Update()
{
// 無視したいレイヤーの番号を取得
int ignoreLayer = LayerMask.NameToLayer("Ignore Raycast");
// 「そのレイヤーのビットだけを立てて、全体を反転」=それ以外のすべて
int layerMask = ~(1 << ignoreLayer);
Vector3 origin = transform.position;
Vector3 direction = transform.forward;
float maxDistance = 10f;
// 反転させたLayerMaskを適用
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, layerMask))
{
Debug.DrawRay(origin, direction * hit.distance, Color.green);
Debug.Log("無視レイヤー以外のオブジェクトに衝突: " + hit.collider.gameObject.name);
}
else
{
Debug.DrawRay(origin, direction * maxDistance, Color.white);
}
}
}
この~(1 << ignoreLayer)という記述は、UnityのLayerMaskを扱う上で非常に頻繁に使用されるイディオム(慣用的な書き方)ですので、ぜひ覚えておきましょう。
実践:1本のRayで「敵・壁・空振り」を撃ち分ける
仕上げに、ここまでの部品——RaycastHitの中身、レイヤーマスク、可視化——を1本の射撃にまとめます。FPS・TPSの銃はもちろん、ソウルライクの遠距離攻撃でも、SFのレーザーポインタでも、中身はこの形です。目標は「敵に当たればHPが減って火花、壁なら弾痕、何もなければ何も起きない」を1本のRayで分岐させることです。

using UnityEngine;
public class HitscanShooter : MonoBehaviour
{
[SerializeField] private LayerMask hitLayers; // EnemyとWallだけを入れる
[SerializeField] private GameObject enemyHitEffect; // 火花・ヒットマークなど
[SerializeField] private GameObject wallHitEffect; // 弾痕・土煙など
[SerializeField] private float range = 50f;
[SerializeField] private int damage = 10;
void Update()
{
if (Input.GetMouseButtonDown(0)) Shoot();
}
void Shoot()
{
// 画面中央(照準)からまっすぐ前へ1本
Ray ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward);
// 空振り: 何にも当たらなければ、何もしないで終わり
if (!Physics.Raycast(ray, out RaycastHit hit, range, hitLayers,
QueryTriggerInteraction.Ignore))
{
return;
}
if (hit.collider.TryGetComponent(out EnemyHealth enemy))
{
// 敵: HPを減らして、敵用エフェクト
enemy.TakeDamage(damage);
Instantiate(enemyHitEffect, hit.point, Quaternion.LookRotation(hit.normal));
}
else
{
// 壁: 弾痕を「面の向き」に沿わせて置く
Instantiate(wallHitEffect, hit.point, Quaternion.LookRotation(hit.normal));
}
}
}
見どころは3つあります。
- 分岐は
TryGetComponent1回: 当たった相手がEnemyHealthを持っていれば敵、持っていなければ壁。「敵か壁か」をタグ文字列の比較でなくコンポーネントの有無で判定するので、敵の種類が増えても書き換え不要です hit.normalの回収: 冒頭で紹介した法線ベクトルがここで活躍します。Quaternion.LookRotation(hit.normal)で、弾痕が斜めの壁でも面にぴたりと沿って貼り付きますQueryTriggerInteraction.Ignore: 回復エリアのような透明トリガーを弾が素通りします。これを外すと「見えない何かに弾が吸われる」不思議バグの完成です
わざと壊して、直す
この射撃は、Raycastの定番事故をまとめて体験できる教材でもあります。
hitLayersからEnemyを外す: 弾が敵を貫通して、背後の壁にだけ弾痕が付きます。「当たらない」バグの最有力容疑者は、いつでもレイヤーマスクです- 始点をカメラでなく銃口にする: 銃口はプレイヤーのColliderのすぐそば(または内部)なので、マスクの設定次第で自分に当たります。FPSで「照準はカメラ中心・演出は銃口から」と2本に分けるのはこれが理由です
rangeを5fに縮める: 遠くの敵に当たらなくなります。Debug.DrawRay(ray.origin, ray.direction * range, Color.red, 0.5f)を1行足してRayを目で見れば、「短すぎた」が一瞬で分かります
Playして、敵を撃てばHPが減って火花が散り、壁なら弾痕が面に沿って付き、空を 撃てば何も起きない——1本のRayで3つの結果が正しく分かれたら完成です。この分岐の型は、視線判定にもアイテム調べにもそのまま流用できます。
おまけ:先に知っておくと良いこと
Raycastを使いこなせるようになったら、次はこの兄弟たちが視野に入ってきます。
- 全部欲しいなら
RaycastAll:Physics.Raycastは最初の1つにしか当たりません。貫通する弾や「壁越しに敵が何体いるか」には、射線上のすべての衝突を配列で返すPhysics.RaycastAllを使います。 - 太さが欲しいなら
SphereCast: Rayは太さゼロの線なので、細い隙間をすり抜けます。「球を転がしながら進める」Physics.SphereCastにすると、キャラクターの幅を考慮した接地判定や当たり判定になります。 - トリガーに当てたくないとき: 引数の
QueryTriggerInteraction.Ignoreを指定すると、Is TriggerのColliderを無視できます(デフォルトはプロジェクト設定の「Queries Hit Triggers」に従います)。 - 2Dゲームでは
Physics2D.Raycast: 2DのColliderには2D専用APIを使います。戻り値がRaycastHit2Dを直接返すなど形も違うため、Rigidbody2DとCollider2Dの記事 とあわせて確認してください。
まとめ
Physics.Raycastは、Unityでのインタラクション実装の基盤となる技術です。
- Raycastは「光線」による当たり判定:始点と方向を持つRayを飛ばし、Colliderとの衝突を判定する。
- 衝突情報 は
RaycastHitに格納される:相手(collider)・位置(point)・法線(normal)・距離(distance)が取れる。 - クリック選択は
ScreenPointToRay:マウスのスクリーン座標を3D空間のRayに変換する定番パターン。 - 接地判定は「足元から真下に短いRay」+地面レイヤー指定。
- LayerMaskで対象を絞り込む:指定しないと自分自身にも当たる。無視したい場合は
~(1 << layer)のビット反転イディオム。 Debug.DrawRayで可視化:「当たらない」バグは、まずRayを目で見るのが最短ルート。- 分岐は
TryGetComponent:当たった相手のコンポーネントの有無で処理を分け、hit.pointとhit.normalでエフェクトを置く。
これらの知識とコード例を活用することで、オブジェクト操作や視線判定の精度は飛躍的に向上するはずです。