タッチしたポイントを配列に格納しておき
そのポイントをラインで囲みたいのですが、どのようなメソッド?を使えばよいでしょうか
また、その過去㏍たエリアを塗りつぶしたいといった場合はどのような処理が必要になりますでしょうか
配列は 下記のようなList配列にX,Y座標を格納しようと考えています。
List<Vector2> points
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答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点を繋いだ三角形を追加することにしました。
このようなシンプルなメッシュの作り方だと、ちょっと塗り方を工夫する必要があるでしょう。単純な塗り方をしてしまうと、くぼみのない凸形状なら問題ないですが、凹形状やエッジが交差する図形を描こうとすると...
こんな風になってしまうはずです。それぞれの三角形の全面が塗りつぶされ、折り紙のようになってしまっています。
そこで、三角形が重なる回数をステンシルバッファを使って数えることにしました。偶数回重なった部分は形状の外側、奇数回重なった部分は内側になるはずなので、奇数回部分だけに描画させると...
こんな風に、凹形状やエッジ交差のある形状もそれらしく塗ることができました。
ただし、メッシュの実際の形状は折り紙状態のままなので、それが気に入らない場面があるかもしれません。そのような場合は各頂点の位置関係を調べて、交差のない結び方で三角形を作るようにメッシュを再構成してやる必要があるでしょう。Javaゲーム制作記 任意多角形の三角形分割や、多角形の三角形分割 - Wikipediaがご参考になるでしょうか。
エッジ交差のある図形の場合、三角形を繋ぎ直すだけでは対応できないでしょうから、交差部分に頂点を追加してやる必要もあると思います。折れ線の自己交差 [いかたこのたこつぼ]
によると、Bentley-Ottmanのアルゴリズムとやらで交差する位置を特定できるらしいですね。
※当初思い当たったのは多角形の三角形分割によるアプローチだったのですが、今回のようなどんな頂点が与えられるか分からない条件で分割しようとすると、上記のように考慮するべき事項が多くなってややこしくなり、早々にあきらめてステンシルによる重なり偶奇判定に方針変更しました。すみません...
投稿2019/01/11 23:16
総合スコア10807
0
2Dの線を描画する【Unity】
四角い画像を用意してそれをクリックした座標におくのが一番手っ取り早いんじゃないですかね。
投稿2019/01/11 07:03
総合スコア433
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/01/11 07:11 編集
2019/01/11 23:49
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/01/11 23:49