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

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

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

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

Q&A

解決済

1回答

1959閲覧

unity 地面までの方向ベクトルを表示させたい

eggpol

総合スコア60

Unity

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

0グッド

0クリップ

投稿2019/05/03 17:30

編集2019/05/03 17:38

やりたいこと↓
イメージ説明

カメラからみたターゲットから地面(X-Z平面)までの方向ベクトルを取得しUIに矢印として表示させたいです。
Vectro3.downで取得した方向を2Dに変換すればいいのは分かりますが具体的な方法が分かりません。
画面に表示する矢印はなるべく端に表示させたいです。

よくわからないの教えて下さい。

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

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

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

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

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

bochan2

2019/05/04 02:54

地面とはX-Z平面ですか?それとも凸凹のあるメッシュですか?
eggpol

2019/05/04 04:08

X-Z平面です。地面がメッシュの場合は想定しておらず気づきませんでした。 メッシュのほうが難しそうですね
ozwk

2019/05/08 06:16

カメラが真下/真上を向いている場合矢印はどうしますか?
eggpol

2019/05/08 12:26

カメラが真下/真上を向いている場合 これは全く想定していませんでした。 ちょっと考えてみたんですが カメラが真下/真上を向くと点になるんですかね。 Bongoさんの考えを借りると、真上だと矢印がつぶれるように見えて、真下だと伸びるように見えるということでしょうか。
Bongo

2019/05/08 19:40

私の場合ですと、視点が真上や真下に近づくほど矢印の先端はグレーの円の中心に近づいていき(つまりどんどん潰れていく)、本当にぴったり真上・真下を向いている場合は矢印が消えるようにしたつもりです。逆に、視点が地面と平行の時に矢印の長さが最大になり、先端は円の外周を指すはずです。
eggpol

2019/05/09 14:05

Bongoさんのコードをもとに挙動を確認しようとしたのですが Assets/teach2/Graphic.cs(106,22): error CS0019: Operator `*' cannot be applied to operands of type `UnityEngine.Vector3' and `UnityEngine.Vector2' と Assets/teach2/Graphic.cs(114,21): error CS0019: Operator `*' cannot be applied to operands of type `UnityEngine.Vector3' and `UnityEngine.Vector2' とエラーが出てしまいました。 var p = (vertex * headScale) + headOffset; と var p = vertex * shaftScale; がだめらしいです。 どうすればいいですか・・・?
Bongo

2019/05/09 17:13

型変換してくれませんでしたか、Unityのバージョンによる違いでしょうかね...? コードの最初の方にあるheadVerticesとshaftVerticesですが、配列の型が方法検討中のなごりでVector3[]になっていました。 これらはVector2[]の方が適切でしたので、そのようにコードを変更してみました。これならどうでしょうか?
eggpol

2019/05/10 14:44

だめでした・・・ 同じところでエラーが出ますね。 unityのバージョンは2017.2.0f3 Visualstudioを使っています。 Vector2同士って掛け算できないんでしょうか リファレンス見る限りだとpublic static Vector2 operator * (Vector2 a, float d); となっていました。 Assets/teach2/Graphic.cs(106,22): error CS0019: Operator `*' cannot be applied to operands of type `UnityEngine.Vector2' and `UnityEngine.Vector2' Assets/teach2/Graphic.cs(114,21): error CS0019: Operator `*' cannot be applied to operands of type `UnityEngine.Vector2' and `UnityEngine.Vector2' var p = (vertex * headScale) + headOffset; var p = vertex * shaftScale;
Bongo

2019/05/10 22:11 編集

Vector2のリファレンスを一番下までスクロールすると出てくるpublic static Vector2 operator * (Vector2 a, Vector2 b);を使いたかったのですが、先ほど過去バージョンのリファレンスを見てみたところ2017.4以前には載っていませんね... ともかくエラーが出るからには、Vector2同士をそのまま掛け算するのはあきらめて var p = new Vector2(vertex.x * headScale.x, vertex.y * headScale.y) + headOffset; var p = new Vector2(vertex.x * shaftScale.x, vertex.y * shaftScale.y); でどうでしょう。Vector2同士の乗算演算子でも内部的には同様になっているようですので、このように書き換えても動作上問題はないかと思います。
eggpol

2019/05/11 09:13

できました。ありがとうございます。 Vector2同士の掛け算は リファレンスを確認してみたところ 2017.4verではできなくて2019.1verではできるんですね。 バージョンが違うことで演算子のオーバーロード?が変わることは今まで遭遇したことがなかったのでいい経験になりました。 ここも直しました。 自分用 p = new Vector2((((p * 0.5f) + new Vector2(0.5f, 0.5f)) - pivot).x * size.x, (((p * 0.5f) + new Vector2(0.5f, 0.5f)) - pivot).y * size.y); p = new Vector2((((p * 0.5f) + new Vector2(0.5f, 0.5f)) - pivot).x * size.x, (((p * 0.5f) + new Vector2(0.5f, 0.5f)) - pivot).y * size.y);
guest

