UnityユーザーのためのUEブループリント入門
コードとビジュアル両方のスクリプトを比較してゲームエンジンを学びます

17. 物体検知

  • Aのライントレース(レイキャスト)による物体検知
  • Aのライントレース(レイキャスト)がBに当たったときに得られる情報

17. 物体検知

17.1 Aのレイキャストによる物体検知

レイキャストは相互作用と異なり、あるゲームオブジェクトから発するレイキャストが相手のゲームオブジェクトに作用させることはありません。物体検知にコライダーは必要はなく、ゲームオブジェクトに対して検知します。
AのレイキャストからBを検知する
AのレイキャストからBを検知させます。シーン上に二つのSphereゲームオブジェクトを置いて、赤いSphereをSphereA、青いSphereをSphereBとします。
SphereAからレイキャストを発射させてBを検知するスクリプトを用意します。 レイキャストを表示するためにDebug.DrawRayメソッドを使って赤色の線で示しています。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RaycastSample : MonoBehaviour
{
    public float maxDistance = 10.0f;
    Ray ray;

    void Start()
    {
        ray = new Ray(  transform.position, 
                            transform.TransformDirection(Vector3.forward));
        Debug.DrawRay(      gameObject.transform.position,  
                            ray.direction * maxDistance, 
                            Color.red);
        if(Physics.Raycast(ray, out RaycastHit hit))
        {
            //var target = ray.origin + ray.direction * maxDistance;
            Debug.Log(hit.collider.gameObject.name);
        }
    }
}
RaycastSampleコンポーネントをSphereAのゲームオブジェクトに追加します。
SphereAのインスペクタです。
SphereBのインスペクタです。
playを実行して、SphereAから発射されるレイキャストがSphereBに当たりSphereAを検知していることがわかります。
Aのレイキャストで複数のゲームオブジェクトを検知する
Aのレイキャストで複数のゲームオブジェクトを検知させます。シーン上に3つのSphereゲームオブジェクトを置いて、赤いSphereをSphereA、青いSphereをSphereB、ピンク色のSphereをSphereCとします。
SphereAからレイキャストを発射させて複数のゲームオブジェクトを検知するスクリプトを用意します。 RaycastSampleスクリプトと同じくレイキャストを表示するためにDebug.DrawRayメソッドを使って赤色の線で示しています。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class RaycastAll : MonoBehaviour
{
    public float maxDistance = 10.0f;
    Ray ray;
    RaycastHit[] hits;

    void Start()
    {
        ray = new Ray(  transform.position, 
                        transform.forward);
        hits = Physics.RaycastAll(  ray,
                                    maxDistance);
        Debug.DrawRay(      transform.position,  
                            ray.direction * maxDistance, 
                            Color.red,
                            1.0f);
        if(hits.Length > 0)
        {
            Array.Sort(hits, (RaycastHit x, RaycastHit y) => x.distance.CompareTo(y.distance));
            foreach(var hit in hits)
            {
                Debug.Log(hit.collider.gameObject.name);
            }
        }
    }
}
RaycastAllコンポーネントをSphereAのゲームオブジェクトに追加します。
SphereAのインスペクタです。
SphereBのインスペクタです。
SphereCのインスペクタです。
playを実行して、SphereAから発射されるレイキャストが複数のゲームオブジェクトを検知していることがわかります。
Aのレイキャストで指定したゲームオブジェクトを検知する
Aのレイキャストで複数のゲームオブジェクトから指定したゲームオブジェクトを検知させます。「Aのレイキャストで複数のゲームオブジェクトを検知する」で使用した3つのSphereゲームオブジェクトを使います。SphereCを指定して検知させます。
Layerに新しく値を追加します。
インスペクタのLayerをクリックしてAdd Layer...をクリックします。
User Layer 7 にSphereが登録されていることを確認します。相互作用の説明で追加していない場合は登録します。
SphereAからレイキャストを発射させて指定したゲームオブジェクトを検知するスクリプトを用意します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class RaycastDetection : MonoBehaviour
{
    public float maxDistance = 10.0f;
    Ray ray;
    RaycastHit[] hits;

    void Start()
    {
        ray = new Ray(  transform.position, 
                        transform.forward);
        hits = Physics.RaycastAll(  ray, 
                                    maxDistance, 
                                    1 << 7);
        Debug.DrawRay(      transform.position,  
                            ray.direction * maxDistance, 
                            Color.red,
                            1.0f);
        if(hits.Length > 0)
        {
            Array.Sort(hits, (RaycastHit x, RaycastHit y) => x.distance.CompareTo(y.distance));
            foreach(var hit in hits)
            {
                Debug.Log(hit.collider.gameObject.name);
            }
        }
    }
}
RaycastDetectionコンポーネントをSphereAのゲームオブジェクトに追加します。
SphereAのインスペクタです。
SphereBのインスペクタです。
SphereCのインスペクタです。LayerをSphereに変更します。
playを実行して、SphereAから発射されるレイキャストが指定したゲームオブジェクトを検知していることがわかります。
レイヤーを複数指定して物体検知することもできます。
SphereBのレイヤーはDefaultにします。
SphereCのレイヤーはSphereにします。
複数のレイヤーを指定して物体検知するために、以下のようにスクリプトを変更します。DefaultレイヤーとSphereレイヤーをもったゲームオブジェクトを検知します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class RaycastMultiDetection : MonoBehaviour
{
    public float maxDistance = 10.0f;
    Ray ray;
    RaycastHit[] hits;

    void Start()
    {
        ray = new Ray(  transform.position, 
                        transform.forward);
        hits = Physics.RaycastAll(  ray, 
                                    maxDistance, 
                                    1 << 0 | 1 << 7);
        Debug.DrawRay(      transform.position,  
                            ray.direction * maxDistance, 
                            Color.red,
                            1.0f);
        if(hits.Length > 0)
        {
            Array.Sort(hits, (RaycastHit x, RaycastHit y) => x.distance.CompareTo(y.distance));
            foreach(var hit in hits)
            {
                Debug.Log(hit.collider.gameObject.name);
            }
        }
    }
}
playを実行して、SphereAから発射されるレイキャストが指定したゲームオブジェクトを複数検知していることがわかります。

