今回は厚みのあるレイを飛ばして当たり判定を行ってみたいと思います。
今まではPhysics.LinecastやPhysics.Raycast等の点や方向を使って線を引いて他のゲームオブジェクトと接触しているかどうかを判定している事が多かったですが、
今回はPhysics.BoxCast、Physics.CapsuleCast、Physics.SphereCastを使ってコライダのような空間でレイを飛ばし当たり判定をしてみたいと思います。
それぞれのメソッドの引数の指定方法は数多くあるので、その中の一部だけを使います。
レイのターゲットゲームオブジェクトの作成
レイを飛ばす前にまずはレイが当たるかどうかのターゲットであるゲームオブジェクトを作成します。
ヒエラルキー上で右クリック→3D Object→Cubeを選択し、名前をTargetCubeにします。
TargetCubeのインスペクタでLayerにTargetという名前の新しいレイヤーを作成し、設定します。
BoxCast
BoxCastはCubeのレイを飛ばして当たり判定をすることが出来ます。
BoxCastメソッドは与える引数や個数が違うオーバーロードメソッドがいくつか定義されています。
与える引数はボックスの中心位置とボックスサイズの半分の値、レイを飛ばす方向、当たった相手の情報、レイの長さ、接触判定をするレイヤーのマスク等を与えます。
1 2 3 | Physics.BoxCast (Vector3 中心位置, Vector3 ボックスサイズの半分, Vector3 レイを飛ばす方向, out ヒットした情報, Quaternion ボックスの回転, float レイの長さ, int レイヤーマスク); |
Physics.BoxCastの戻り値で条件に当てはまるゲームオブジェクト接触したかどうかのbool値が得られますのでif文の条件式にそのまま記述出来ます。
それでは、サンプルを作成していきます。
ヒエラルキー上で右クリック→3D Object→Cubeを選択し、名前をCubeRayとします。
CubeRayのTransformのY軸を回転させ前方(青矢印の方向)をTargetCubeの方向に向けておきます。
このCubeRayからBoxCastでCubeのレイを飛ばしてTargetCubeとの当たり判定をしてみます。
CubeRayにBoxCastRayTestスクリプトを作成し取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class BoxCastRayTest : MonoBehaviour { [SerializeField] private Transform targetTra; // ターゲットとの距離 private float distanceFromTargetObj; // Update is called once per frame void Update () { // ターゲットとの距離 distanceFromTargetObj = Vector3.Distance(transform.position, targetTra.position); RaycastHit hit; // Cubeのレイを飛ばしターゲットと接触しているか判定 if(Physics.BoxCast(transform.position, Vector3.one * 0.5f, transform.forward, out hit, Quaternion.identity, 100f, LayerMask.GetMask("Target"))) { Debug.Log(hit.transform.name); } } void OnDrawGizmos() { // Cubeのレイを疑似的に視覚化 Gizmos.color = Color.green; Gizmos.DrawWireCube(transform.position + transform.forward * distanceFromTargetObj, Vector3.one); } } |
targetTraにはインスペクタでTargetCubeを設定します。
distanceFromTargetObjはCubeRayとTargetCubeの距離を入れるフィールドで、Boxのレイを視覚化するときに使用します。
UpdateメソッドでPhysics.BoxCastでCubeRayを中心にSizeが1のBoxを作る為にVector3.one(new Vector3(1f, 1f, 1f)と同じ)に0.5をかけてSizeの半分を設定し、CubeRayの向いている方向100m先までレイを飛ばし、Targetレイヤーが設定されたゲームオブジェクトと接触しているかどうかを調べています。
接触していた場合はhitにデータが入るので、その当たった相手のゲームオブジェクトの名前を表示しています。
OnDrawGizmosにはシーンビューに疑似的にBoxのレイを表示する為にワイヤフレームのCubeを表示させる処理を記述しています。
OnDrawGizmosに関しては

を参照してください。
Unityのプレイボタンを押して実行すると、
上のようにシーンビューでCubeRayを移動させるとBox状のレイから外れるとコンソールにTargetCubeの表示がされなくなります。
SphereCast
SphereCastはSphere(球)のレイを飛ばして接触判定をすることが出来ます。
与える引数はBoxCastと同じような感じで複数のオーバーロードメソッドが用意されているので、自分の都合に合わせて与える引数を変更します。
SphereCastで与える引数の上方としては球の中心位置、球の半径、レイの方向、接触した相手の情報、レイの長さ、レイヤーマスク等です。
1 2 3 | Physics.SphereCast (Vector3 中心位置, float 半径, Vector3 レイの方向, out 接触した相手の情報, float レイの長さ, int レイヤーマスク); |
それではサンプルを作成していきます。
ヒエラルキー上で右クリック→3D Object→Sphereを選択し、名前をSphereRayとします。
SphereRayのY軸の数値を変更しTargetCubeの方向に向けておきます。
SphereRayにはSphereCastRayTestスクリプトを新しく作成し取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class SphereCastRayTest : MonoBehaviour { [SerializeField] private Transform targetTra; // ターゲットとの距離 private float distanceFromTargetObj; // Update is called once per frame void Update () { // ターゲットとの距離 distanceFromTargetObj = Vector3.Distance(transform.position, targetTra.position); RaycastHit hit; // SphereCastのレイを飛ばしターゲットと接触しているか判定 Ray ray = new Ray(transform.position, transform.forward); if(Physics.SphereCast(ray, 0.5f, out hit, 100f, LayerMask.GetMask("Target"))) { Debug.Log(hit.transform.name); } } void OnDrawGizmos() { // Capsuleのレイを疑似的に視覚化 Gizmos.color = Color.red; Gizmos.DrawWireSphere(transform.position + transform.forward * distanceFromTargetObj, 0.5f); } } |
BoxCastRayTestスクリプトとほぼ同じなので問題はないと思います。
ただ今回は球の中心位置とレイの方向を引数に与えるのではなくレイの飛ばす位置と飛ばす方向からRay型の変数を作成し、それを引数に与えるメソッドを使用しています。
Unityのプレイボタンを押して実行すると、
上のような感じになります。
ちゃんと球での接触判定がされていることがわかります。
CapsuleCast
最後にCapsuleCastを使います。CapsuleCastはCapsuleのレイを飛ばして接触判定をすることが出来ます。
CapsuleCastも複数のオーバーロードメソッドが定義されています。
与える引数はカプセルを形成する両端の球の中心位置の2点、球の半径、レイの方向、レイの長さ、レイヤーマスク等です。
1 2 3 | Physics.CapsuleCast (Vector3 カプセルの球のStartの中心位置, Vector3 カプセルの球のEndの中心位置, float 半径, Vector3 レイの方向, out 接触した相手の情報, float レイの長さ, int レイヤーマスク); |
それではサンプルを作成していきましょう。
ヒエラルキー上で右クリック→3D Object→Capsuleを選択し、名前をCapsuleRayとします。
CapsuleRayのY軸の回転を変更し、TargetCubeの方向を向くようにします。
CapsuleRayにはCapsuleCastRayTestスクリプトを作成し取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class CapsuleCastRayTest : MonoBehaviour { [SerializeField] private Transform targetTra; // ターゲットとの距離 private float distanceFromTargetObj; // Update is called once per frame void Update () { // ターゲットとの距離 distanceFromTargetObj = Vector3.Distance(transform.position, targetTra.position); RaycastHit hit; // Cubeのレイを飛ばしターゲットと接触しているか判定 if(Physics.CapsuleCast(transform.position - transform.up * 0.5f, transform.position + transform.up * 0.5f, 0.5f, transform.forward, out hit, LayerMask.GetMask("Target"))) { Debug.Log(hit.transform.name); } } void OnDrawGizmos() { // Capsuleのレイを疑似的に視覚化 Gizmos.color = Color.red; Gizmos.DrawWireSphere(transform.position - transform.up * 0.5f + transform.forward * distanceFromTargetObj, 0.5f); Gizmos.DrawWireSphere(transform.position + transform.up * 0.5f + transform.forward * distanceFromTargetObj, 0.5f); } } |
Physics.CapsuleCastの引数には作成したCapsuleRayのオブジェクトの形状と合わせたレイの形を作成する為、CapsuleRayの中心位置から上と下の位置を計算し、それをカプセルを形成する二つの球の中心位置として渡しています。
OnDrawGizmosメソッドではレイを疑似的に視覚化する為、二つのワイヤフレームの球を表示しています。
Unityのプレイボタンを押して実行すると、
上のようになります。
カプセル状のレイが飛んでいるのがわかりますね。(^^)/
似たメソッド
BoxCast、SphereCast、CapsuleCastには似たようなメソッド名のBoxCastAll、BoxCastNonAlloc、SphereCastAll、SphereCastNonAlloc、CapsuleCastAll、CapsuleCastNonAllocがあります。
BoxCastAll等はBoxCast等と違い、レイと接触した情報(RaycastHit)を全て取得する事が出来ます。
接触した相手の全ての情報を使って何らかの処理をしたい時はBoxCastAllメソッドを使用すると便利です。
1 2 3 | RaycastHit[] hits = Physics.BoxCastAll (Vector3 中心位置, Vector3 ボックスのサイズの半分, Vector3 レイの方向, Quaternion ボックスの回転, float レイの長さ, int レイヤーマスク); |
BoxCast等は接触した相手がいるかどうかのbool値を返しますが、BoxCastAll等は接触した全ての相手の情報であるRaycastHit型の配列を取得出来ます。
用途としてはFPSゲーム等のショットガンで敵を打った時に複数の接触相手に攻撃を与える時等に使えます。
実際に使った例は『終わりに』の項目のリンク先の記事にあります。
BoxCastNonAlloc等はBoxCastAllメソッドとほぼ同じ機能ですが、引数にRaycastHit型の配列を指定しそこにデータを保持し、戻り値は接触した相手の数が返ってきます。
BoxCastNonAlloc等はBoxCastAll等と違ってゴミを発生させないみたいです。
ということは全データを取得する時はNonAlloc系の方がいいんですかね?
終わりに
LinecastやRaycastは使う機会が多いですが、厚みのあるレイも使えるようにしておくと見えない弾丸を飛ばして当たり判定をする時など弾の厚みのレイを飛ばして判定という事が出来ます。
そこら辺は

の記事で実際に使っているので参照してみてください。