回答1

0

ベストアンサー

ワールド空間における方向ベクトルがすでにVector3.downだと決定しているのなら、おっしゃる通りそれをカメラやスクリーンに合わせた座標系に変換してやればいいでしょう。これはUnityが提供している座標系変換用メソッドを使えばよさそうですが、悩みどころはむしろUI上に矢印をどうやって描くかという点かもしれませんね。

一例として、矢印描画用の独自UI要素を作る方針でやってみました。

C#

1#if UNITY_EDITOR 2using UnityEditor; 3#endif 4using UnityEngine; 5using UnityEngine.EventSystems; 6using UnityEngine.UI; 7 8[ExecuteInEditMode] 9public class DirectionArrow : Graphic 10{ 11 // 矢印の原型となる頂点の位置、およびそれらを繋ぐ順番 12 private static readonly Vector2[] headVertices = 13 { 14 Vector2.up + (Vector2.left * 0.5f), 15 Vector2.right, 16 Vector2.zero, 17 Vector2.down + (Vector2.left * 0.5f) 18 }; 19 private static readonly int[][] headIndices = 20 { 21 new[] {0, 1, 2}, 22 new[] {3, 2, 1} 23 }; 24 private static readonly Vector2[] shaftVertices = 25 { 26 Vector2.zero, 27 Vector2.up + Vector2.right, 28 Vector2.down + Vector2.right 29 }; 30 private static readonly int[][] shaftIndices = 31 { 32 new[] {0, 1, 2} 33 }; 34 35 // 各部位の大きさ 36 // RectTransformの半分の大きさを1.0とする比率で指定する 37 public float arrowLength = 1.0f; // 矢印全体の長さ 38 public float shaftThickness = 0.0625f; // 軸の太さ(逆三角形のため、矢じりとの接続部の太さということになる) 39 public float headLength = 0.25f; // 矢じりの先端から矢じりと軸の接続部までの長さ 40 public float headSize = 0.25f; // 軸に対して垂直な方向の矢じりの大きさ 41 42 public Vector3 directionVector = Vector3.down; // ワールド座標系における矢印の向き 43 public Transform directionCameraTransform; // ワールド方向をビュー方向に変換するのに使う(シーンを撮影しているカメラをセットしておく) 44 45#if UNITY_EDITOR 46 // メニューの「GameObject」→「UI」→「Direction Arrow」でオブジェクト作成 47 [MenuItem("GameObject/UI/Direction Arrow")] 48 public static void CreateDirectionArrow() 49 { 50 var canvas = FindObjectOfType<Canvas>(); 51 if (canvas == null) 52 { 53 canvas = new GameObject("Canvas", typeof(Canvas)).GetComponent<Canvas>(); 54 canvas.gameObject.AddComponent<CanvasScaler>(); 55 canvas.gameObject.AddComponent<GraphicRaycaster>(); 56 canvas.renderMode = RenderMode.ScreenSpaceOverlay; 57 } 58 59 if (FindObjectOfType<EventSystem>() == null) 60 { 61 var eventSystem = new GameObject("EventSystem", typeof(EventSystem)); 62 eventSystem.AddComponent<StandaloneInputModule>(); 63 } 64 65 var directionArrow = new GameObject("Direction Arrow", typeof(CanvasRenderer), typeof(DirectionArrow)); 66 directionArrow.transform.SetParent(canvas.transform, false); 67 Selection.activeGameObject = directionArrow; 68 } 69 70#endif 71 72 /// <inheritdoc /> 73 protected override void OnPopulateMesh(VertexHelper vh) 74 { 75 vh.Clear(); 76 var vectorLength = this.directionVector.magnitude; 77 if ((this.directionCameraTransform == null) || Mathf.Approximately(vectorLength, 0.0f)) 78 { 79 return; 80 } 81 82 // directionVectorの向きをビュー空間の向きに変換し、さらに大きさと向きに分離する 83 var arrowVector = this.directionCameraTransform.InverseTransformDirection(this.directionVector); 84 var arrowDirection = ((Vector2)arrowVector).normalized; 85 var arrowMagnitude = ((Vector2)arrowVector).magnitude * this.arrowLength; 86 87 // 先端の奥行きに合わせた拡縮率を求める 88 // 矢印が画面の奥・手前に傾いている場合、それをアピールするためスケールにこれを上乗せする 89 var zScale = 1.0f / (1.0f + (arrowVector.z * 0.5f)); 90 91 // 矢印の原型の座標を目的の形に加工するための各種パラメーターを用意する 92 var pivot = this.rectTransform.pivot; 93 var size = this.rectTransform.rect.size; 94 var shaftLength = Mathf.Max(arrowMagnitude - (this.headLength * zScale), 0.0f); 95 var shaftScale = new Vector2(shaftLength, this.shaftThickness * zScale); 96 var headScale = new Vector2(this.headLength, this.headSize) * zScale; 97 var headOffset = new Vector2(shaftLength, 0.0f); 98 var rotationX = new Vector2(arrowDirection.x, -arrowDirection.y); 99 var rotationY = new Vector2(arrowDirection.y, arrowDirection.x); 100 101 // 矢印の原型を加工し、vhに追加していく 102 var vert = UIVertex.simpleVert; 103 vert.color = this.color; 104 foreach (var vertex in headVertices) 105 { 106 var p = (vertex * headScale) + headOffset; 107 p = new Vector2(Vector2.Dot(rotationX, p), Vector2.Dot(rotationY, p)); 108 p = (((p * 0.5f) + new Vector2(0.5f, 0.5f)) - pivot) * size; 109 vert.position = p; 110 vh.AddVert(vert); 111 } 112 foreach (var vertex in shaftVertices) 113 { 114 var p = vertex * shaftScale; 115 p = new Vector2(Vector2.Dot(rotationX, p), Vector2.Dot(rotationY, p)); 116 p = (((p * 0.5f) + new Vector2(0.5f, 0.5f)) - pivot) * size; 117 vert.position = p; 118 vh.AddVert(vert); 119 } 120 foreach (var index in headIndices) 121 { 122 vh.AddTriangle(index[0], index[1], index[2]); 123 } 124 var indexOffset = headVertices.Length; 125 foreach (var index in shaftIndices) 126 { 127 vh.AddTriangle(index[0] + indexOffset, index[1] + indexOffset, index[2] + indexOffset); 128 } 129 } 130 131 private void Update() 132 { 133 this.SetVerticesDirty(); 134 } 135}

