【Unity】カプセルキャストで太さのあるレイキャストを実現する

太いレーザービームのような当たり判定を実装したいけど、普通のレイキャストだとこれができないの…😥

CapsuleCast(カプセルキャスト)を使えば良いわ。

レイキャストは、太さのない線とコライダーとの当たり判定となりますが、CapsuleCast(カプセルキャスト)を使えば、太さのあるレイとコライダーとの当たり判定を行うことができます。

// CapsuleCastによる当たり判定
var isHit = Physics.CapsuleCast(
    center + new Vector3(0, 0.5f, 0), // 始点
    center + new Vector3(0, -0.5f, 0), // 終点
    0.5f, // キャストする距離
    Vector3.forward, // キャスト方向
    out var hit // ヒット情報
);

Physics.CapsuleCastメソッドでカプセルキャストによる当たり判定が可能です。カプセルを指定した向きに飛ばし、ヒットするコライダーの取得を行います。

図では縦幅と横幅が異なるビームを放つような当たり判定になりますが、カプセルの高さを0にすれば球を放つ(縦横同じ幅)ことも可能です。 [1]

また、ヒットしたすべてのコライダーを取得するPhysics.CapsuleCastAllメソッドPhysics.CapsuleCastNonAllocメソッドなどもあります。

本記事では、これらのメソッドを使ってカプセルキャストによる当たり判定を行う方法を解説します。

動作環境
  • Unity2021.2.1f1

Physics.CapsuleCastメソッド

指定されたカプセルを飛ばし、その経路上にヒットするコライダーがあるか判定するメソッドです。ただし、開始位置に重なっているコライダーは無視されます。

次のオーバーロードされた2つが用意されています。

public static bool CapsuleCast(
    Vector3 point1,
    Vector3 point2,
    float radius,
    Vector3 direction,
    float maxDistance = Mathf.Infinity,
    int layerMask = DefaultRaycastLayers,
    QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
);
public static bool CapsuleCast(
    Vector3 point1,
    Vector3 point2,
    float radius,
    Vector3 direction,
    out RaycastHit hitInfo,
    float maxDistance = Mathf.Infinity,
    int layerMask = DefaultRaycastLayers,
    QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
);

両者の違いは、第5引数にレイキャスト情報を受け取るhitInfo引数があるかどうかのみです。

引数point1point2には、それぞれカプセル形状の始点終点を指定します。

radiusには、カプセルの半径を指定します。0を指定した場合の動作は未定義なことに注意が必要です。

directionには、カプセルを飛ばす向きを指定します。

hitInfoには、レイキャスト情報を受け取る構造体変数を指定します。カプセルがヒットしたコライダーを取得したい場合などに使います。

maxDistanceには、カプセル状のレイを伸ばす距離を指定します。引数が省略された場合は、距離が無限大のMathf.Infinityが指定されます。

layerMaskには、当たり判定対象となるレイヤーを指定します。引数が省略された場合は、DefaultRaycastLayers定数が指定されます。これは、Ignore Raycastレイヤー以外の全てのレイヤー指定を表す値です。 [2]

queryTriggerInteractionには、トリガー設定されているコライダー(Is Triggerが有効になっているもの)を判定に含めるかどうかを指定します。引数が省略された場合は、グローバル設定が継承されます。 [3]

戻り値は、カプセルがコライダーにヒットしたらtrueヒットしなかったらfalseとなります。

引数と当たり判定領域

カプセルキャストの当たり判定領域を可視化すると、次のようになります。

サンプルスクリプト

カプセルキャストによる当たり判定を行うサンプルスクリプトです。

CapsuleCastExample.cs
using UnityEngine;

