【Unity】隠れた相手が現れたら見つける

3Dオブジェクト同士で、相手が隠れている状態では見つけられなくて、ひょこっと現れたら見つけてしまうようにしましょう

学習用のサンプル動画

シーン構成

相手を見つけようとしているオブジェクト

Cubeです
1つの見つける機能を持った後述するDetectTestスクリプト(コンポーネント)をアタッチしています

隠れる障害物

Cubeでスケールを増やしています

見つけたい相手

Capsuleで、Character Controllerをアタッチしています
CharacterController内蔵のコライダーを使うので、CapsuleコライダーはRomoveしておきます

コード

テスト用のサンプルコードになります

これは、スクリプトと呼ばれていますが、実体はクラスですね
データの部分は、次のフィールド一覧で
振る舞いの部分は、メソッド一覧を確認してみましょう

フィールド一覧

フィールド名意味
Vector3playerTranチェック相手のTransform
RaycastHit[]raycastHitsRayを飛ばしたチェック相手のコライダーにヒットした情報(複数の相手の情報を配列で保存)
Vector3positionDiffチェック相手との距離の差のベクトル(3次元空間)
floatdistanceチェック相手との距離(いわゆるメートル)
Vector3directionチェック相手の方向(方向情報だけなので大きさは1:正規化と言います)
inthitCount相手との間のコライダーの数(相手のコライダーも含む)

メソッド一覧

Calcメソッド(計算)

positionDiff = playerTran.position - transform.position;

distance = positionDiff.magnitude;

direction = positionDiff.normalized;

hitCount = Physics.RaycastNonAlloc(transform.position, direction, raycastHits, distance);

DrawRayLineメソッド(デバッグ用のRayの描画)

チェックする相手にCharacterControllerコンポーネントがアタッチされています
これには、Colliderがアタッチされているのでコライダーカウントがされます(間違っている情報があるので気をつけましょう)

DrawRayメソッドは、シーンビューにRayを描画します
第1引数は、ラインを引く始点
第2引数は、方向のベクトル(長さも関係あり)
第3引数は、ライン色

hitCount が1の時、つまり、チェックしたい相手との間に障害物がないときは、緑で
障害物があるときは、赤で線を引きます

if (hitCount == 1)
{
    Debug.DrawRay(transform.position, positionDiff, Color.green);
}
else
{
    Debug.DrawRay(transform.position, positionDiff, Color.red);
}

OnGUIメソッド(Unityのイベントハンドラ)

ゲームビューにデバッグ情報を表示するために使います

DrawRayメソッドで、シーンビューに描画します
第1引数は、表示する四角形情報(Rect構造体は、四角形の始点(x)、四角形の始点(y)、幅、高さ)
第2引数は、表示文字列になります

Rect rect = new Rect(10, 10, 500, 20);
GUI.Label(rect, $"ヒットカウント: {hitCount}");

rect = new Rect(10, 30, 500, 20);
GUI.Label(rect, $"相手とのベクトル:{positionDiff}");

rect = new Rect(10, 50, 500, 20);
GUI.Label(rect, $"距離:{distance}");

rect = new Rect(10, 70, 500, 20);
GUI.Label(rect, $"方向:{direction}");

見つけたオブジェクトの名前をゲームビューに表示しています

for (int i = 0; i < hitCount; i++)
{
    rect = new Rect(10, columnCount, 500, 20);
    GUI.Label(rect, $"ヒットオブジェクト{i}:{raycastHits[i].transform.name}");
    columnCount += 20;
}

全体

using UnityEngine;
using System;

public class DetectTest : MonoBehaviour
{
    // チェック相手のTransform(欲しいのは、Position情報)
    [SerializeField]
    Transform playerTran;

    // Rayを飛ばしたチェック相手のコライダーにヒットした情報を配列で取得するための変数
    RaycastHit[] raycastHits = new RaycastHit[10];

    // チェック相手との差をベクトルで管理
    Vector3 positionDiff;

    // チェック相手との距離
    float distance;

    // チェック相手の方向情報(大きさ情報を無しにしています:つまり大きさは1)
    Vector3 direction;

    // 相手オブジェクトとの間のコライダーの数(Pivot間)
    int hitCount;

    void Update()
    {
        Calc();
        DrawRayLine();
    }

    private void Calc()
    {
        positionDiff = playerTran.position - transform.position;

        distance = positionDiff.magnitude;
        // distance = Vector3.Distance(playerTran.position, transform.position);

        direction = positionDiff.normalized;
        // direction = Vector3.Normalize(positionDiff);

        hitCount = Physics.RaycastNonAlloc(transform.position, direction, raycastHits, distance);
    }

    private void DrawRayLine()
    {
        if (hitCount == 1)
        {
            Debug.DrawRay(transform.position, positionDiff, Color.green);
        }
        else
        {
            Debug.DrawRay(transform.position, positionDiff, Color.red);
        }
    }

    void OnGUI()
    {
        Rect rect = new Rect(10, 10, 500, 20);
        GUI.Label(rect, $"ヒットカウント: {hitCount}");

        rect = new Rect(10, 30, 500, 20);
        GUI.Label(rect, $"相手とのベクトル:{positionDiff}");

        rect = new Rect(10, 50, 500, 20);
        GUI.Label(rect, $"距離:{distance}");

        rect = new Rect(10, 70, 500, 20);
        GUI.Label(rect, $"方向:{direction}");

        int rowCount = 90;

        for (int i = 0; i < hitCount; i++)
        {
            rect = new Rect(10, rowCount, 500, 20);
            GUI.Label(rect, $"ヒットオブジェクト{i}:{raycastHits[i].transform.name}");
            rowCount += 20;
        }
    }
}

おまけ

Unity

Posted by hidepon