質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.98%

Unity : 広範囲にRayを飛ばしたい(BoxCastを検討しています)

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,809

japomondo

score 13

 実現したいこと

Unityにおけるゲーム開発で、広範囲にRayを飛ばしたい

 詳細

Unityで3Dのゲームを開発しており、ユーザーが操作するプレイヤーをNPCが探し回る、という内容を考えています。

通常時は決まったルートを巡回しているNPCが、自身の子オブジェクトから発するRayがプレイヤーのコライダにヒットした場合、追跡モードに切り替わる、という仕様を考えています。

当初はRayCastを複数発することにより実現できました。しかしながら、RayCastは処理が非常に重いと判断したため、別の方法を模索し始めました。そこでBoxCastの存在を知り色々と試してみたのですが、思うように実現することができなかったためご相談させていただいております。

実現したい内容は以下のイメージです(上空からの視点)。カメラコンポーネントが付与されているオブジェクトがプレイヤー、オレンジ色のオブジェクトがNPCで、NPCから長方形のBoxCastを発したいと考えています。

なお白い線は、NPCの子オブジェクトにアタッチした下記のコードによるものです。

イメージ説明

public class Boxcast : MonoBehaviour
{
    Transform myTransform;
    RaycastHit hit;

    public float rayLength = 15/2f;
    public Vector3 rayRange = new Vector3(8,0.1f,15);

void OnDrawGizmos()
    {
        Gizmos.DrawWireCube (myTransform.position + myTransform.forward * rayLength, rayRange);
    }
}

長方形以内にプレイヤーがいた場合コライダがヒットし、NPCを追跡モードに切り替える、という仕様を想定しています。同クラスに下記のコードを書いているのですが、1枚目のイメージどおりに実装できておりません。

    void FixedUpdate ()
    {
        Ray();
    }

    void Ray ()
    {
        RaycastHit hit;

        if (Physics.BoxCast (myTransform.position + myTransform.forward * rayLength, rayRange, myTransform.forward, out hit, myTransform.rotation, 0))
        {
            if (hit.collider.tag == "Player")
            {
              // 追跡を開始するコード 
            }
        }
    }

以上の内容にて、解決策がお分かりの方がいらっしゃいましたら、ご助言いただけますと大変助かります。なお、BoxCastに固執しているわけではありませんので、他の実現方法をご存知という場合でもコメントいただけますと幸いです。

説明が長くなり恐縮です。

 参考にした情報

https://docs.unity3d.com/jp/540/ScriptReference/Physics.BoxCast.html
http://tsubakit1.hateblo.jp/entry/2016/02/25/025922

 追記(コードのみ記載)

using UnityEngine;
using System.Collections;
using UniRx;
using UniRx.Triggers;

public class Boxcast : MonoBehaviour
{
    Transform myTransform;

    RaycastHit hit;

   // Rayボックスの各軸の半分のサイズ
    public Vector3 rayRange = new Vector3(5, 0.05f, 0.05f);

   // Rayの最大距離
    public float rayLength = 15;

    GameObject player;

    void Start ()
    {
        myTransform = transform;

        player = GameObject.FindWithTag("Player");

      // コライダがヒットした場合、追跡モードに切り替え
        this.UpdateAsObservable()
                .Where(_ => Physics.BoxCast (myTransform.position, rayRange, myTransform.forward, out hit, myTransform.rotation, rayLength) && hit.collider.tag == "Player")
                .Subscribe(_ => Ray());
    }

  void Ray ()
  {
        // 追跡を開始するコードを記載
  }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

追跡者の前方に位置する直方体を距離0で射出することで、結果的に図中の白い長方形の範囲をスキャンしようという意図かと思いますが、ご提示いただいた【Unity】SphereCastやBoxcastで、球や箱のあたり判定が通過できるか判断する - テラシュールブログによると

 BoxCastとSphereCastの死角

BoxcastSphereCastを利用する上での注意点として、開始地点を含まないといった物があります。

20160225023229.jpg

例えば下のように接触対象がめり込んでいる場合、判定の範囲外になるのでスルーして上まで線が延びてしまっています。

20160225094450.jpg

これを回避するために各Castの発信位置を下げるのはあまり面白くないので、判定の内側はCheckSphereCheckBox、もしくはOverlapBoxOverlapSphereのようなAPIで埋めてしまうのが良いかなと思います

とのことですので、おそらくご提示の図の白い長方形内は判定範囲外になってしまい、ヒットを検出できないのではないかと思います。

サイトで言及されているCheckBoxまたはOverlapBoxによる方法に変更してみてはいかがでしょうか?

※CheckBoxやOverlapBoxとレイキャスティングを複合させるのも面白そうです。たとえば...

