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

2023年4月6日

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

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

相手との距離を測る

手順

  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の日本語説明

バージョン2023.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);
    }
}

Unity

Posted by hidepon