17.2 AのレイキャストがBに当たったときに得られる情報

articulationBody The ArticulationBody of the collider that was hit. If the collider is not attached to an articulation body then it is null.
barycentricCoordinate ヒットした三角形の重心座標
collider ヒットしたコライダー
colliderInstanceID Instance ID of the Collider that was hit.
distance レイの原点から衝突点までの距離
lightmapCoord 衝突点の UV ライトマップ座標
normal レイがヒットしたサーフェスの法線
point レイがコライダーにヒットした位置 (ワールド空間)。
rigidbody ヒットしたコライダーの Rigidbody。コライダーに Rigidbody がアタッチされていない場合は null になります。
textureCoord The uv texture coordinate at the collision location.
textureCoord2 衝突したセカンダリ UV テクスチャの座標
transform 衝突したコライダーまたは Rigidbody の Transform
triangleIndex 衝突したメッシュの三角形におけるインデックス
🔗 参考リンク
Unity Documentation
RaycastHit

17. 物体検知

Unityが持つレイキャストとUEのライントレースを比較すると、UEには物体検知をするノードがいくつか用意されているので、Unityと似たノードとそうでないノードについて、その違いを理解する必要があります。結論を先に申し上げると、相手にCollisionコンポーネントが必要であることを除いて、Unityと同じコライダーの機能を使いたい場合は、MultiLineTraceForObjectsノードの使い方を覚えておくとよいでしょう。初めからUnityのColliderとLayerがオブジェクトチャンネルとトレースチャンネルと対応していると思って覚えてしまうと、後々わけがわからなくなるので、この節で最後の方の「Aのライントレースで複数のアクターを検知する」「Aのライントレースで指定したアクターを検知する」で紹介するMultiLineTraceForObjectsノードの使い方をマスターしてから、トレースチャンネルを使いたくなったら残りのノードについて徐々に学ぶとよいでしょう。
それから、Unity相手のコライダーに関係なく物体検知ができるのに対して、UEは相手のアクターにCollisionコンポーネントが追加されていることが条件になり、さらに「最初にヒットした物体を検知するライントレース」と「すべての物体または特定の物体を検知するライントレース」の二つに分かれます。そして相手のアクターがトレースチャンネルを使う場合とオブジェクトチャンネルを使う場合で分かれてます。以下の表にまとめました。
Aのライントレースによる物体検知 LineTraceByChannelノード 相手のアクターはCollisionコンポーネントが必要 指定したトレースチャンネルに最初にヒットした物体を検知するライントレース
LineTraceForObjectsノード 指定したオブジェクトチャンネルに最初にヒットした物体を検知するライントレース
Aのライントレースで複数のアクターを検知する、Aのライントレースで指定したアクターを検知する MultiLineTraceForObjectsノード、MultiLineTraceByChannelノード(※) Unityのレイキャストと同じ動きをする、指定したオブジェクトチャンネルのすべての物体を検知するライントレース
ノードの名前にByChannelが含まれている場合はトレースチャンネルを使用し、ForObjectsが含まれている場合はオブジェクトチャンネルを使用すると覚えておきましょう。
ライントレースについては、第5章の5.9. ジャンプにて「ジャンプから落下時のアニメーションの追加」で実際に使います。
UEのコリジョンをまとめるときに、以下のUE公式のブログを確認しました。UEにおけるコリジョンの基本的な設計について記載されていますので一度読んでおくと良いと思います。特にトレースチャンネルとオブジェクトチャンネルを分けて使いたいときは参考になります。
🔗 参考リンク
Unreal Engine
Collision Filtering
UEには、Unityと同様にライントレースの他にボックストレース、カプセルトレース、スフィアトレースがありますが、物体検知の説明ではライントレースのみ扱います。

