Unityで線を描画したいと思っています。
現在、ARを使ってプログラミングをしているのですが、その中でARCameraの子オブジェクトとして使っているcanvasの中で線を描画したいです。LineRendererや、他のサイトでの関数など試してみましたが、描画ができませんでした。
LineRendererに関しては、描画はできましたがワールド座標なのでマーカーを動かすと一緒に動いてしまいます。
UIの中での直線の描画の方法を教えてください。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答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総合スコア10811
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/01/20 10:52
2018/01/20 14:13
2018/01/20 15:48
2018/01/20 22:36
2018/01/21 07:09