【Unity】スライムが複数いると追跡をやめる原因と対処法|Raycastとレイヤー設定を理解しよう

広告

こんな現象に心当たりはありませんか?

Unityでアクションゲームを作っているとき、敵のスライムを複数体置いたところ、次のような動きになりませんでしたか。

  • スライムがプレイヤー(クエリちゃん)を追いかけている途中で、急に立ち止まる
  • スライムが1体だけのときは、ちゃんと追いかけてくる
  • 他のスライム(味方)が近くにいるときに限って、追跡が止まる

スクリプトにミスは見当たらないのに、複数体にしたとたんに起きる――このようなバグは、実はスクリプトではなく、レイヤーの設定が原因です。この記事では、原因をたどり、初学者の方でも理解して直せるように解説します。

取り上げるのは、敵がプレイヤーを索敵して追いかける、次のような構成です。

  • 索敵エリア:スライムの子オブジェクト CollisionDetector(半径4のトリガーSphere Collider)
  • 追跡処理:EnemyMove.OnDetectObject(索敵エリアに入った相手が Player タグなら、NavMeshAgentで追いかける)

ポイント1 原因は「視線チェックのRaycastが、他のスライムの索敵エリアに当たっている」

スライムは、プレイヤーを見つけると、間に障害物がないかを Physics.RaycastNonAlloc で確かめてから追いかけます。問題の箇所は次のコードです。

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

if (hitCount == 0)
{
    // 間に何もない → 追いかける
    _agent.isStopped = false;
    _agent.destination = collider.transform.position;
}
else
{
    // 何かに当たった → 見失ったとみなして停止
    _agent.isStopped = true;
}

このコードは「レイが1つでも何かに当たれば、障害物ありと判断する」作りです。ポイントは、何に当たるかを決めるのが raycastLayerMask である点です。

このプロジェクトでは、マスクは Default レイヤーのみになっていました。一方で、索敵エリアを担当する子オブジェクト CollisionDetector も Default レイヤーのままでした。

さらに、Raycastはトリガー(Is Trigger が ON の Collider)にも当たるという初期設定です(Physics.queriesHitTriggers がデフォルトで ON)。つまり、レイが他のスライムの索敵球を横切ると、「間に障害物がある」と判定されてしまいます。

ポイント2 なぜ「複数体のときだけ」起きるのか

流れを順に追うと、理由が見えてきます。

  1. スライムAがプレイヤーを見つけ、プレイヤーに向けてレイを飛ばす
  2. そのレイが、近くにいるスライムBの索敵球(半径4・Defaultレイヤー・トリガー)を横切る
  3. マスクに Default が含まれ、トリガーも無視されないので、レイが当たる
  4. hitCount が 0 でなくなり、「見失った」扱いでスライムAが停止する

なお、スライムA自身の索敵球には当たりません。レイの始点が索敵球の内側にあるためです。そのためスライムが1体だけのときは問題が表に出ず、複数体置いて初めて症状が見えるのです。スライム同士が近づくほど、起きやすくなります。

ポイント3 対処:索敵エリア専用のレイヤーを作る

解決策は、索敵エリアを「レイが当たらないレイヤー」に移すことです。スクリプトは変えず、エディタの設定だけで直ります。索敵エリア専用のレイヤーを作る方法を、手順で紹介します。

手順A レイヤーを追加する

  1. メニューから Edit > Project Settings > Tags and Layers を開く
  2. Layers を開き、空いているスロットに EnemyMoveTargetDetector と入力する

手順B 索敵エリアにレイヤーを割り当てる

  1. スライムのプレハブを開く
  2. 子オブジェクト CollisionDetector を選択し、Inspector上部の Layer を EnemyMoveTargetDetector に変更する
  3. このモデルをバリアントやコピーで増やしている場合(大きいスライム、小さいスライムなど)は、それらも同じ設定になっているか確認する

手順C Layer Collision Matrix を整える

専用レイヤーを作ったら、Edit > Project Settings > Physics の Layer Collision Matrix で、どのレイヤーと当たり判定をするかを決めます。

  • EnemyMoveTargetDetector の行・列を、Player とだけ ON にする
  • このレイヤーと他のレイヤーの組み合わせは、すべて OFF にする

マトリクスは「どのレイヤー同士で当たり判定を行うか」の一覧表です。センサー(索敵エリア)は、探したい相手の Player とだけ当たればよい、と役割から考えると理解しやすくなります。このプロジェクトでは、攻撃判定も同じ考え方です。

