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

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

ただいまの
回答率

90.43%

  • C#

    7979questions

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

  • Unity2D

    1030questions

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

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 89

Qoo

score 910

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

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

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

List<Vector2> points 

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

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

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

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

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class ShapeDrawer : MonoBehaviour
{
    [Range(0.0f, 20.0f)] public float cursorDistance = 10.0f;
    public Color FillColor = Color.green;
    public Color StrokeColor = Color.red;
    [Range(0.0f, 0.5f)] public float StrokeWidth = 0.2f;

    private LineRenderer currentLineRenderer;
    private Mesh currentMesh;
    private bool drawing;
    private Shader fillShader;
    private readonly List<int> indices = new List<int>();
    private Camera mainCamera;
    private bool meshInited;
    private readonly List<Vector3> points = new List<Vector3>();
    private Shader strokeShader;

    private void AddMeshVertex()
    {
        var lastIndex = this.points.Count - 1;
        if (lastIndex < 2)
        {
            return;
        }

        this.indices.Add(0);
        this.indices.Add(lastIndex - 1);
        this.indices.Add(lastIndex);
        this.currentMesh.SetTriangles(this.indices, 0);
    }

    private Vector3 GetCursorPoint()
    {
        return this.currentLineRenderer.transform.InverseTransformPoint(
            this.mainCamera.ScreenToWorldPoint(Input.mousePosition + (Vector3.forward * this.cursorDistance)));
    }

    private void Start()
    {
        this.fillShader = Shader.Find("Unlit/ShapeFill");
        this.strokeShader = Shader.Find("Unlit/ShapeStroke");
    }

    private void Update()
    {
        if (!this.drawing)
        {
            // マウス主ボタンクリックで図形を作成、描画を開始
            if (!Input.GetMouseButtonDown(0))
            {
                return;
            }

            this.mainCamera = Camera.main;
            var shape = new GameObject("Shape");

            // 塗りを作成
            var meshFilter = shape.AddComponent<MeshFilter>();
            this.currentMesh = new Mesh();
            meshFilter.mesh = this.currentMesh;
            var meshRenderer = shape.AddComponent<MeshRenderer>();
            var fillMaterial = new Material(this.fillShader) {color = this.FillColor};
            meshRenderer.material = fillMaterial;
            this.meshInited = false;

            // 線を作成
            this.currentLineRenderer = shape.AddComponent<LineRenderer>();
            this.currentLineRenderer.material = new Material(this.strokeShader) {color = this.StrokeColor};
            this.currentLineRenderer.widthMultiplier = this.StrokeWidth;
            //this.currentLineRenderer.startColor = this.currentLineRenderer.endColor = this.StrokeColor;
            this.currentLineRenderer.loop = true;
            this.currentLineRenderer.shadowCastingMode = ShadowCastingMode.Off;
            this.currentLineRenderer.receiveShadows = false;
            this.currentLineRenderer.useWorldSpace = false;

            // 点を初期化
            var cursorPoint = this.GetCursorPoint();
            this.points.Clear();
            this.points.Add(cursorPoint);
            this.points.Add(cursorPoint);
            this.currentLineRenderer.SetPositions(this.points.ToArray());
            this.drawing = true;
        }
        else
        {
            // ポインタ位置を取得し、図形を更新
            var lastPointIndex = this.points.Count - 1;
            var cursorPoint = this.GetCursorPoint();
            this.currentLineRenderer.SetPosition(lastPointIndex, cursorPoint);
            this.points[lastPointIndex] = cursorPoint;
            if (lastPointIndex >= 2)
            {
                this.UpdateMesh();
            }

            if (Input.GetMouseButtonDown(0))
            {
                // 主ボタンクリックで頂点を追加
                this.points.Add(cursorPoint);
                this.currentLineRenderer.positionCount++;
                this.currentLineRenderer.SetPosition(lastPointIndex + 1, cursorPoint);
                this.UpdateMesh();
                this.AddMeshVertex();
            }
            else if (Input.GetMouseButtonDown(1))
            {
                // 副ボタンクリックで描画を終了
                this.drawing = false;
            }
        }
    }

    private void UpdateMesh()
    {
        this.currentMesh.SetVertices(this.points);
        if (!this.meshInited)
        {
            this.indices.Clear();
            this.currentMesh.SetTriangles(this.indices, 0);
            this.meshInited = true;
        }
        this.currentMesh.RecalculateBounds();
    }
}

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

塗り用

Shader "Unlit/ShapeFill"
{
    Properties
    {
        _Color ("Color", Color) = (0, 0, 0, 1)
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }

        CGINCLUDE

        struct appdata
        {
            float4 vertex: POSITION;
        };

        struct v2f
        {
            float4 vertex: SV_POSITION;
        };

        v2f vert(appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            return o;
        }

        ENDCG

        Pass // ステンシルパス...描画するべき部分をマークする
        {
            Name "Shape Stencil"
            Cull Off
            ZWrite Off
            ColorMask 0 // カラーバッファへの書き込みは不要
            Stencil 
            {
                WriteMask 1 // 一番下の1ビットだけを使う...ステンシルバッファに加算するたび、1と0を繰り返す
                Pass IncrWrap // 描画が行われたら、ステンシルバッファ上の値を1増やす
            }

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            fixed4 frag(v2f i): SV_Target
            {
                return 0;
            }

            ENDCG
        }

        Pass // 塗りつぶしパス...描画するべき部分だけに色を塗る
        {
            Name "Shape Fill"
            Cull Off
            ZWrite Off
            Stencil 
            {
                ReadMask 1 // 一番下の1ビットだけ見ればよい
                Ref 1
                Comp Equal // ステンシルバッファ上の値が1のフラグメント(奇数回重なっている部分)だけに描画する
            }

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment fragFill
            #include "UnityCG.cginc"

            float4 _Color;

            fixed4 fragFill(v2f i): SV_Target
            {
                return _Color;
            }

            ENDCG
        }
    }
}

線用

Shader "Unlit/ShapeStroke"
{
    Properties
    {
        _Color ("Color", Color) = (0, 0, 0, 1)
    }
    SubShader
    {
        Tags { "Queue" = "Transparent+1" "RenderType" = "Transparent" } // QueueをShapeFillより大きくし、塗りより手前に描画する

        Pass
        {
            Name "Shape Stroke"
            Cull Off
            ZWrite Off

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex: POSITION;
            };

            struct v2f
            {
                float4 vertex: SV_POSITION;
            };

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            float4 _Color;

            fixed4 frag(v2f i): SV_Target
            {
                return _Color;
            }

            ENDCG
        }
    }
}

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

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

結果1

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

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

結果2

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/12 08:49

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

    キャンセル

+1

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/11 16:06

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

    キャンセル

  • 2019/01/11 16:11 編集

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

    キャンセル

  • 2019/01/12 08:49

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

    キャンセル

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

  • ただいまの回答率 90.43%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C#

    7979questions

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

  • Unity2D

    1030questions