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

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

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

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

Q&A

解決済

2回答

629閲覧

クリックしたポイントをラインで囲み

Qoo

総合スコア1249

C#

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

0グッド

1クリップ

投稿2019/01/11 06:49

タッチしたポイントを配列に格納しておき
そのポイントをラインで囲みたいのですが、どのようなメソッド?を使えばよいでしょうか

また、その過去㏍たエリアを塗りつぶしたいといった場合はどのような処理が必要になりますでしょうか

配列は 下記のようなList配列にX,Y座標を格納しようと考えています。

List<Vector2> points

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

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

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

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

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

guest

回答2

0

ベストアンサー

ラインを引くのは、koronatailさんからご紹介のあったLineRendererが使えそうです。ですが塗りつぶしをどうするかは悩みどころですね...

Unityは3Dであっても2Dであっても、基本的に3Dグラフィックスの作法に従って図形を描く方法を検討する必要があるでしょう。つまり、図形の形をしたポリゴンメッシュを作ってシーンに配置するのです(テクスチャを書き換える...などのアプローチで、ペイント系ソフトのような2Dグラフィックス的作法を使うこともできるとは思いますが)。

ちょっと実験を行ってみました。下記のようなスクリプトをアタッチした空のオブジェクトをシーンに配置して...

C#

1using System.Collections.Generic; 2using UnityEngine; 3using UnityEngine.Rendering; 4 5public class ShapeDrawer : MonoBehaviour 6{ 7 [Range(0.0f, 20.0f)] public float cursorDistance = 10.0f; 8 public Color FillColor = Color.green; 9 public Color StrokeColor = Color.red; 10 [Range(0.0f, 0.5f)] public float StrokeWidth = 0.2f; 11 12 private LineRenderer currentLineRenderer; 13 private Mesh currentMesh; 14 private bool drawing; 15 private Shader fillShader; 16 private readonly List<int> indices = new List<int>(); 17 private Camera mainCamera; 18 private bool meshInited; 19 private readonly List<Vector3> points = new List<Vector3>(); 20 private Shader strokeShader; 21 22 private void AddMeshVertex() 23 { 24 var lastIndex = this.points.Count - 1; 25 if (lastIndex < 2) 26 { 27 return; 28 } 29 30 this.indices.Add(0); 31 this.indices.Add(lastIndex - 1); 32 this.indices.Add(lastIndex); 33 this.currentMesh.SetTriangles(this.indices, 0); 34 } 35 36 private Vector3 GetCursorPoint() 37 { 38 return this.currentLineRenderer.transform.InverseTransformPoint( 39 this.mainCamera.ScreenToWorldPoint(Input.mousePosition + (Vector3.forward * this.cursorDistance))); 40 } 41 42 private void Start() 43 { 44 this.fillShader = Shader.Find("Unlit/ShapeFill"); 45 this.strokeShader = Shader.Find("Unlit/ShapeStroke"); 46 } 47 48 private void Update() 49 { 50 if (!this.drawing) 51 { 52 // マウス主ボタンクリックで図形を作成、描画を開始 53 if (!Input.GetMouseButtonDown(0)) 54 { 55 return; 56 } 57 58 this.mainCamera = Camera.main; 59 var shape = new GameObject("Shape"); 60 61 // 塗りを作成 62 var meshFilter = shape.AddComponent<MeshFilter>(); 63 this.currentMesh = new Mesh(); 64 meshFilter.mesh = this.currentMesh; 65 var meshRenderer = shape.AddComponent<MeshRenderer>(); 66 var fillMaterial = new Material(this.fillShader) {color = this.FillColor}; 67 meshRenderer.material = fillMaterial; 68 this.meshInited = false; 69 70 // 線を作成 71 this.currentLineRenderer = shape.AddComponent<LineRenderer>(); 72 this.currentLineRenderer.material = new Material(this.strokeShader) {color = this.StrokeColor}; 73 this.currentLineRenderer.widthMultiplier = this.StrokeWidth; 74 //this.currentLineRenderer.startColor = this.currentLineRenderer.endColor = this.StrokeColor; 75 this.currentLineRenderer.loop = true; 76 this.currentLineRenderer.shadowCastingMode = ShadowCastingMode.Off; 77 this.currentLineRenderer.receiveShadows = false; 78 this.currentLineRenderer.useWorldSpace = false; 79 80 // 点を初期化 81 var cursorPoint = this.GetCursorPoint(); 82 this.points.Clear(); 83 this.points.Add(cursorPoint); 84 this.points.Add(cursorPoint); 85 this.currentLineRenderer.SetPositions(this.points.ToArray()); 86 this.drawing = true; 87 } 88 else 89 { 90 // ポインタ位置を取得し、図形を更新 91 var lastPointIndex = this.points.Count - 1; 92 var cursorPoint = this.GetCursorPoint(); 93 this.currentLineRenderer.SetPosition(lastPointIndex, cursorPoint); 94 this.points[lastPointIndex] = cursorPoint; 95 if (lastPointIndex >= 2) 96 { 97 this.UpdateMesh(); 98 } 99 100 if (Input.GetMouseButtonDown(0)) 101 { 102 // 主ボタンクリックで頂点を追加 103 this.points.Add(cursorPoint); 104 this.currentLineRenderer.positionCount++; 105 this.currentLineRenderer.SetPosition(lastPointIndex + 1, cursorPoint); 106 this.UpdateMesh(); 107 this.AddMeshVertex(); 108 } 109 else if (Input.GetMouseButtonDown(1)) 110 { 111 // 副ボタンクリックで描画を終了 112 this.drawing = false; 113 } 114 } 115 } 116 117 private void UpdateMesh() 118 { 119 this.currentMesh.SetVertices(this.points); 120 if (!this.meshInited) 121 { 122 this.indices.Clear(); 123 this.currentMesh.SetTriangles(this.indices, 0); 124 this.meshInited = true; 125 } 126 this.currentMesh.RecalculateBounds(); 127 } 128}

また、プロジェクト内に線・塗り用のシェーダーファイルを用意します。

塗り用

ShaderLab

1Shader "Unlit/ShapeFill" 2{ 3 Properties 4 { 5 _Color ("Color", Color) = (0, 0, 0, 1) 6 } 7 SubShader 8 { 9 Tags { "Queue" = "Transparent" "RenderType" = "Transparent" } 10 11 CGINCLUDE 12 13 struct appdata 14 { 15 float4 vertex: POSITION; 16 }; 17 18 struct v2f 19 { 20 float4 vertex: SV_POSITION; 21 }; 22 23 v2f vert(appdata v) 24 { 25 v2f o; 26 o.vertex = UnityObjectToClipPos(v.vertex); 27 return o; 28 } 29 30 ENDCG 31 32 Pass // ステンシルパス...描画するべき部分をマークする 33 { 34 Name "Shape Stencil" 35 Cull Off 36 ZWrite Off 37 ColorMask 0 // カラーバッファへの書き込みは不要 38 Stencil 39 { 40 WriteMask 1 // 一番下の1ビットだけを使う...ステンシルバッファに加算するたび、1と0を繰り返す 41 Pass IncrWrap // 描画が行われたら、ステンシルバッファ上の値を1増やす 42 } 43 44 CGPROGRAM 45 46 #pragma vertex vert 47 #pragma fragment frag 48 #include "UnityCG.cginc" 49 50 fixed4 frag(v2f i): SV_Target 51 { 52 return 0; 53 } 54 55 ENDCG 56 } 57 58 Pass // 塗りつぶしパス...描画するべき部分だけに色を塗る 59 { 60 Name "Shape Fill" 61 Cull Off 62 ZWrite Off 63 Stencil 64 { 65 ReadMask 1 // 一番下の1ビットだけ見ればよい 66 Ref 1 67 Comp Equal // ステンシルバッファ上の値が1のフラグメント(奇数回重なっている部分)だけに描画する 68 } 69 70 CGPROGRAM 71 72 #pragma vertex vert 73 #pragma fragment fragFill 74 #include "UnityCG.cginc" 75 76 float4 _Color; 77 78 fixed4 fragFill(v2f i): SV_Target 79 { 80 return _Color; 81 } 82 83 ENDCG 84 } 85 } 86}

線用

ShaderLab

1Shader "Unlit/ShapeStroke" 2{ 3 Properties 4 { 5 _Color ("Color", Color) = (0, 0, 0, 1) 6 } 7 SubShader 8 { 9 Tags { "Queue" = "Transparent+1" "RenderType" = "Transparent" } // QueueをShapeFillより大きくし、塗りより手前に描画する 10 11 Pass 12 { 13 Name "Shape Stroke" 14 Cull Off 15 ZWrite Off 16 17 CGPROGRAM 18 19 #pragma vertex vert 20 #pragma fragment frag 21 #include "UnityCG.cginc" 22 23 struct appdata 24 { 25 float4 vertex: POSITION; 26 }; 27 28 struct v2f 29 { 30 float4 vertex: SV_POSITION; 31 }; 32 33 v2f vert(appdata v) 34 { 35 v2f o; 36 o.vertex = UnityObjectToClipPos(v.vertex); 37 return o; 38 } 39 40 float4 _Color; 41 42 fixed4 frag(v2f i): SV_Target 43 { 44 return _Color; 45 } 46 47 ENDCG 48 } 49 } 50}

左クリックするたびに頂点を追加し、ラインレンダラーのコーナーもそれら座標に更新します。塗り部分はポリゴンメッシュになっており、単純に「追加された最新の頂点」「最新の頂点の一つ前の頂点」「最初の頂点」の3点を繋いだ三角形を追加することにしました。

このようなシンプルなメッシュの作り方だと、ちょっと塗り方を工夫する必要があるでしょう。単純な塗り方をしてしまうと、くぼみのない凸形状なら問題ないですが、凹形状やエッジが交差する図形を描こうとすると...

結果1

こんな風になってしまうはずです。それぞれの三角形の全面が塗りつぶされ、折り紙のようになってしまっています。

そこで、三角形が重なる回数をステンシルバッファを使って数えることにしました。偶数回重なった部分は形状の外側、奇数回重なった部分は内側になるはずなので、奇数回部分だけに描画させると...

結果2

こんな風に、凹形状やエッジ交差のある形状もそれらしく塗ることができました。

ただし、メッシュの実際の形状は折り紙状態のままなので、それが気に入らない場面があるかもしれません。そのような場合は各頂点の位置関係を調べて、交差のない結び方で三角形を作るようにメッシュを再構成してやる必要があるでしょう。Javaゲーム制作記 任意多角形の三角形分割や、多角形の三角形分割 - Wikipediaがご参考になるでしょうか。

エッジ交差のある図形の場合、三角形を繋ぎ直すだけでは対応できないでしょうから、交差部分に頂点を追加してやる必要もあると思います。折れ線の自己交差 [いかたこのたこつぼ]
によると、Bentley-Ottmanのアルゴリズムとやらで交差する位置を特定できるらしいですね。

※当初思い当たったのは多角形の三角形分割によるアプローチだったのですが、今回のようなどんな頂点が与えられるか分からない条件で分割しようとすると、上記のように考慮するべき事項が多くなってややこしくなり、早々にあきらめてステンシルによる重なり偶奇判定に方針変更しました。すみません...

投稿2019/01/11 23:16

Bongo

総合スコア10807

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

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

Qoo

2019/01/11 23:49

ありがとうございます!すごいです! とても参考になりました!やってみます!
guest

0

2Dの線を描画する【Unity】
四角い画像を用意してそれをクリックした座標におくのが一番手っ取り早いんじゃないですかね。

投稿2019/01/11 07:03

koronatail

総合スコア433

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

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

Qoo

2019/01/11 07:06

すみませんー 四角を書きたい訳ではなく、4点とも限らないので その方法ではダメなんです(´°̥̥̥̥̥̥̥̥ω°̥̥̥̥̥̥̥̥`)
koronatail

2019/01/11 07:11 編集

>Qooさん 画像は乱暴でしたがリンク先に掲載されているLineRendererでもだめなんですか?何を描画したいのかイメージが付きませんでした。
Qoo

2019/01/11 23:49

ありがとうございます! とても参考になりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問