メニューからDirection Arrowを生成して色をつけ、メインカメラをセットし、背景となる円形イメージの上に配置して実行したところ、カメラの向きに応じて矢印の向きが変化しました。
実際のゲームに使うには矢印がシンプルすぎる気がしますので、もっと見栄えをよくする必要があるでしょう。

結果

bochan2さんからご指摘のあった凹凸のあるメッシュに対応するには、ターゲットに最も近いメッシュ上の点を見つけて、DirectionArrowdirectionVectorをターゲットから最近点への向きに書き換えることになるでしょうが、これはなかなかやっかいそうですね。
Collider.ClosestPointで3D空間上の1点に最も近いコライダー表面上の1点を得られるようですが、残念ながらこれは凹部分を持つメッシュには使用できないようです。地形メッシュはおそらく凹部分を持つでしょうから、別の手を使う必要があると思います。
Finding the closest point on a concave mesh (Unity C#) - Game Development Stack Exchangeでは...

  1. メッシュ上のすべての頂点を走査し、ターゲットに最も近い頂点を見つける。
  2. メッシュ上のすべてのポリゴンを走査し、その最も近い頂点を含んでいる三角形を抜き出す。
  3. 抜き出された三角形について、それぞれターゲットに最も近い三角形上の点を求め、それらのうち最もターゲットに近いものを選ぶ。

といった手順が提案されていました。凹凸付きメッシュに対応できないとゲームデザイン上まずいようでしたら、こういった方法を検討してみてはいかがでしょうか。ただし地形メッシュが多数の頂点を含む場合は、提案者の方が言及しているように不要な候補を早期に除去できるような最適化を行わないと、毎フレーム実行するには少々きついかもしれません。

投稿2019/05/05 21:52

編集2019/05/09 17:12
Bongo

総合スコア10807

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

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

eggpol

2019/05/08 05:42

回答ありがとうございます。まさにこの挙動が望んでいたものです。 矢印の拡大縮小についてなのですが、これは何を意味しているのでしょうか?
Bongo

2019/05/08 06:52

視点を回したときに矢印の先端が大きくなったり小さくなったりする挙動のことでしょうか? ちょっと蛇足だったかもしれませんが、地面の方向が2Dの画面に対して奥にあるか手前にあるかを区別できるような、多少3Dっぽい表現にした方がいいかな...と思って付け加えたものです。とはいえ、方向の手かがりが乏しい宇宙空間や海中ならともかく、回答に載せた図のような一般的な地上なら「画面が地面を映していれば地面方向はカメラの前方、空を映していれば地面方向はカメラの背後」ということは映像を見れば伝わるでしょうから、わざわざ矢印を拡大縮小する必要はなかったかもしれませんね。 拡大縮小をなくす場合はzScaleを削除して、さらにその少し後で出てくるshaftLength、shaftScale、headScaleの式の中で「 * zScale」とスケールを掛けている部分も削除してしまってください。
eggpol

2019/05/08 12:21

なるほど つまり空を向く(カメラの後方に地面が存在)ほど矢印が大きくなるということですね
Bongo

2019/05/08 20:01

はい、そういうことになりますね。 それと、追記修正欄にもコメントしましたように視線が地面に対して垂直に近づくほど矢印が短くなるようにしてみましたが、 var arrowMagnitude = ((Vector2)arrowVector).magnitude * this.arrowLength; を var arrowMagnitude = this.arrowLength; に変更すると、この効果がなくなって矢印が常に円の外周を指すようになるはずです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問