Unityの「Raycast」を使ってゲーム内オブジェクトとの衝突判定を実装する方法

ゲーム開発

Raycastとは何か

ゲームの開発において、目の前にあるオブジェクトとの当たり判定をどのように調べればいいのか、悩むことはありませんか。

Raycast は、そのようなシーンで活躍する仕組みです。

線上にまっすぐ伸びる疑似的な「光線」を飛ばし、その先に何があるのかを検知するために使われます。 3D空間や2D空間のいずれでも利用できるので、幅広い場面で使われています。

たとえば、3Dゲーム内でプレイヤーが銃を発射する動作を考えてみてください。 弾道を物理的にシミュレーションするのは大変ですが、Raycastを使えば、瞬時に「当たった先」を見つけ出すことができます。 スナイパーライフルの弾丸が当たる相手の位置を計算する、といった場面で多用されます。

また、物体の表面を触っているかどうかを判定する場合にも使われます。 壁をよじ登るアクションや、床に着地しているかどうかを判別するロジックに組み込まれることが多いです。 一方で、Raycastを実行するときは、何を「狙いたい」のかを明確にしないと結果が望まない形になるケースもあるでしょう。

そういう理由で、「何を検知したいか」 を事前にイメージすることが大切です。 目的がハッキリしていれば、Raycastで取得するオブジェクトや情報が明確になり、コードや設計がスッキリしてきます。

Raycastが担う役割

ここまででわかるように、Raycastはゲーム内の衝突判定をシンプルに実装する手段です。 Physicsエンジンを直接大がかりに使う前に、Raycastという射線判定の仕組みを理解すると、Unityでの開発がしやすくなります。 たとえば、クリックした先のオブジェクトを特定する処理にも活かせるので、マウス操作がメインのシステムにも役立ちます。

Raycastの基本的な使い方

Raycastを実行するためには、Physics.Raycast というメソッドを呼び出します。 これはUnityの標準的なPhysics機能の一部です。 空間上に「Ray」を発射し、何かしらのColliderに衝突した場合、その結果をRaycastHitという構造体で受け取ります。

簡単な例として、カメラから画面中央に向けてRayを飛ばす場合を考えてみてください。

Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
RaycastHit hit;

if (Physics.Raycast(ray, out hit, 100f))
{
    Debug.Log("Hit object: " + hit.collider.name);
}

このように書くことで、画面中央の位置から奥に向けて光線を飛ばし、何かに当たればオブジェクト名をログに表示します。 第二引数の out hit は衝突先の情報を受け取るためのものです。 第三引数の 100f はRayを飛ばす最大距離を示します。

カメラではなく、プレイヤーやその他のオブジェクトからRayを飛ばすことももちろん可能です。 また、Physics.Raycast にはオーバーロードが存在しており、レイヤーマスクや判定モードなどを細かく設定できます。 たとえば特定のレイヤーだけを検知したい場合は、レイヤーマスクのパラメータを加えると便利です。

2Dの場合

2DプロジェクトでRaycastを使う場合には Physics2D.Raycast を使用します。 やり方は3Dとほぼ同じですが、返り値が RaycastHit2D となり、衝突情報を保持する構造が異なります。 2D空間での射線判定は、横スクロールゲームなどで地面とプレイヤーの接触を確認するのに応用できます。

実務での活用シーンをイメージする

Raycast は衝突を検知するだけでなく、使い方次第でいろいろな演出も実現できます。 たとえば、アクションゲームではキャラクターが壁に張り付く動作をするとき、Raycastで壁との接触面を検知して動作を制御することがあります。 また、リアルタイム戦略ゲームなどでユニット同士が距離を取り合うシステムでも、Raycastを組み合わせるケースが見られます。