17.1 Aのライントレースによる物体検知

AのライントレースでBを検知する

LineTraceByChannelを使った物体検知

Aから発射するライントレースの対象トレースチャンネルをBのトレースチャンネルに設定した相手アクターにCollisionコンポーネントを追加して、相手のトレースチャンネルトレース応答がBlockかをみて検知します。
AのライントレースからBを検知させます。レベル上に二つのSphereアクターを置いて、赤いSphereをSphereA、青いSphereをSphereBとします。
ライントレースが物体を検知するので、SphereAのSphere Static MeshコンポーネントのPhysicsとCollisionに対して、相互作用で設定したような、Simulate Physics、Simuration Generates Hit Events、Generate Overlap Eventsにチェックを入れる必要はありません。
SphereBのSphere Static MeshコンポーネントのPhysicsとCollisionに対しても、Simulate Physics、Simuration Generates Hit Events、Generate Overlap Eventsにチェックを入れる必要はありません。CollisionのコリジョンプリセットはBlock All Dynamicにします。今回はSphereBのトレースチャンネルVisibilityに対してライントレースを発射させたときの物体検知を確認します。
SphereAからライントレースを発射させてSphereBを検知するブループリントを用意します。LineTraceByChannelノードを使って相手のトレースチャンネルがVisibleのライントレースを発射させています。LineTraceByChannelノードのTrace ChannelはVisibilityを選択します。Draw Debug TypeをPersistentにすると、play実行時にライントレースを赤色の線で表示します。
playを実行して、SphereAから発射されるライントレースがSphereBに当たりSphereAを検知していることがわかります。
🔗 参考リンク
Unreal Engine
Single Line Trace (レイキャスト) by Channel を使用する

LineTraceByChannelノードの相互作用の有無についての確認

次にSphereBに対してHitイベントが発生しているかについて確認します。
SphereAのSphere Static MeshコンポーネントのPhysicsとCollisionに対して、Simulate Physics、Simuration Generates Hit Eventsにチェックを入れます。
SphereBのSphere Static MeshコンポーネントのPhysicsとCollisionに対して、Simulate Physics、Simuration Generates Hit Eventsにチェックを入れます。
SphereBのブループリントに以下のとおりHitイベントノードを追加します。
playを実行して、LineTraceByChannelノードからのライントレースについては相互作用が発生していないことを確認します。

LineTraceForObjectsノードを使った物体検知

今度はLineTraceByChannelノードの代わりにLineTraceForObjectsを使います。
SphereAのブループリントを以下のように変更します。 Make Arrayノードを追加して、Object Typesピンに接続すると、オブジェクトチャンネルのEnumリストが表示されますので、WorldDynamicを選択します。
SphereBのSphere Static MeshコンポーネントのPhysicsとCollisionは変更せず、以下の設定のままにします。
playを実行すると、トレースチャンネルではなくオブジェクトチャンネルで物体を検知していることが確認できます。
🔗 参考リンク
Unreal Engine
Single Line Trace (レイキャスト) by Object を使用する

LineTraceForObjectsノードの相互作用の有無についての確認

次にSphereBに対してHitイベントが発生しているかについて確認します。
SphereAのSphere Static MeshコンポーネントのPhysicsとCollisionに対して、Simulate Physics、Simuration Generates Hit Eventsにチェックを入れます。
SphereBのSphere Static MeshコンポーネントのPhysicsとCollisionに対して、Simulate Physics、Simuration Generates Hit Eventsにチェックを入れます。
Simulae Physicsを使うことによってSphereBのオブジェクトチャンネルがPhysicsActorに変更になったので、SphereAのLineTraceForObjectsノードのObject TypesをPhysicsBodyに変更します。
SphereBのブループリントに以下のとおりSphere Static MeshコンポーネントのOn Component Hitイベントノードを追加します。Floorとのヒットを検出しないようにSphereA側の名前でその対象を絞ります。
playを実行して、LineTraceForObjectsノードからのライントレースについては相互作用が発生していないことを確認します。
Aのライントレースで複数のアクターを検知する
Aのライントレースで複数のアクターを検知させます。レベル上に3つのSphereアクターを置いて、赤いSphereをSphereA、青いSphereをSphereB、ピンク色のSphereをSphereCとします。
SphereAは先ほどと同じくSphere Static MeshコンポーネントのPhysicsとCollisionに対しても、Simulate Physics、Simuration Generates Hit Events、Generate Overlap Eventsにチェックを入れる必要はありません。
SphereBとSphereCのSphere Static MeshコンポーネントのPhysicsとCollisionに対しても、Simulate Physics、Simuration Generates Hit Events、Generate Overlap Eventsにチェックを入れる必要はありません。CollisionのコリジョンプリセットはBlock All Dynamicにします。
SphereAからライントレースを発射させてSphereBとSphereCを検知するブループリントを用意します。複数の物体(アクター)を検知するのはMultiLine Trace For Objectsノードを使います。
MultiLine Trace For ObjectsノードのObject TypesピンはArray型のオブジェクトチャンネルを入力します。Make Arrayノードを追加して、Object Typesピンに接続すると、オブジェクトチャンネルのEnumリストが表示されますので、WorldDynamicを選択します。
playを実行して、SphereAから発射されるライントレースが複数のアクターを検知していることがわかります。
Aのライントレースで指定したアクターを検知する
Aのライントレースで複数のアクターから指定したアクターを検知させます。Aのライントレースで複数のアクターを検知するで使用した3つのSphereアクターを使います。SphereCを指定して検知させます。
新しいオブジェクトチャンネルを追加します。 Edit > Project Settings... > Engine > Collisionを選択して、Object ChannelsにあるNew Object Channel...ボタンをクリックします。
新しいオブジェクトチャンネルの名前をSphereにします。デフォルトの応答タイプはBlockになっていることを確認して、OKをクリックします。
オブジェクトチャンネルにSphereが追加されていることを確認します。
Aのライントレースで複数のアクターを検知するときに使用したSphereAとSphereBの設定はそのまま使用します。
SphereBのSphere CollisionコンポーネントのDetailsパネルでオブジェクトチャンネルがWorldDynamicが選択されていることを確認します。
SphereCのSphere CollisionコンポーネントのDetailsパネルで、CollisionのCollision PresetsをCustomにします。Trace ResponseでオブジェクトタイプをSphereに変更します。
SphereAからライントレースを発射させて指定しアクターを検知するブループリントを用意します。
MultiLine Trace For ObjectsノードのObject TypesピンはArray型のオブジェクトチャンネルを入力します。Make Arrayノードを追加して、Object Typesピンに接続すると、オブジェクトチャンネルのEnumリストが表示されますので、Sphereを選択します。
playを実行して、SphereAから発射されるライントレースが指定したアクターを検知していることがわかります。
オブジェクトチャンネルの指定はMake Arrayノードを使っているので配列数を増やして複数選択することができます。

