障害物がある時とない時に動作を変える

2025年10月7日

追っかけキャラクタから逃げる時、間にオブジェクトがあったら追っかけキャラクタが止まってくれると隠れたように思いますよね。この処理の実現の方法を見ていきましょう

光線を飛ばして間の障害物を検知する

相手との距離を測る

手順

  1. ベクトルの差を計算(引き算)します
  2. 三平方の定理からポジション間の距離を計算します

三平方の定理では、それぞれの2乗を足してルートすると距離が長さとして得られるものです

ベクトルの差を計算します

敵にスクリプトがアタッチされているケースを考えます(つまり敵から見た計算)

自分の場所は、transform.positionで表せます
相手の場所は、(相手のオブジェクト).transform.positonで表せますが、
(相手のコライダー).transform.positonでも取得できます

colliderは、相手のコライダーを引数で取得しているとします

Vector3 Diff = collider.transform.position - transform.position;

相手との距離を数値で得るためにまず、位置情報同士を引きます

距離を計算します

三平方の定理の計算用メソッドが用意されているのを使います

次のコードで距離を計算できます

float L = Diff.magnitude;

間に障害物があるか判定する

自分から見た相手に対して直線上に何かあるか?を数えて1以上なら障害物があると判断する処理を考えます
何かあるか?は、コライダーが途中で検出されたか?になります

間の障害物の数を数えるメソッド

今回のサンプルでは、元(基準)は、敵になります
つまり、敵から見た世界ですね

int hitCount = Physics.RaycastNonAlloc(元の場所, 方向, 障害物格納用配列, 確認する距離);

各変数の計算式をhitCountの計算は、次のようになります

RaycastHit[] _raycastHits = new RaycastHit[10];
// 自身とプレイヤーの座標差分を計算
var positionDiff = collider.transform.position - transform.position; 

// プレイヤーとの距離を計算
var distance = positionDiff.magnitude; 

// プレイヤーへの方向(長さだけ1にする処理。方向は変わらない)
var direction = positionDiff.normalized; 

// _raycastHitsに、ヒットしたColliderや座標情報などが格納される
// RaycastAllとRaycastNonAllocは同等の機能だが、RaycastNonAllocだとメモリにゴミが残らないのでこちらを推奨
var hitCount = Physics.RaycastNonAlloc(transform.position, direction, _raycastHits, distance);

hitCountが0なら障害物がないと判断します

if (hitCount == 0)
{
    // 障害物がない時の処理を記述
}

Rayを可視化(見えるようにする)

可視化の様子

Sceneビューの表示されます

RayはイメージでSceneでも表示されませんが、デバッグの機能として見えるようにすることができます

Rayを表示するコード

Debug.DrawRay(transform.position, direction, Color.green);

Unityのスクリプトリファレスから

Debug.DrawRay

public static void DrawRay (Vector3 startVector3 dirColor color= Color.white, float duration= 0.0f, bool depthTest= true);

パラメーター

startレイを開始する位置(ワールド座標)
dirレイの向きと長さ
colorラインの色
durationラインを表示する時間(秒単位)
depthTestラインがカメラから近いオブジェクトによって隠された場合にラインを隠すかどうか

説明

ワールド座標にて start (開始地点)から start + dir (開始地点+方向)までラインを描画します。

duration は命令を発行してからラインが描画され、消えるまでの時間(秒単位)です。duration が 0 の場合は 1 フレームのみ表示されます。

バージョン2021.3の日本語説明(十分参考になります)

バージョン6.2の英語説明

おまけ

相手を検知した時には緑のラインで、していないときは、赤のラインで表示するようしたコードです

if (collider.CompareTag("Player"))
{
    var positionDiff = collider.transform.position - transform.position;

    var distance = positionDiff.magnitude;
    var directiton = positionDiff.normalized;

    var hitCount = Physics.RaycastNonAlloc(transform.position, directiton, _raycastHits, distance, raycastLayerMask);

    //            Debug.Log(hitCount);

    if (hitCount == 0)
    {
        _agent.isStopped = false;
        _agent.destination = collider.transform.position;
        // Rayの表示
        Debug.DrawRay(transform.position, positionDiff, Color.green);
    }
    else
    {
        _agent.isStopped = true;
        // Rayの表示
        Debug.DrawRay(transform.position, positionDiff, Color.red);
    }
}

このコードは NavMeshAgent を使った敵キャラクターがプレイヤーを追いかける処理の一部で、プレイヤーとの間に障害物があるかどうかを Raycast で判定しています。順を追って説明します。


コードの流れ

1. プレイヤーとの判定

if (collider.CompareTag("Player"))
  • 当たった collider が Player タグを持っているときだけ処理します。
  • つまり「プレイヤーを検知したとき」の処理。

2. プレイヤーとの距離・方向を計算

var positionDiff = collider.transform.position - transform.position;
var distance = positionDiff.magnitude;
var directiton = positionDiff.normalized;
  • positionDiff: 敵からプレイヤーまでのベクトル(位置差)。
  • distance: 敵とプレイヤーの距離(長さ)。
  • directiton: 敵からプレイヤーへの方向ベクトル(正規化)。

3. Raycast で障害物を調べる

var hitCount = Physics.RaycastNonAlloc(
    transform.position,   // Ray の始点(敵の位置)
    directiton,           // Ray の方向(プレイヤーの方向)
    _raycastHits,         // 結果を格納する配列
    distance,             // 長さ(プレイヤーまでの距離分)
    raycastLayerMask      // 対象とするレイヤー
);
  • プレイヤーまでの直線上に障害物があるか調べます。
  • hitCount は衝突したオブジェクトの数。
  • 0 なら「何も遮るものがない(プレイヤーが見えている)」。

4. 移動制御

if (hitCount == 0)
{
    _agent.isStopped = false;
    _agent.destination = collider.transform.position;
    Debug.DrawRay(transform.position, positionDiff, Color.green);
}
else
{
    _agent.isStopped = true;
    Debug.DrawRay(transform.position, positionDiff, Color.red);
}
  • 障害物がない場合 (hitCount == 0)
    • NavMeshAgent の移動を有効化 (isStopped = false)。
    • プレイヤーの位置を目的地にセット (destination = collider.transform.position)。
    • 緑色の Ray を Scene ビューに描画。
  • 障害物がある場合
    • NavMeshAgent の移動を停止 (isStopped = true)。
    • 赤色の Ray を Scene ビューに描画。

まとめ

この処理は要するに:

  • プレイヤーが視界に入っていて遮るものがない → 追いかける
  • 間に障害物がある → 立ち止まる

という挙動を実現しています。

デバッグ用に Debug.DrawRay で緑と赤を使い分けているので、Scene ビューで「プレイヤーを見えているかどうか」を確認できます。


訪問数 161 回, 今日の訪問数 11回

Unity

Posted by hidepon