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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

Q&A

2回答

5826閲覧

【Unity 3D】複雑な形状のメッシュでIs Triggerを使いたい

bullet_3sd8

総合スコア6

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

0グッド

1クリップ

投稿2020/12/14 17:09

編集2020/12/15 00:28

前提・実現したいこと

イメージ説明

画像のように、地図上の色分けされたエリアごとにトリガーを設定し、
地図上を歩いているプレイヤーが現在何色のエリアに居るのかを表示したいです。
(実行中はトリガーの色は非表示にします)

イメージ説明

イメージとしてはこんな感じに、3Dマップ上を歩いている画面の左上に
『現在のエリア:○○(ex. 黄色のエリア、赤色のエリア)』という風にテキストを出したいです。

ですが、このように複雑な形状のトリガーの当たり判定を設定する方法がわかりません。

試したこと

まず、黄色、ピンク色、赤色のエリアの形状ごとに透過png画像を作り
Planeに貼り付けてMesh Colliderを設定しました。

イメージ説明

しかし、透過した部分にも当たり判定は存在し、どこに行っても全てのトリガーが反応してしまいました。
(それが一番上の画像です。コライダーの判定がマップ外枠をぐるっと囲んでいます。)

イメージ説明

次に、Blenderを用いてそれぞれ立体にして(先ほど使ったpng画像からLogo Trackerと押し出しを用いて作りました。)
fbx形式で出力してUnityに読み込み、Mesh Colliderを設定しました。

しかし、Convexをオンにすると画像のように当たり判定が正確ではなくなってしまいます。

ピンクのエリアにいるのに黄色のエリアの近くに行くと黄色のトリガーが反応します。

Unity3DにてPlaneに透過PNG画像を貼った際に、非透過部分のみトリガーを有効にする方法
または、Mesh ColliderでConvexを有効にせずにIs Triggerを使う方法を、
どちらも難しいのであれば、実現するための別の方法をお教え頂ければと思います。

Unityを触って日が浅い初心者なので、初歩的な質問かもしれませんが、
お答えいただけると幸いです。よろしくお願いいたします。

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

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

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

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

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

guest

回答2

0

lazhさんの3つの案に加えて、今回のような状況ではRaycast案のバリエーションとして下記スクリプトのような手が使えるかもしれません。

C#

1using System; 2using System.Linq; 3using UnityEngine; 4 5public class PlayerController : MonoBehaviour 6{ 7 // エリアの定義 8 [SerializeField] private AreaDefinition[] areaDefinitions = 9 { 10 new AreaDefinition {name = "Area R", color = new Color32(255, 0, 0, 255)}, 11 new AreaDefinition {name = "Area Y", color = new Color32(255, 255, 0, 255)}, 12 new AreaDefinition {name = "Area G", color = new Color32(0, 255, 0, 255)}, 13 new AreaDefinition {name = "Area C", color = new Color32(0, 255, 255, 255)}, 14 new AreaDefinition {name = "Area B", color = new Color32(0, 0, 255, 255)}, 15 new AreaDefinition {name = "Area M", color = new Color32(255, 0, 255, 255)} 16 }; 17 18 // ここに上記areaDefinitionsの各エリアの色と同じ色を使って描いた 19 // エリアマップテクスチャをセットしておく 20 // なお、GetPixels32を使いたいので「Read/Write Enabled」を 21 // オンにしておく必要がある 22 [SerializeField] private Texture2D areaMapTexture; 23 24 // ここにPlaneオブジェクトをセットしておく 25 // なお、textureCoordを取得したいので「Convex」はオフにしておく 26 [SerializeField] private MeshCollider areaMapCollider; 27 28 // 後述のRaycastのときに、プレイヤーの座標からレイ起点をどれだけずらすかを設定する 29 [SerializeField] private Vector3 raycastOriginOffset = new Vector3(0.0f, 10.0f, 0.0f); 30 31 private int[] areaMap; 32 private Vector2Int areaSize; 33 private int currentAreaIndex; 34 35 private void Start() 36 { 37 // 色からエリア番号を求める対応表を作り... 38 var colorToIndex = this.areaDefinitions.Select((area, i) => (area, i)).ToDictionary(area => area.area.color, area => area.i); 39 40 // エリアマップテクスチャの色をエリア番号に置き換えた配列を作っておく 41 this.areaMap = this.areaMapTexture.GetPixels32().Select(color => colorToIndex[color]).ToArray(); 42 43 // エリアマップの大きさを覚えておく 44 this.areaSize = new Vector2Int(this.areaMapTexture.width, this.areaMapTexture.height); 45 } 46 47 private int GetAreaIndex(Vector3 position) 48 { 49 // Planeに対してRaycastを行い... 50 if (this.areaMapCollider.Raycast( 51 new Ray(position + this.raycastOriginOffset, Vector3.down), 52 out var hitInfo, 53 float.PositiveInfinity)) 54 { 55 // hitInfo.textureCoordをもとにareaMapからエリア番号を引いてきて返す 56 var pixelCoord = hitInfo.textureCoord * this.areaSize; 57 var x = Mathf.Clamp(Mathf.FloorToInt(pixelCoord.x), 0, this.areaSize.x - 1); 58 var y = Mathf.Clamp(Mathf.FloorToInt(pixelCoord.y), 0, this.areaSize.y - 1); 59 return this.areaMap[(y * this.areaSize.x) + x]; 60 } 61 62 // ヒットしなければ-1とする 63 return -1; 64 } 65 66 private void Update() 67 { 68 this.currentAreaIndex = this.GetAreaIndex(this.transform.position); 69 } 70 71 private void OnGUI() 72 { 73 GUI.color = Color.black; 74 GUILayout.Label(this.currentAreaIndex < 0 ? "???" : this.areaDefinitions[this.currentAreaIndex].name); 75 } 76 77 [Serializable] 78 public struct AreaDefinition 79 { 80 public string name; 81 public Color32 color; 82 } 83}

areaMapTextureとして下図テクスチャを使ったところ...

図1

下図のように現在プレイヤーがいるエリアを判定することができました。

図2

念のため申し上げますと、上図ではエリアをわかりやすくするためPlaneに色付けしていますが、Planeのマテリアルは何であってもかまいません。

図3

投稿2020/12/16 10:56

Bongo

総合スコア10807

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

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

0

簡単に調べた感じではたぶんどっちも難しい気がします
とりあえず自分がパっと思いつくものは下の3つです
3が一番確実かなと思いますね面倒ですが

(1) メッシュに合わせてコライダーを自動作成してくれるSAColliderBuilder【Unity】【アセット】
↑みたいなアセットを探して使う(綺麗にCollider貼れるかわからないが上手くいったらラッキー)

(2) Raycastを使う。Convexをオフにしたエリアモデルを適当にY-100ぐらいに下げて、Raycastのoriginにはプレイヤーのポジションを取得してY座標だけ-90とかに変えたものを、directionにはVector3.down(下方向)を入れてRayを飛ばしてヒットしたもののタグ等で判定する

C#

1 [SerializeField] private Transform player; 2 3 private void Update() { 4 Vector3 origin = player.position; 5 origin.y = -90; // <- エリアがy-100だとしたら 6 7 if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit)) { 8 if (hit.transform.CompareTag("YellowArea")) { 9 Debug.Log("現在のエリア:黄色のエリア"); 10 } 11 } 12 }

(3) エリアモデルを凹がないように分割していったものにMeshColliderを貼る

投稿2020/12/15 16:16

lazh

総合スコア300

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問