public class CapsuleCastExample : MonoBehaviour
{
    private void OnGUI()
    {
        // オブジェクトの位置から上下に伸びるカプセルを想定
        var center = transform.position;

        // CapsuleCastによる当たり判定
        var isHit = Physics.CapsuleCast(
            center + new Vector3(0, 0.5f, 0), // 始点
            center + new Vector3(0, -0.5f, 0), // 終点
            0.5f, // キャストする距離
            Vector3.forward, // キャスト方向
            out var hit // ヒット情報
        );

        // 結果表示
        if (isHit)
        {
            GUI.Box(new Rect(20, 20, 400, 23), $"Hit! - {hit.collider}");
        }
        else
        {
            GUI.Box(new Rect(20, 20, 400, 23), "None");
        }
    }
}

このスクリプトをゲームオブジェクトにアタッチして使います。

アタッチされたゲームオブジェクトから上下に伸びるカプセルをz軸方向に飛ばし、当たったコライダーを表示する内容となっています。

実行結果

カプセルをz軸方向に飛ばし、ヒットしたらそのコライダー名が表示されます。

開始位置に重なっているコライダーは当たり判定とはなりません。

経路上にコライダーが複数存在するときは、手前のコライダーが優先されます。

Physics.CapsuleCastAllメソッド

カプセルを飛ばした経路上に重なるすべてのコライダーを取得するメソッドです。

Physics.CapsuleCastメソッドとは異なり、開始位置に重なるコライダーもヒット対象となります。

public static RaycastHit[] CapsuleCastAll(
    Vector3 point1,
    Vector3 point2,
    float radius,
    Vector3 direction,
    float maxDistance = Mathf.Infinity,
    int layerMask = DefaultRaycastLayers,
    QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
);

全体的な引数の意味もPhysics.CapsuleCastメソッドと同様のため、説明は省略します。

戻り値は、ヒット情報の配列です。メソッドを実行するたびに新しいヒット情報の配列を作成するため、GCが発生してしまうことには注意が必要です。

GCが発生しない版

Physics.CapsuleCastAllメソッドでGCが発生する問題を解決したメソッドです。

public static int CapsuleCastNonAlloc(
    Vector3 point1,
    Vector3 point2,
    float radius,
    Vector3 direction,
    RaycastHit[] results,
    float maxDistance = Mathf.Infinity,
    int layerMask = DefaultRaycastLayers,
    QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
);

ヒット情報の結果を第5引数resultsに格納する点が異なります。

戻り値は、ヒットしたコライダーの数となります。

ヒットしたコライダー情報の数は、必ずresultsに指定する配列サイズ以下となります。(範囲外アクセスが起こりません)例えば、配列サイズ4のときにヒットするコライダーが5つあったとしても、ヒット数は4となります。

サンプルスクリプト

Physics.CapsuleCastNonAllocメソッドでヒットするコライダーをすべて取得するサンプルスクリプトです。

CapsuleCastAllExample.cs
using UnityEngine;

public class CapsuleCastAllExample : MonoBehaviour
{
    // 最大4つまでのヒット情報を取得可能
    private readonly RaycastHit[] _results = new RaycastHit[4];

    private void OnGUI()
    {
        // オブジェクトの位置から上下に伸びるカプセルを想定
        var center = transform.position;

        // CapsuleCastAllによる当たり判定
        // NonAlloc版はGCを発生させない
        var hitCount = Physics.CapsuleCastNonAlloc(
            center + new Vector3(0, 0.5f, 0),
            center + new Vector3(0, -0.5f, 0),
            0.5f,
            Vector3.forward,
            _results
        );

        // ヒット数を表示
        GUI.Box(new Rect(20, 20, 400, 23), $"Hit count = {hitCount}");
    }
}

実行結果

ヒットしたコライダー数が表示されます。

開始位置のカプセルにコライダーが重なっていてもヒットとしてカウントされます。

さいごに

幅のあるレイキャストによる当たり判定を行いたい場合は、カプセルキャストを使えば実現可能です。

開始位置のカプセルを含むか含まないかは、メソッドによって異なる点には注意が必要です。

本記事で紹介したCapsuleCastメソッド以外にも、SphereCastBoxCastなど似たようなキャストで当たり判定を行うメソッドも存在します。興味がある方は、Physicsクラスの公式リファレンスをご覧ください。

参考サイト