  • CheckBoxでプレイヤーのレイヤーを対象に判定を行い、白長方形内にプレイヤーが存在するか判定する。
  • プレイヤーの存在が検出されたら、「追跡者forward」ベクトルと「プレイヤーposition - 追跡者position」ベクトルの成す角度を調べ、これが一定以下なら「プレイヤーが追跡者の視界内にいる」と判定する。
  • プレイヤーが視界内にいれば、次にプレイヤー周辺を狙って数本のレイを射出しプレイヤーとヒットするか判定する。このとき、対象レイヤーは建物など他オブジェクトのレイヤーも含めるようにして、プレイヤーが他オブジェクトの背後にいればレイとヒットしないようにする。
  • レイがプレイヤーとヒットしたら、追跡者を追跡モードに切り替える。

こういった多段階判定なら、レイキャストの回数を節約しつつ「プレイヤーが物陰に隠れていれば追跡者には見つからない」というような精密な視界判定ができそうです。

 追記

なるほど、BoxCastがそのように設定されていればうまくいきそうですね。
複数レイが1本のBoxCastに置き換わったことで、もしかすると下図のようにプレイヤーにとって甘めの判定になってしまうかもしれませんが、ゲームデザインによっては(プレイヤーを見逃しても構わない、あるいはそもそも図のような状況にはならないようにしてある、など)問題ないかと思います。
BoxCast

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/26 11:18

    プレイヤーの起立時としゃがみ時を考慮して2つのBoxCastを飛ばすことにしたので、上下の高さに関しては対応できているのですが、それでもBongo様ご指摘のような場合にはプレイヤーを捉えられないですね。。

    上下それぞれ今より横幅が短いものを2つずつBoxCastを飛ばせば精度は上がりそうですが、それだと4つになってしまって結局通常レイを複数飛ばすのと負担量は変わらなそうですし、ひとまずルートを工夫するしかないのかなぁと思っています。

    キャンセル

  • 2018/01/26 11:49

    負荷と正確さのバランス取りはなかなか難しいですね。
    他の思い付きとしては「追跡者の正面に大きなボックスをキャストする」代わりに、「追跡者の向きはひとまず考慮せず、追跡者の位置からプレイヤーの方角へ小さなボックス(またはスフィア)をキャストする」「もしプレイヤーにヒットしたら、さらに追跡者から見たプレイヤーの距離・方角から追跡者の前方にいるかを判定する」なんていうのもアリかと思いますが、どうでしょう。

    ※「BoxCastによる可視判定」と「位置関係による前方判定」のどちらが高負荷になるかちゃんと調べたわけではないですが、順序を逆にして「位置関係による前方判定」→「BoxCastによる可視判定」にした方が、前方判定ではじかれた場合はBoxCastが行われなくなるので、負荷軽減になるかもしれないですね。

    キャンセル

  • 2018/01/26 12:32

    なるほどですね、そもそもですがUniRxを使って判定するようにしたため負荷量は小さくなっているはずなので、どの方法が最適か色々調べてみたいと思います。懇切丁寧にアドバイスいただき感謝いたします。

    キャンセル

+1

つまりは「監視エリアを設定したい」ということかなと理解したのですが、あってますでしょうか?

もしそれであれば、該当位置に「is Trigger」をオンにしたBoxColliderを持つ不可視オブジェクトを該当箇所に配置しておき、そのオブジェクトにユーザが「触れた」瞬間に追跡を開始、監視エリアからユーザが離脱したら追跡モードを終了、としたら実現できるかなと思ったのですがいかがでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/25 09:07

    追跡者の子として前方にトリガー領域を持たせれば、追跡者の移動に合わせて判定領域も動いてくれるので、CheckBox領域を自前で求めるよりもコードがシンプルになって良さそうですね。

    キャンセル

  • 2018/01/25 11:19

    コメントいただき誠にありがとうございます。少々お返事が遅れてしまいそうなので、取り急ぎお礼申し上げます。

    キャンセル

  • 2018/01/25 11:30

    edo_m18様、「監視エリアを設定したい」というご認識で間違いありません。

    ただ、「is Trigger」をオンにしたBoxColliderで実現しようとすると、例えばプレイヤが車の陰に隠れている場合などでも、車を通り越してプレイヤのコライダにヒットしてしまい追跡モードがオンになってしまうため、今回は最適ではないと判断したのでした。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.98%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる