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

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

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

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

Q&A

解決済

1回答

14286閲覧

Unityで線を描画したい

akito0705

総合スコア23

Unity

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

2グッド

2クリップ

投稿2018/01/19 18:13

編集2018/01/19 19:10

Unityで線を描画したいと思っています。
現在、ARを使ってプログラミングをしているのですが、その中でARCameraの子オブジェクトとして使っているcanvasの中で線を描画したいです。LineRendererや、他のサイトでの関数など試してみましたが、描画ができませんでした。
LineRendererに関しては、描画はできましたがワールド座標なのでマーカーを動かすと一緒に動いてしまいます。
UIの中での直線の描画の方法を教えてください。

Carly7766, usamino👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

Graphicを継承して独自のUIコンポーネントを作ることができるそうですので、これで「線を描くコンポーネント」を作ってみるのはどうでしょう?

自身の勉強をかねて下記のようなものを作ってみました。ご参考になりましたら幸いです。
コード中のコメントにも記しましたが、頂点が無駄に重複している箇所があり、もっと頂点数を削減できる余地がありますので、ぜひ改造を加えていただけますとさらにパフォーマンスが向上するかと思います。

C#

1using System.Linq; 2#if UNITY_EDITOR 3using UnityEditor; 4#endif 5using UnityEngine; 6using UnityEngine.EventSystems; 7using UnityEngine.UI; 8 9[ExecuteInEditMode] 10public class LineStrip : Graphic 11{ 12 public int CornerDivisionCount = 1; // コーナー部分の接続部の分割数...大きくするとなめらかになるが、頂点数が増える 13 public float LineWidth = 1.0f; // 線の太さ 14 public Vector2[] Positions; // これらの点を結んだ折れ線を描く(原点はRectTransformの左下隅) 15 16#if UNITY_EDITOR 17 18 [MenuItem("GameObject/UI/Line Strip")] // メニューの「GameObject」→「UI」→「Line Strip」でオブジェクト作成 19 public static void CreateLineStrip() 20 { 21 // Canvasを探し、もしなければ作成 22 var canvas = FindObjectOfType<Canvas>(); 23 if (canvas == null) 24 { 25 canvas = new GameObject("Canvas", typeof(Canvas)).GetComponent<Canvas>(); 26 canvas.gameObject.AddComponent<CanvasScaler>(); 27 canvas.gameObject.AddComponent<GraphicRaycaster>(); 28 canvas.renderMode = RenderMode.ScreenSpaceOverlay; 29 } 30 // EventSystemを探し、もしなければ作成 31 if (FindObjectOfType<EventSystem>() == null) 32 { 33 var eventSystem = new GameObject("EventSystem", typeof(EventSystem)); 34 eventSystem.AddComponent<StandaloneInputModule>(); 35 } 36 // LineStripを作成し、Canvasの子にする 37 var lineStrip = new GameObject("LineStrip", typeof(CanvasRenderer), typeof(LineStrip)); 38 lineStrip.transform.SetParent(canvas.transform, false); 39 // 作成されたLineStripを選択 40 Selection.activeGameObject = lineStrip; 41 } 42 43#endif 44 45 protected override void OnPopulateMesh(VertexHelper vh) 46 { 47 this.CornerDivisionCount = Mathf.Max(this.CornerDivisionCount, 1); 48 var pivot = this.rectTransform.pivot; 49 var size = this.rectTransform.rect.size; 50 var origin = Vector2.Scale(-pivot, size); // RectTransformの左下隅を原点とする 51 vh.Clear(); // 以前に描いた頂点は削除 52 var cornerCount = this.Positions == null ? 0 : this.Positions.Length; 53 if (cornerCount < 2) 54 { 55 return; // コーナーの数が2未満だと線を引けないため、何もしない 56 } 57 58 var segmentCount = cornerCount - 1; // コーナーをつなぐ線分の数 59 for (var i = 0; i < segmentCount; i++) 60 { 61 var a = origin + this.Positions[i]; 62 var b = origin + this.Positions[i + 1]; 63 // コーナー間に線分を追加 64 AddSegment( 65 vh, 66 a, 67 this.color, 68 this.LineWidth, 69 b, 70 this.color, 71 this.LineWidth); 72 // 細い線なら気にならないでしょうが、太い線だと線分のつなぎ目が目立つので接続部を追加しました 73 // ですが、1回AddSegmentまたはAddJointを行うごとに頂点数+4、三角形数+2のオーダーで形状が複雑化します 74 // (本当は、折れ線全体が一つの三角形ストリップになるようにすれば頂点数増加を+2に抑えられるはずですが、手抜きしました...) 75 // もし細い線しか使わない、あるいは大量のコーナーを持つ折れ線を描くので描画負荷を下げたい、といったことがありましたら 76 // 以下のifブロックは削除してしまうのがいいでしょう 77 if (i < (segmentCount - 1)) 78 { 79 var c = origin + this.Positions[i + 2]; 80 var tangentFrom = (b - a).normalized; 81 var tangentTo = (c - b).normalized; 82 var tangentCount = this.CornerDivisionCount + 1; 83 var tangentAngle = -Vector2.SignedAngle(tangentFrom, tangentTo) * Mathf.Deg2Rad; 84 var tangents = Enumerable.Range(0, tangentCount) 85 .Select( 86 index => 87 { 88 var angle = Mathf.Lerp(0.0f, tangentAngle, index / (tangentCount - 1.0f)); 89 var cosAngle = Mathf.Cos(angle); 90 var sinAngle = Mathf.Sin(angle); 91 return new Vector2( 92 (cosAngle * tangentFrom.x) + (sinAngle * tangentFrom.y), 93 (cosAngle * tangentFrom.y) - (sinAngle * tangentFrom.x)); 94 }) 95 .ToArray(); 96 for (var j = 0; j < this.CornerDivisionCount; j++) 97 { 98 AddJoint(vh, b, tangents[j], tangents[j + 1], this.color, this.LineWidth); 99 } 100 } 101 } 102 } 103 104 // p点に接続部を追加 105 // tangentFromは入っていく線分の向き 106 // tangentToは出ていく線分の向き 107 private static void AddJoint( 108 VertexHelper vh, 109 Vector2 p, 110 Vector2 tangentFrom, 111 Vector2 tangentTo, 112 Color color, 113 float width) 114 { 115 var dFrom = width * 0.5f * new Vector2(-tangentFrom.y, tangentFrom.x); 116 var dTo = width * 0.5f * new Vector2(-tangentTo.y, tangentTo.x); 117 var offset = vh.currentVertCount; 118 var vertex = UIVertex.simpleVert; 119 vertex.position = p - dFrom; 120 vertex.color = color; 121 vh.AddVert(vertex); 122 vertex.position = p + dFrom; 123 vh.AddVert(vertex); 124 vertex.position = p + dTo; 125 vh.AddVert(vertex); 126 vertex.position = p - dTo; 127 vh.AddVert(vertex); 128 vh.AddTriangle(offset + 0, offset + 1, offset + 2); 129 vh.AddTriangle(offset + 2, offset + 3, offset + 0); 130 } 131 132 // a、b点間に線分を追加 133 private static void AddSegment( 134 VertexHelper vh, 135 Vector2 a, 136 Color colorA, 137 float widthA, 138 Vector2 b, 139 Color colorB, 140 float widthB) 141 { 142 var tangent = (b - a).normalized; 143 var normal = new Vector2(-tangent.y, tangent.x); 144 var dA = widthA * 0.5f * normal; 145 var dB = widthB * 0.5f * normal; 146 var offset = vh.currentVertCount; 147 var vertex = UIVertex.simpleVert; 148 vertex.position = a - dA; 149 vertex.color = colorA; 150 vh.AddVert(vertex); 151 vertex.position = a + dA; 152 vh.AddVert(vertex); 153 vertex.position = b + dB; 154 vertex.color = colorB; 155 vh.AddVert(vertex); 156 vertex.position = b - dB; 157 vh.AddVert(vertex); 158 vh.AddTriangle(offset + 0, offset + 1, offset + 2); 159 vh.AddTriangle(offset + 2, offset + 3, offset + 0); 160 } 161}