他にも、カメラワークの自動調整に用いることができます。 カメラとプレイヤーの間に障害物がある場合、その障害物の場所をRaycastで判定し、視覚的な邪魔にならないようカメラ位置を調整する手法が一般的です。 プレイヤーが壁の裏側に行ってしまった場合でも、Raycastの結果をもとにカメラの位置を動かすことで視界を確保しやすくなります。

3Dシューティング などでは、着弾エフェクトを発生させる場所を決めるためにもRaycastを使います。 プレイヤーが弾を撃った瞬間にRaycastを飛ばし、衝突地点を取得してそこに爆発やスパークのエフェクトを生成すると、演出がスムーズになります。 このとき、衝突先の地形が斜面だった場合には、エフェクトをその面に対して回転させるなどの工夫が必要です。

こうした実務に近い場面をイメージすると、Raycastが汎用性の高い仕組みだと感じませんか。 当たり判定という基本機能だけでなく、演出やカメラ調整、UI操作の検知などにも使うことができるのです。

安定した実装のポイント

実際の開発では、フレームごとの処理負荷にも注意することが大切です。 Raycastを多数飛ばすと、物理計算の処理が増える場合があります。 何度も連続して判定する必要があるなら、射線をまとめて飛ばす方法や、一定フレームごとに処理する方法などを検討するといいかもしれません。

具体的なサンプルコード

ここでは、プレイヤーの視界にあるオブジェクトを検出する簡単なサンプルを見てみます。 プレイヤーの正面方向にRayを飛ばし、一定範囲内にあるオブジェクトの情報をログに出す例です。

using UnityEngine;

public class PlayerRaycast : MonoBehaviour
{
    public float rayDistance = 5f;

    void Update()
    {
        // プレイヤーの位置と向きをもとにRayを作成
        Ray forwardRay = new Ray(transform.position, transform.forward);
        RaycastHit hitInfo;

        // Rayを飛ばして衝突判定
        if (Physics.Raycast(forwardRay, out hitInfo, rayDistance))
        {
            // 当たったオブジェクトの名前を表示
            Debug.Log("Detected: " + hitInfo.collider.gameObject.name);
        }
    }
}

このスクリプトをPlayerオブジェクトにアタッチしておけば、プレイヤーが向いている方向にいるオブジェクトが検知された場合に、その名前がコンソールに表示されます。 rayDistance にはRayを飛ばす最大距離を指定しています。

もし衝突判定の精度を変えたい場合は、 Physics.RaycastNonAlloc などを活用し、複数ヒットの結果を取得して順番に処理する方法もあります。 特定のタグやレイヤーを持つオブジェクトだけを検知したいなら、レイヤーマスク機能を組み合わせると柔軟な制御ができます。

衝突判定の結果をどのタイミングで利用するかを考えておくと、余計なバグを防ぎやすくなります。 特にUpdate内でRaycastを実行する際は、ゲームの進行状況によっては呼び出し頻度が多くなりすぎる可能性があります。

Raycastの応用テクニック

Raycastは使い方次第で、衝突判定以外にも役立つことが多いです。 たとえば、ScreenPointToRayViewportPointToRay を用いれば、マウスクリックがどのオブジェクトに当たったかを取得することも可能です。 UI要素ではなく3D空間の選択を行うときに、こうした仕組みが使われます。

キャラクター操作への応用

キャラクターの足元にRayを飛ばして地面との距離を測定することで、高低差のある場所での移動やジャンプの開始判定を正確に行うことができます。 たとえば、崖がある場所では、Raycastの結果をみて足元が途切れているかどうかを検査し、安全な足場を見つけられなければ落下モーションに切り替える仕組みを作ることも可能です。

レイヤーマスクでのフィルタリング

Unityではオブジェクトに対してレイヤーを設定し、衝突判定や描画などの対象を細かく分けることができます。 Raycastでも同じように、指定したレイヤーにのみヒット判定を行うオプションが存在します。 これを活用すれば、不要なオブジェクトに対するRaycast判定を避け、処理負荷を下げたり、想定外の衝突を排除できたりします。

