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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

9155閲覧

Physics.CheckSphereで可視化したり、ヒットしたオブジェクトの情報を取得。

退会済みユーザー

退会済みユーザー

総合スコア0

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2018/11/23 07:23

編集2018/11/24 02:47

前提・実現したいこと

Physics.CheckSphereというメソッドを試してみましたが、これはシーン上で、その検知範囲の球を可視化することってできませんか?
リファレンスを見ると、それらしき引数が見つかりませんでした。
このままだと、とてもテストしづらいなと思いました。

以下、2点質問させてください。

1点目:
ゲーム画面では検知範囲の球が表示されず、シーン上だけで検知範囲の球を表示させる方法ってありませんか?

2点目:
球の検知範囲内にヒットしたオブジェクト情報の取得ってできませんか?
OnCollisionEnterみたいにCollision等取得できませんか?
ヒットしたオブジェクトの条件により、処理の分岐はできないか知りたいです。

試したこと

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Sample : MonoBehaviour { 6 7 [SerializeField] 8 float sphereRadius; 9 10 void Start () { 11 } 12 13 void Update () { 14 if(Physics.CheckSphere(this.transform.position, sphereRadius)){ 15 Debug.Log("hit"); 16 } 17 } 18} 19

追記。

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Sample : MonoBehaviour { 6 7 [SerializeField] 8 float sphereRadius = 3.0f; 9 Vector3 center; 10 11 // Use this for initialization 12 void Start () { 13 center = this.transform.position; 14 } 15 16 // Update is called once per frame 17 void Update () { 18 Collider[] hitColliders = Physics.OverlapSphere(center, sphereRadius); 19 int i = 0; 20 while (i < hitColliders.Length) 21 { 22 Debug.Log(hitColliders[i].gameObject); 23 i++; 24 } 25 } 26 27 void OnDrawGizmos(){ 28 Gizmos.color = Color.yellow; 29 Gizmos.DrawWireSphere(center, sphereRadius); 30 } 31}

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

まず、球状の領域の可視化にはGizmos.DrawWireSphere、またはGizmos.DrawSphereを使うのが妥当ではないでしょうか。Gizmosには他にも色々なシーンビュー上への描画メソッドがあり、デバッグ目的には便利かと思います。

衝突相手の情報を得るのでしたら、Physics.CheckSphereの代わりにPhysics.OverlapSphere、またはPhysics.OverlapSphereNonAllocを使ってみてはいかがでしょう。
ただ、球領域と接触するコライダーは得られるものの、OnCollisionEnterのような衝突に関する詳しい情報は得られないのは難点かもしれませんね。どちらかというとOnTriggerEnterに近い動作になるかと思います。

#コメントについて追記
1点目
空のオブジェクトでも描画されるはずですが、なぜでしょうかね...?
可能性としては、このSampleスクリプトでは、ギズモ描画位置の決定にフィールドcenterを使っています。このcenterにオブジェクトの座標がセットされるのはStartメソッドが実行された後ですので、シーン編集中の段階ではcenterに座標がセットされず(0, 0, 0)のままとなり、ワールド原点に球が描画されてしまっている...ということがありそうな気がします。ワールド原点がシーンビューの画面外に出てしまっている場合、一見何も描画されなかったように見えてしまうかもしれません。
ワールド原点に球が描画されているか確認してみたり、あるいはプレイボタンを押して実行してみてください。プレイモードにするとcenterにオブジェクトの座標がセットされ、オブジェクトの位置に球が現れるかと思います。
もし、プレイモードにしてもやっぱりオブジェクトの位置に球が現れないようなら、なにか他の原因でしょうね...その場合はコメントいただければ、さらに調査してみようと思います。

2点目
1点目とも関連している話ですが、centerがセットされるのはStart実行時の一回きりです。ですので実行中にオブジェクトを動かそうが、球の位置はStart時の位置から変わらないはずです。
もし常にオブジェクトの座標に追従させたいのでしたら、そもそもcenterを覚えておくのはやめて、常に最新の位置を球の中心にしてしまってはいかがでしょうか?

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Sample : MonoBehaviour 6{ 7 8 [SerializeField] 9 float sphereRadius = 3.0f; 10 // Vector3 center; // centerは廃止 11 12 // Startも不要なので削除 13 /* 14 // Use this for initialization 15 void Start() 16 { 17 center = this.transform.position; 18 } 19 */ 20 21 // Update is called once per frame 22 void Update() 23 { 24 Collider[] hitColliders = Physics.OverlapSphere(transform.position, sphereRadius); // 常にtransform.positionを見るよう変更 25 int i = 0; 26 while (i < hitColliders.Length) 27 { 28 Debug.Log(hitColliders[i].gameObject); 29 i++; 30 } 31 } 32 33 void OnDrawGizmos() 34 { 35 Gizmos.color = Color.yellow; 36 Gizmos.DrawWireSphere(transform.position, sphereRadius); // 常にtransform.positionを見るよう変更 37 } 38}

3点目
OnTriggerEnterに引数として渡ってくるのは相手のColliderだけですが、OnCollisionEnterの場合に渡ってくるのはCollisionです。このオブジェクトには色々な情報が入っていて、相手のgameObjectcolliderrigidbodytransform、他にも...

接触した点についての情報が入っている。これらContactPointから、どの地点でどの向きに接触したか、めり込みの程度はどれだけか...といったことを調べられる。

めり込みを押し戻す力積を調べられる。特殊な跳ね返りを表現するのに使えるでしょうかね...?
CollisionではなくCollision2Dですが、Unity - Unityでbouncinessを変更する方法|teratailでは反発係数の変化を表現するのに衝突時の力積を使用しました。

相手と自分の相対速度を調べられる。キャラクター同士がぶつかった速度に応じてダメージ量を変動させる...なんて表現に使えるかもしれません。

といったことを調べることができます。難点かも...などと書いたものの、主に物理的応答と関連した情報なので、単に範囲内に入ったかどうかを検知する上では役に立つ場面は少なそうですね。

投稿2018/11/23 09:49

編集2018/11/24 23:00
Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/11/24 03:28

ご回答ありがとうございます。 追記のようにコードを試してみました。 すみません、3点質問があります。 1点目。 Gizmos.DrawWireSphereを試したのですが、スクリプトを空のゲームオブジェクトにアタッチすると描画されなかったのですが、空のゲームオブジェクトでは描画されないのでしょうか? キューブにアタッチしたら球が描画されました。 2点目。 追記のコードをキューブにアタッチしても、ゲーム実行中にキューブを動かしたら、描画の球の位置は変わらないまま(取り残されたまま)で、移動に伴ってキューブの位置に描画が更新されませんでした。 位置の更新ってできませんか? 半径は動的に変えることができました。 3点目。 追記のコードで接触したコライダーは取得できましたが、 >OnCollisionEnterのような衝突に関する詳しい情報は得られない 得られない詳しい情報とはどういうものですか? OnCollisionEnterとOnTriggerEnterは、接触した相手がトリガーかどうかの違いしか分かっていないため、OnCollisionEnterとOnTriggerEnterで、詳しい情報が得られるのと得られないとの違いも分かっていないのでご教示いただきたいです。
退会済みユーザー

退会済みユーザー

2018/11/25 07:21

ご回答ありがとうございます。 1点目。 下記のように修正しましたが、空のゲームオブジェクトは球が描画されませんでした。 可能でしたら、こちら原因についてご教示いただけませんか? void OnDrawGizmos(){ Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(this.transform.position, sphereRadius); } 2点目。 1点目のコードで空のゲームオブジェクトの球はスタート時も移動時も描画されませんでしたが、 同じコードで、キューブはスタートと移動更新で描画されるようになりました。 ありがとうございます。 3点目。 OnTriggerEnterの引数はColliderで、OnCollisionEnterの引数はCollisionだったのですね。 両方ともコライダーが渡ってくるものと勘違いしていました。 ご提示いただいたCollisionの情報、とても便利ですね。 衝突具合で色々な調整や表現ができそうです。
Bongo

2018/11/25 07:40

球が表示されない件についておうかがいしますが、その空のゲームオブジェクトをインスペクタで見てみたとき、スクリプトのコンポーネントは折り畳まれて(スクリプト名の左の三角形が右を向いて)いるでしょうか? リファレンスによると、その場合はOnDrawGizmosは動作しないそうですが...
退会済みユーザー

退会済みユーザー

2018/11/25 08:19

ご回答ありがとうございます。 失礼しました、空のゲームオブジェクトにアタッチしているスクリプトが古くて、 sphereRadiusが0に設定されている為、表示されませんでした。 下記のように設定した所、空のゲームオブジェクトでも表示されました。 [SerializeField] float sphereRadius = 3.0f; また、スクリプトのコンポーネントが折り畳まれている場合、表示されなくなることも確認致しました(これもハマると気づかない挙動ですね)。 ありがとうございます。 最後に1点ご意見いただきたいのですが、 Physics.CheckSphereを使うメリットってありますか? Physics.OverlapSphereかPhysics.OverlapSphereNonAllocのほうがコライダーが取得できて、検知したコライダーにより条件分岐できて使い勝手が良さそうなのですが。 Physics.CheckSphereを使うとしたら、何のコライダーが入ってきたかは分からないが、とにかく何者かのコライダーが入ってきたかのtrue,falseだけ取得、Physics.OverlapSphereより負荷が少ない、ぐらいの用途とメリットしか思い浮かばないのですが。
Bongo

2018/11/25 10:04

なるほど、解決されたようで安心しました。 CheckSphereの最大の利点は速さなんだろうと思います。気になったので対決させてみたところ、729個の球の群れに対して1048576回...約100万回のヒットテストを行った場合、CheckSphereは2.08秒で終わったのに対し、OverlapSphereは254秒、OverlapSphereNonAllocもほぼ同等の249秒となりました。 多分、OverlapSphereが領域内のすべてのコライダーを見つけなければならないのに対し、CheckSphereは一つでもコライダーが見つかった時点で処理を終えることができるため、このような100倍以上の差が付いたのではないでしょうか。
退会済みユーザー

退会済みユーザー

2018/11/25 13:18

ご回答ありがとうございます。 ありがとうございます、スピードで圧倒的な差があるのですね。 色々なことをご教示いただき、本当にありがとうございます。 すみません、さらに1点、気になってしまったのですが、 プレイヤーを中心に球を張り、球内に入った敵キャラを検知するという仕組みとか、よく使えるパターンだと思うのですが、この場合、プレイヤー自身も検知してしまうので、ゲームオブジェクトにタグを付けて、 if(hitColliders[i].gameObject.tag != "Player"){敵キャラや他のコライダーに関する処理} みたいな条件分岐をすればよいかと思ったのですが、 プレイヤー自身が入れ子構造のゲームオブジェクトで親にも子にもコライダーがあった場合、 プレイヤーに関するコライダーが付いたゲームオブジェクト全てにPlyaerタグを付けるよりも、 そのゲームオブジェクトのルートオブジェクト(最上位オブジェクト)のタグがPlayerかどうか判定するほうが効率がよいかと思いました。 しかし、その子オブジェクトの何階層上がルートオブジェクトに当たるかわからないので、任意のオブジェクトのルートオブジェクトの取得は、それなりの工夫が必要ですよね? 敵キャラの検知にしても、敵の指先のコライダーが球内にヒットしたら、敵のルートオブジェクトのタグやゲームオブジェクト名が知りたくなると思うので、やはり任意のオブジェクトのルートオブジェクトを取得する術が必要なのかと思いました。 すみません、長くなってしまいそうなので、こうコメントしておきながら、この疑問点は他で質問を設けるかもしれません。ただ、次の疑問点と課題を残しておきたくコメントしました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問