MultiLineTraceForObjectsノードの相互作用の有無についての確認

次にSphereCに対してHitイベントが発生しているかについて確認します。
オブジェクトチャンネルの指定はSphereに戻します。
SphereAのSphere Static MeshコンポーネントのPhysicsとCollisionに対して、Simulate Physics、Simuration Generates Hit Eventsにチェックを入れます。
SphereBのSphere Static MeshコンポーネントのPhysicsとCollisionに対して、Simulate Physics、Simuration Generates Hit Eventsにチェックを入れます。
SphereBのブループリントを記述します。
SphereCのSphere Static MeshコンポーネントのPhysicsとCollisionに対して、Simulate Physics、Simuration Generates Hit Eventsにチェックを入れます。オブジェクトチャンネルはSphereに変更します。
SphereCのブループリントを記述します。
playを実行して、LineTraceForObjectsノードからのライントレースについては相互作用が発生していないことを確認します。

MultiLine Trace By Channelノードを使った物体検知

MultiLine Trace By ChannelはSphereAから発射したライントレースから一番近くに当たった該当のトレースチャンネルがBlockのアクターを検知します。発射したアクターとBlockで検知されたアクターの間に、別のアクターが存在したとき、該当のトレースチャンネルがOverlapに設定している場合はそれらも含めて取得します。
...とのことですが、手元の環境では確認できなかったのと、Unityに該当する機能がないのでここでの説明は省略します。詳しくは以下のサイトにてご確認ください。
🔗 参考リンク
Unreal Engine
Multi Line Trace by Channel を使用する

17.2 AのライントレースがBに当たったときに得られる情報

ライントレースが相手に当たったときに得られる情報は以下のとおりです。
  • Line/Box/CapsuleTraceByChannelノード
Location トレースの形状に応じて修正されるヒットのワールド空間の位置。
Impact Point ヒットの絶対位置。ヒットのポイントだけでトレースの形状は含みません。
Normal トレースの方向です。
Impact Normal ヒットしたサーフェスの法線です。
Phys Mat ヒットしたサーフェスの 物理マテリアル です。
Hit Actor ヒットした アクタ
Hit Component ヒットした特定の コンポーネント
Hit Bone Name スケルタルメッシュ に対してトレースする場合にヒットしたボーン名
Bone Name トレース ボーン ヒットの名前。 スケルタルメッシュにヒットした場合にのみ有効です。
Hit Item プリミティブのどのアイテムにヒットしたかを記録するプリミティブ固有のデータ
Element Index 複数のパーツを持つプリミティブと衝突した場合にヒットしたパーツのインデックス。
Face Index 三角メッシュやランドスケープと衝突する場合に、ヒットした面のインデックスです。
Trace Start トレースの開始位置です。
Trace End トレースの終了位置です。