int layerMask = 1 << LayerMask.NameToLayer("Enemy");
if (Physics.Raycast(transform.position, transform.forward, out hitInfo, rayDistance, layerMask))
{
    Debug.Log("Hit Enemy: " + hitInfo.collider.name);
}

このように書くと、Enemyレイヤーを持つオブジェクトだけが検知対象になります。 メインカメラとキャラクターなど、ゲームの主要要素が混在するシーンでも、的確な衝突判定を行いやすくなるでしょう。

Raycast結果をもとに地形の傾斜角などを取得することで、キャラクターの足裏を斜面に合わせるといった表現にも発展できます。

Raycastのトラブルシューティング

Raycastを使っていると、思わぬところでうまく検知できなかったり、逆に検知しすぎてしまう場合があります。 ここでは、よくあるトラブルとその原因をいくつか紹介します。

ヒットしない場合

  • Colliderがついていないオブジェクトを狙っている
  • Rayの向きや原点が想定外の場所を向いている
  • Rayの最大距離が足りず、衝突する前に判定が切れてしまっている

多くの場合、Colliderの設定やレイヤー設定に何らかの見落としがあります。 特定のオブジェクトだけが検知されないなら、レイヤーマスクやColliderの有無を重点的に確認してみるとよいでしょう。

余計なものがヒットする場合

  • Rayを飛ばすレイヤー設定が広すぎる
  • Rayが2DオブジェクトやUIなど、別の空間を拾っている
  • 判定を必要以上に連続して行っている

このような状況では、レイヤーマスクによるフィルタリングや、Raycastの呼び出しを最適化することが効果的です。 また、カメラの位置やプレイヤーの位置が何かを貫通してしまっているケースもあるため、シーンをワイヤーフレーム表示にして確認するのがおすすめです。

ヒット情報の扱い

RaycastHitなどの衝突情報は、どのオブジェクトにどの部分で当たったかを示す便利なデータを含んでいます。 しかし、取得した衝突座標や法線ベクトルを誤って扱うと、意図しない挙動が起こる可能性があります。 たとえば、エフェクトを生成するときに衝突面の角度を無視すると、地面に垂直な爆発エフェクトが現れるなどの違和感が出やすいです。

解決アプローチ

衝突座標 (hit.point) や法線ベクトル (hit.normal) を必要に応じて活用し、ローカルの回転や位置決めに反映することがポイントです。 地面に対して垂直に表示したいなら、法線方向を参照してゲームオブジェクトを回転させるなど、臨機応変に対応する必要があります。

まとめ

Raycast は、Unityでのゲーム開発において欠かせない機能です。 まっすぐに線を飛ばして、どのオブジェクトに当たったかを瞬時に判断できるため、幅広いシーンで活用されています。 実務の現場でも、キャラクターの移動やエフェクトの配置、カメラの制御など、さまざまな場所で使われています。

Raycastを使うときは、Colliderやレイヤーの設定を正しく行うことが大切です。 また、コードの呼び出し頻度やパフォーマンスを管理しつつ、必要に応じてPhysics.RaycastNonAllocなどのメソッドも検討することで、複数の衝突結果をまとめて扱えるようになります。 それによって、より正確で柔軟なゲームロジックを組み立てやすくなるでしょう。

Raycastは単に衝突判定を行うだけでなく、クリックした先の情報を取得したり、キャラクターを斜面に合わせたりといった高度な表現にも応用できます。 ゲームの仕組みをリアルに見せたいなら、Raycastで取得できるヒット情報をうまく利用してみてください。

衝突判定の基礎を抑えたら、次は実際に自分のプロジェクトでRaycastを使ってみるのがおすすめです。 手を動かして、どのようなタイミングでどの程度の処理が必要になるのかを確かめると、より理解が深まるはずです。

Unityをマスターしよう

この記事で学んだUnityの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。