UnityのRaycastHitには何が入っている? hit.point・distance・normal・colliderを理解しよう

広告

前回は、マウスでクリックした3Dオブジェクトを取得するために、次のようなコードを書きました。

Ray ray =
    Camera.main.ScreenPointToRay(
        Mouse.current.position.value
    );

RaycastHit hit;

if (Physics.Raycast(ray, out hit))
{
    Debug.Log(hit.collider.gameObject.name);
}

この中に、

RaycastHit hit;

というコードがあります。

前回は、

Rayが当たったときの情報を受け取る箱

くらいに考えました。

今回は、この hit の中に実際にどんな情報が入っているのかを見ていきましょう。

RaycastHitの中には何が入っている? collider・point・distance・normalを図で理解する

RaycastHitは「当たった結果のセット」

Raycastをすると、

Rayを飛ばす
↓
Colliderに当たる

だけではありません。

Unityは、そのときの情報をいろいろ教えてくれます。

たとえば、

何に当たった?

どこに当たった?

どのくらい離れていた?

どちらを向いた面に当たった?

といった情報です。

それをまとめて受け取るのが、

RaycastHit hit;

です。

イメージとしては、

hit
├ collider
├ point
├ distance
├ normal
├ rigidbody
├ transform
└ その他の情報

のようなデータのまとまりです。

今回は特に重要な、

hit.collider
hit.point
hit.distance
hit.normal

の4つを見ていきます。

hit.collider ― 何に当たった?

まず一番分かりやすいのが、

hit.collider

です。

これは、

Rayが当たったCollider

を表します。

たとえばCubeに Box Collider が付いていて、そこにRayが当たった場合、

hit.collider

には、その Box Collider が入ります。

Unity 6の公式リファレンスでも collider は「ヒットしたCollider」と定義されています。

hit.colliderはRayが当たったColliderを表す

GameObjectまで取得する

通常はColliderそのものより、

そのColliderが付いているGameObject

を知りたいことが多いでしょう。

そこで、

hit.collider.gameObject

と書きます。

たとえば、

GameObject obj =
    hit.collider.gameObject;

Debug.Log(obj.name);

とすれば、

Cube

など、クリックしたGameObjectの名前を表示できます。

処理の流れは、

hit
↓
collider
↓
gameObject

です。

hit.point ― どこに当たった?

次は、

hit.point

です。

これは、

RayがColliderに当たった位置

を表します。

型は、

Vector3

です。

つまり、

X
Y
Z

の3つの座標を持っています。

Unity公式では、hit.point は「RayがColliderに当たったワールド空間上の位置」とされています。

hit.pointはRayが当たったワールド座標上の位置を表す

実際に表示してみる

Debug.Log(hit.point);

としてみましょう。

たとえば、

(2.3, 1.1, 5.7)

のように表示されます。

これは、

GameObjectの中心位置

ではありません。

重要なのは、

Rayが実際に表面へぶつかった位置

だということです。

Cubeの中心とは違う

たとえばCubeのTransform.positionが、

(0, 0, 5)

だったとします。

しかし、RayがCubeの右上あたりに当たれば、

hit.point

は、

(0.4, 0.3, 4.5)

などになる可能性があります。

つまり、

transform.position

は、

GameObjectの基準位置

で、

hit.point

は、

Rayが実際に当たった場所

です。

この違いは非常に重要です。

hit.pointは何に使える?

たとえば、

クリックした位置にエフェクトを出す

銃弾が当たった位置に弾痕を作る

地面をクリックした場所へキャラクターを移動する

当たった位置にパーティクルを発生させる

といった処理に使えます。

たとえば、

Debug.DrawLine(
    ray.origin,
    hit.point,
    Color.red
);

とすれば、

Rayの開始位置から実際に当たった位置までを可視化することもできます。

hit.distance ― どのくらい離れていた?

次は、

hit.distance

です。

これは、

Rayの原点から、Rayが当たった位置までの距離

を表します。

Unity 6の公式リファレンスでも、Rayの場合の distance は「Rayの原点から衝突点までの距離」とされています。

型は、

float

です。

hit.distanceはカメラから当たった位置までの距離を表す

表示してみる

Debug.Log(hit.distance);

たとえば、

8.52

と表示されたら、

Rayの開始位置から当たった場所までが、

8.52 Unity units

離れているということです。

一般的なUnityの3D空間では、1 Unity unitを1mとして扱う設計が多いので、

約8.52m

として扱うゲームもあります。

ただし、Unityが「1 unitは必ず1m」と強制しているわけではなく、ゲーム側のスケール設計によります。

pointとの関係

hit.point と hit.distance は似ていますが、意味が違います。

hit.point

は、

どこに当たったか

です。

hit.distance

は、

そこまでどのくらい離れているか

です。

たとえば、

Camera ●──────────────× Cube
                       ↑
                    hit.point

この線の長さが、

hit.distance

です。

距離によって処理を変える

たとえば、

if (hit.distance < 3f)
{
    Debug.Log("近くのオブジェクトです");
}

とすれば、

近くにあるオブジェクトだけを操作することもできます。

たとえば、

3m以内ならドアを開けられる

2m以内ならアイテムを拾える

10m以内なら敵を攻撃できる

といった処理につながります。

hit.normal ― 当たった面はどちらを向いている?

