レイキャスト:目の前にあるものを世界に尋ねる
プレイヤーが何を目指し、何に乗っているか判定するには?
Physics.Raycast は見えない線を飛ばし、最初に当たった collider をヒット地点・距離・法線つきで返します。LayerMask と最大距離は必ず指定——でないと無限先まで検査し、三人称カメラからだと手前のプレイヤーに当たります。
Raycast とは一つの質問です——ここからこの向きに見えない線を放ったら、最初に何に当たるか?射撃、地面チェック、プレイヤーの指の下にあるもの、カメラの視線の先、敵から見えるかどうか——すべては引数 4 つの関数一つの話です。
見えない線一本。最初にぶつかった collider が、ヒット地点、面の法線、距離を返してくれます。
その形
using UnityEngine;
public class Shooter : MonoBehaviour
{
public float range = 50f;
public LayerMask hittable; // set this in the Inspector
void Fire()
{
// origin, direction, out result, how far, what counts
if (Physics.Raycast(transform.position, transform.forward,
out RaycastHit hit, range, hittable))
{
Debug.Log("hit " + hit.collider.name + " at " + hit.distance + "m");
}
}
}ヒットが教えてくれること
hit.point——世界空間での正確な位置です。弾痕、火花、デカールはここに置きます。
hit.normal——面が向いている方向です。エフェクトをこの向きに合わせると、火花は壁から飛び出さず面にぴったり沿います。
hit.distance——ray が進んだ長さです。距離によるダメージ減衰や、正しい長さのトレーサーを描くのに役立ちます。
hit.collider——当たった対象です。そこから GetComponent を呼んで体力スクリプトを探したり、CompareTag で味方と壁を区別したりします。
// Quaternion.LookRotation(hit.normal) makes the effect's forward axis point
// straight out of the wall.
Instantiate(impactPrefab, hit.point, Quaternion.LookRotation(hit.normal));LayerMask を正しく使う
LayerMask は layer の番号ではありません。32 ビットの値で、layer に一つずつビットが対応し、判定に含める layer のビットが立ちます。フィールドを LayerMask 型で宣言すれば、Inspector にはチェックボックス付きのドロップダウンが出て、自力でビット計算をすることもなくなります。
// WRONG: layer 8 as a number means bit 3, which is layer 3.
Physics.Raycast(origin, dir, out hit, range, 8);
// Right, in code:
int mask = 1 << LayerMask.NameToLayer("Enemy");
// Right, and better: let the Inspector do it.
public LayerMask hittable;
// Everything EXCEPT the player's own layer:
int allButPlayer = ~(1 << LayerMask.NameToLayer("Player"));カメラから、あるいは指から
void Update()
{
// Mouse. On a phone, Input.mousePosition follows the first touch too,
// so this works on both without changing anything.
if (!Input.GetMouseButtonDown(0)) return;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, 100f, selectable))
{
hit.collider.GetComponent<Selectable>()?.Select();
}
}線を見えるようにする
Raycast は見えないものなので、描き出さない限りデバッグは当てずっぽうになります。たった 2 行で、ゲーム実行中の Scene view で確かめられるようになります。
// Green to where it hit, red for the full length when it missed.
if (Physics.Raycast(origin, dir, out RaycastHit hit, range, mask))
Debug.DrawLine(origin, hit.point, Color.green, 1f);
else
Debug.DrawRay(origin, dir * range, Color.red, 1f);線では細すぎるとき
SphereCast は ray に沿って球を転がす判定です。地面チェックも太い弾も望むのはこちらで、物体なら通れない隙間を線だとすり抜けてしまうことがあります。
RaycastAll は線上のすべてを返しますが、順不同です。複数の敵を貫通する射撃に使えますが、距離順の並び替えは自分で行ってください。順序は保証されません。
RaycastNonAlloc は新しい配列を確保する代わりに、自分で用意した配列に結果を詰めます。スマホで Update のたびに RaycastAll を呼び続けるとゴミが溜まり、いつかガベージコレクションがゲームを止めて掃除することになります。
Trigger を無視するか含めるかは Physics の設定次第です。誰かが変えかねないプロジェクト全体の既定値に頼らず、QueryTriggerInteraction は明示的に渡してください。
これを作るレッスン
弾、ダメージ、エフェクト
この記事はそれだけで完結するレシピです。コースでは同じものを、5 つのタブを貫くひとつのプロジェクトの一部として作ります — キャラクターと戦闘 のレッスン 05。