レイヤー役割ON にする相手
EnemyMoveTargetDetector敵の索敵センサーPlayer のみ
EnemyAttack敵の攻撃判定Player のみ
PlayerAttackプレイヤーの攻撃判定Enemy のみ

これで直る理由

  • Raycast Layer Mask は Default のままで、索敵エリアはマスクの対象外になるため、レイが当たらなくなる
  • 地形や壁など実体のある障害物(Defaultレイヤー)は、これまでどおり視線を遮る
  • 索敵エリアは Player とだけ当たるので、プレイヤーを見つける動作はそのまま動く

ポイント4 コードでさらに安全にする:QueryTriggerInteraction.Ignore

レイヤー設定で直る方法は、「索敵エリアのレイヤーが、マスクに含まれていないこと」を前提にしています。この前提はコードのどこにも書かれていないため、後からレイヤーを変更したときに同じバグが再発する可能性があります。

そこで、Raycastの最後の引数に QueryTriggerInteraction.Ignore を渡して「視線チェックではトリガーを無視する」と宣言しましょう。

// 視線チェックでは索敵エリアなどのトリガーを無視し、実体のある障害物だけを対象にする
var hitCount = Physics.RaycastNonAlloc(
    transform.position, direction, _raycastHits, distance,
    raycastLayerMask, QueryTriggerInteraction.Ignore);

引数は、順に「始点、方向、結果を入れる配列、距離、レイヤーマスク、トリガーの扱い」です。

値動き
Ignoreトリガーを無視する(今回はこれ)
Collideトリガーにも当たる
UseGlobal省略時の既定値。プロジェクト設定の Queries Hit Triggers に従う(デフォルトで ON)

これで「レイヤー設定」と「コード」の二重の守りになり、レイヤー設定を変更しても追跡が止まりにくい構造になります。地形や壁など、トリガーではない Collider は、これまでどおり視線を遮ります。

ポイント5 とりあえず直したいときは Enemy レイヤーでもOK

新しいレイヤーを作るのがやや大がかりに感じる場合は、CollisionDetector の Layer を、スライム本体と同じ Enemy にするだけでも直ります。

  • 新しいレイヤーも、Layer Collision Matrix の追加設定もいらない
  • Enemy と Player の組み合わせは ON なので、プレイヤーを見つける動作もこれまでどおり
  • Raycastのマスクは Default のままで、索敵エリアがマスクの対象外になる

索敵エリアはセンサーですので、役割で分けた専用レイヤー(ポイント3)のほうが、設計としてはすっきりします。まずは Enemy で動かして理解し、慣れたら専用レイヤーに進む、という順序もおすすめです。

よくある勘違い:Physics Materialやマトリクスのせい?

原因を探すときに疑いやすい、次の2点は今回のバグとは関係ありません。

  • Physics Material:摩擦や反発を決める設定です。トリガー判定やレイキャストには影響しません
  • Layer Collision Matrix:Collider同士の衝突とトリガーの発生だけを決めます。Raycastはマトリクスを見ず、コードで渡すレイヤーマスクだけで当たる相手を決めます

「衝突の設定」と「レイの設定」は別物と覚えておくと、トラブルシュートが速くなります。

動作確認のポイント

  1. シーンにスライムを、3体以上近くに置く
  2. プレイヤーをスライムの索敵範囲に入れ、追いかけてくることを確認する
  3. 他のスライムが間に入っても、追跡が止まらないことを確認する
  4. 壁や箱(Defaultレイヤーのコライダー)の裏に隠れたときは、これまでどおり追跡が止まることも確認する

まとめ

  • 現象:スライムを複数体置くと、追跡中に立ち止まる
  • 原因:視線チェックのRaycastが、他のスライムの索敵エリア(Defaultレイヤー・トリガー)に当たっていた
  • 対処:索敵エリアを専用レイヤーに分け、マトリクスで Player とだけ ON にする(簡易版は Enemy レイヤー)
  • さらに安全に:QueryTriggerInteraction.Ignore で、視線チェックからトリガーを外す

レイヤーは「誰と誰が当たるか」を整理する道具です。スライムを増やすときは、最初から索敵エリアに専用レイヤーを割り当てる習慣をつけておくと、同じトラブルを防げます。

関連記事

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

広告

Collider,NavMesh,Ray,Unity

Posted by hidepon