4つの中で少し難しいのが、

hit.normal

です。

normal は、

Rayが当たった面の法線ベクトル

です。

Unity公式でも、normal は「Rayがヒットしたサーフェスの法線」とされています。

hit.normalは当たった面の法線ベクトルを表す

法線とは?

難しく考える必要はありません。

法線とは、

面に対して垂直に外向きへ伸びている方向

です。

たとえば、床なら、

       ↑ normal
       |
──────────── 床

となります。

通常の水平な床なら、

hit.normal

は、おおよそ、

(0, 1, 0)

になります。

つまり、

上方向

です。

壁ならnormalは横を向く

たとえば、右向きの面にRayが当たった場合、

壁
│ →
│ normal
│

のようになります。

面の方向によって、

hit.normal

も変わります。

そのため、

床なのか

壁なのか

斜面なのか

を判断するときにも利用できます。

normalは何に使える?

たとえば銃弾が壁に当たったとします。

弾痕の画像をただ配置するだけでは、

斜めの壁では向きがおかしくなるかもしれません。

そこで、

hit.normal

を利用すれば、

壁の面と同じ方向を向く

ように弾痕やエフェクトを配置できます。

ほかにも、

爆発エフェクトの向きを決める

地面の傾きを調べる

反射方向を求める

接触面にオブジェクトを沿わせる

といった処理につながります。

当たった位置にマーカーを表示し、法線の向きに合わせて調整する応用例

4つをまとめて表示してみよう

では、前回のクリック処理に、

collider
point
distance
normal

を追加してみましょう。

using UnityEngine;
using UnityEngine.InputSystem;

public class RaycastHitTest : MonoBehaviour
{
    void Update()
    {
        if (Mouse.current.leftButton.wasPressedThisFrame)
        {
            Ray ray =
                Camera.main.ScreenPointToRay(
                    Mouse.current.position.value
                );

            RaycastHit hit;

            if (Physics.Raycast(ray, out hit))
            {
                Debug.Log(
                    "Collider : " +
                    hit.collider.name
                );

                Debug.Log(
                    "Point : " +
                    hit.point
                );

                Debug.Log(
                    "Distance : " +
                    hit.distance
                );

                Debug.Log(
                    "Normal : " +
                    hit.normal
                );
            }
        }
    }
}

Cubeをクリックすると、Consoleにはたとえば、

Collider : Cube

Point : (0.42, 0.18, 4.50)

Distance : 9.53

Normal : (0.00, 0.00, -1.00)

のような情報が表示されます。

RaycastHitの情報を表示するコード例とConsoleの実行結果

1回のRaycastでいろいろ分かる

ここで大切なのは、

Physics.Raycast(ray, out hit)

を実行すると、

単に、

当たった

だけではなく、

何に当たったのか
どこに当たったのか
どのくらい離れているのか
その面はどちらを向いているのか

という情報まで取得できることです。

つまり、

RaycastHit hit

は、

Raycastの結果報告書

のようなものだと考えると分かりやすいでしょう。

図にするとこうなる

Raycastの結果を図にすると、次のような関係です。

                         ↑
                         │ hit.normal
                         │
Camera ●───────────────×│ Cube
       <---------------> │
          hit.distance   │
                        ↑
                     hit.point

当たったCollider
       ↓
 hit.collider

これを見ると、

hit.collider

は、

何に当たったか

hit.point

は、

どこに当たったか

hit.distance

は、

そこまでどのくらい離れているか

hit.normal

は、

当たった面がどちらを向いているか

という違いが分かります。

RaycastHitはstruct

ここで少しだけC#の知識につなげてみましょう。

RaycastHit は、

class

ではなく、

struct

です。

Unityの公式リファレンスでも、

struct in UnityEngine

とされています。

つまり、

RaycastHit hit;

では、RaycastHit という構造体型の変数を用意しています。

その中に、

Collider
Vector3 point
float distance
Vector3 normal

など、複数の情報がまとめられています。

初心者の段階では、

Raycastの結果をひとまとめにするデータ型

と考えておけば十分です。

out hitの意味も見えてくる

ここまで分かると、

Physics.Raycast(ray, out hit)

の意味も以前より分かりやすくなります。

これは、

Rayを飛ばしてください

↓

当たったら

↓

結果の詳しい情報を
hitに入れてください

ということです。

そして、その後で、

hit.collider
hit.point
hit.distance
hit.normal

として情報を取り出しているわけです。

まとめ

RaycastHit hit には、Raycastによって得られたさまざまな情報が入ります。

特に最初に覚えておきたいのは、この4つです。

プロパティ 分かること
hit.collider 何に当たったか
hit.point どこに当たったか
hit.distance そこまでの距離
hit.normal 当たった面の向き

一言で整理すると、

collider → 何?

point    → どこ?

distance → どのくらい?

normal   → どっち向き?

です。

前回までの流れも含めると、

Camera.main
↓
ScreenPointToRay()
↓
Ray
↓
Physics.Raycast()
↓
RaycastHit
├ collider
├ point
├ distance
└ normal

となります。

RaycastHit を理解すると、Raycastは単なる「当たったかどうかの判定」ではなく、

3D空間の情報を調べるための非常に便利な仕組み

だということが見えてきます。

訪問数 1 回, 今日の訪問数 1回

広告

Physics,Unity

Posted by hidepon