###追記
上記LineStripを使う側のスクリプトでは、たとえば下記のようにPositionsを書き換えていくことになるでしょう。試しにAudioSourceの出している音をGetOutputDataで取得してPositionsに反映させてみました。

C#

1using System.Collections; 2using UnityEngine; 3 4public class WaveForm : MonoBehaviour 5{ 6 private const int DataLength = 256; 7 8 public AudioSource soundSource; 9 public LineStrip waveLineStrip; 10 11 private float soundVolume; 12 private Coroutine stoppingSoundCoroutine; 13 private float[] waveData; 14 private float waveFormOffsetY; 15 16 // 音量によって波の振幅が変化しているのをアピールするため、停止時にフェードアウトさせるようにしました 17 private IEnumerator StopSound(float time) 18 { 19 while (this.soundSource.volume > 0.0f) 20 { 21 this.soundSource.volume -= (this.soundVolume * Time.deltaTime) / time; 22 yield return null; 23 } 24 25 this.soundSource.Stop(); 26 this.stoppingSoundCoroutine = null; 27 } 28 29 private void Start() 30 { 31 this.soundVolume = this.soundSource.volume; 32 this.waveData = new float[DataLength]; 33 this.waveLineStrip.Positions = new Vector2[DataLength]; // Positionsに新しいVector2配列をセット→DataLength個のコーナーを持つ折れ線になる 34 35 for (var i = 0; i < DataLength; i++) 36 { 37 this.waveLineStrip.Positions[i].x = i; // 各コーナーのX座標を書き換える 38 } 39 40 this.waveLineStrip.SetVerticesDirty(); // Positionsを書き換えたので、メッシュの更新が必要なことを通知する 41 this.waveFormOffsetY = this.waveLineStrip.rectTransform.rect.height * 0.5f; 42 } 43 44 private void Update() 45 { 46 // スペースキーで音声の再生・停止を切り替える 47 if (Input.GetKeyDown(KeyCode.Space)) 48 { 49 var stopping = this.stoppingSoundCoroutine != null; 50 51 if (this.soundSource.isPlaying && !stopping) 52 { 53 this.stoppingSoundCoroutine = StartCoroutine(StopSound(5.0f)); 54 } 55 else 56 { 57 if (stopping) 58 { 59 this.StopCoroutine(this.stoppingSoundCoroutine); 60 this.stoppingSoundCoroutine = null; 61 } 62 63 this.soundSource.volume = this.soundVolume; 64 this.soundSource.Play(); 65 } 66 } 67 68 this.soundSource.GetOutputData(this.waveData, 0); // soundSourceが現在再生している音声データをwaveDataに取り出す 69 70 for (var i = 0; i < DataLength; i++) 71 { 72 this.waveLineStrip.Positions[i].y = 73 this.waveFormOffsetY + (this.waveData[i] * 64.0f); // waveDataの値に応じて各コーナーのY座標を書き換える 74 } 75 76 this.waveLineStrip.SetVerticesDirty(); // Positionsを書き換えたので、メッシュの更新が必要なことを通知する 77 } 78}

プレビュー

コード内コメントにあるように、今回の例ではPositions変更後にいちいちSetVerticesDirtyを実行しないと変更が反映されなかったりします。Positions書き換え用のメソッドを追加して、その中でSetVerticesDirtyを行うようにした方が使いやすいかもしれません。

投稿2018/01/20 00:00

編集2018/01/20 14:16
Bongo

総合スコア10811

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

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

akito0705

2018/01/20 10:52

ありがとうございます!作成していただいたコンポーネントを改造して使わせていただきたいのですが、コンポーネントの作成をやったことがなくて使い方がわかりません。 これをLineStrip.csを作成し張り付けて、線を引きたいときは別スクリプトからPositions.Addを行えばいいのでしょうか?初歩的な質問ですみません、よろしくお願いします。
Bongo

2018/01/20 14:13

今回作ったものの場合は、Positionsは単なるVector2の配列でして、新しい配列を代入するか、Positionsの各要素を個別に書き換えることになりますね。スクリプトからPositionsの値を書き換える例を追記しました。 配列でなく、たとえばList<Vector2>を使うようにすれば、ご質問者さんのおっしゃるようにAddで座標を追加できるようになるかと思います。 追記でも言及しましたが、今回の例ではPositionsに変化を加えた場合、メッシュの更新が必要なことをUnityに知らせるために、少々面倒ですがSetVerticesDirtyを実行する必要があります。 Positionsは取得専用にして、コーナーの追加や変更は専用のメソッドを使わせるよう書き換え、それらメソッド内でPositionsの書き換えおよびSetVerticesDirtyの実行を行うようにすれば、LineStripを使う側がいちいちSetVerticesDirtyを呼ぶ必要はなくなるかと思います...
akito0705

2018/01/20 15:48

たびたびの質問申し訳ありません。Start関数内で最初にwaveLineScript.Position= new Vector2[DataLength]を書くと思いますが、プログラム実行時にそこでObject reference not set to an instance of an object(オブジェクトに参照できない?)というエラーが出てしまいます。 これにかかわる変数は '''public LineStrip ImageLineStrip; private const int DataLength = 256;    void start(){ this.ImageLineStrip.Positions = new Vector2[DataLength]; }''' しか入れてないのですが、なぜでしょうか。原因に心当たりがあれば教えていただきたいです。
Bongo

2018/01/20 22:36

そのスクリプトをアタッチしたオブジェクトのインスペクタを見てみると、「Image Line Strip」という名前の参照設定欄が出現しているかと思いますが、そこが「None (LineStrip)」のままになっていませんでしょうか? もしそうであれば、そこにヒエラルキー上のLineStripをドラッグ&ドロップする、または設定欄右隣にある丸をクリックすると現れるウィンドウでシーン上のLineStripを選択すると、スクリプト実行時にそのLineStripを参照してくれるので、NullReferenceExceptionは消えるかと思います... ※お節介かもしれませんが補足いたしますと、回答に追記しました例では、soundSourceフィールドも「public AudioSource soundSource;」という風に「public」指定して外部に公開しています。これもインスペクタ上で「Sound Source」に手動で適当なAudioSourceをセットすることを前提としたものです。 たとえば下記のような感じで... - プロジェクトに適当な音声ファイルを追加 - 適当なオブジェクトに「Audio Source」コンポーネントをアタッチ - そのAudio Sourceの「AudioClip」に追加した音声をセット - 「WaveForm」スクリプトをアタッチしたオブジェクトの「Sound Source」に、先ほどのAudio Sourceをアタッチしたオブジェクトをセット
akito0705

2018/01/21 07:09

何から何まで本当にありがとうございます。無事カメラ映像内に描画できました。初歩的な質問にまで答えてくださって本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問