【Unity】LineRendererの拡張 その1【メッシュ】【自動生成】

おっすお疲れ様です!(謎テンション

・・・

・・・・・・

いや、お久しぶりです。

先日とある理由でUnityで線を描画する必要があり
いろいろしてたので、備忘録がてら記録しておきます。

なぜLineRendererだとだめなのか?

そもそもUnityで線を描画させるならLineRendererという機能が存在するのに
なぜ、使わなかったのかというと、いろいろあるんですがとりあえずこれを見てほしい。

f:id:seiroise:20160529220438g:plain:w300

こいつを見てどう思うだろうか?

標準のLineRendererでは頂点の位置によってメッシュが捻られてしまいます。
そのせいで、線の太さが均一ではなくなってしまいます。これは致命的です。

今回はこれが気に入らなかったのでLineRendererExなるものを自分で作ることにしました。
最終的なコードは一番下にあるのでソースが気になる人はスクロールバーを全力で動かしてください。

要件

線を作る前に自分なりの要件を挙げておきます。

  1. 2D/3Dともになるべく線の太さが一定に見える(重要)
  2. 頂点ごとに色が変えられる
  3. あと基本的な機能(頂点の追加、挿入、削除)
  4. エディター上で確認できる
  5. 変更はリアルタイムに適応されなくてもいい

今回はこれらが満たされるようなものを作っていきます。

Unityで線を表現する方法はいくつかあるのですが、要件1を満たすために
メッシュを自動生成する方向で行こうと思います。
要件2は頂点カラーを使っていきます。
要件3は実装で,要件4はExecutableInEditMode属性つけてエディタを少し拡張してあげればおk。
要件5は・・・とくに気にしなくていいよね。

完成物

作る前に完成物をチラ見せ
f:id:seiroise:20160529224343g:plain:w300
こんな感じ
エッジ(辺)は必要に応じて粗さ(何角形か)をいじれるので、
「こんなに細かくなくていいな!」ってきは調節できます。

f:id:seiroise:20160529224515g:plain:w300
頂点(ノード)に表示するメッシュは選択できるのでキューブにするとこんな感じ

作る

それでは実際に作っていきます。 ※あくまで備忘録なので説明は最小限です。気になる人はコメおなしゃす。

必要なデータ構造として頂点(ノード)の色とかを記憶しておく必要があるのでそれらを定義します。

/// <summary>
/// ノード記録用クラス
/// </summary>
[Serializable]
public class Node {
    public Vector3 pos;     //座標
    public Vector3 scale;   //大きさ
    public Color color;     //色
    //public Mesh mesh;     //メッシュ

    //Constructor
    public Node() {
        this.pos = Vector3.zero;
        scale = Vector3.one;
        color = new Color(1f, 0.5f, 0.2f, 0.5f);
    }
    public Node(Vector3 pos, Vector3 scale, Color color) {
        this.pos = pos;
        this.scale = scale;
        this.color = color;
    }
}

このクラスをリストの形で持たせておいて更新関数が走ったら再描画するという感じ。

/// <summary>
/// 更新
/// </summary>
public void Apply() {
    if(nodes.Count <= 0) return;
    //線状のメッシュを作成
    Mesh mesh = CreateLineMesh(nodes, edges);
    mesh.RecalculateNormals();
    mf.mesh = mesh;
}

更新関数の肝になってるCreateLineMeshの流れは

  1. 必要なエッジに相当するメッシュを作成(重要)
  2. ノードとエッジのメッシュをリストに格納
  3. メッシュを結合
  4. 結合したメッシュを返す

という感じになってます。

一番重要なエッジメッシュの作成はこんな感じ

/// <summary>
/// エッジメッシュの作成
/// </summary>
private Mesh CreateEdgeMesh(Vector3 from, Color fromColor, Vector3 to, Color toColor, float size, int square = 4) {
    Vector3 dir = (to - from).normalized;   //方向ベクトル
    //Quaternion.LookRotation(dir)がみそ
    Vector3 dirVertical = Quaternion.AngleAxis(90, dir) * (Quaternion.LookRotation(dir) * Vector3.right) * size;    //方向ベクトルに垂直なベクトル

    Vector3[] vertices = new Vector3[square * 4];
    Vector2[] uvs = new Vector2[square * 4];
    Color[] colors = new Color[square * 4];
    int[] triangles = new int[square * 6];
    for (int i = 0; i < square; ++i) {
        Vector3 angleDir1 = Quaternion.AngleAxis((360f / square) * i, dir) * dirVertical;
        Vector3 angleDir2 = Quaternion.AngleAxis((360f / square) * (i + 1), dir) * dirVertical;
        //vertex
        vertices[i * 4 + 0] = from + angleDir1;
        vertices[i * 4 + 1] = to + angleDir1;
        vertices[i * 4 + 2] = from + angleDir2;
        vertices[i * 4 + 3] = to + angleDir2;
        //uv
        uvs[i * 4 + 0] = new Vector2(0f, 0f);
        uvs[i * 4 + 1] = new Vector2(1f, 0f);
        uvs[i * 4 + 2] = new Vector2(0f, 1f);
        uvs[i * 4 + 3] = new Vector2(1f, 1f);
        //Color
        colors[i * 4 + 0] = fromColor;
        colors[i * 4 + 1] = toColor;
        colors[i * 4 + 2] = fromColor;
        colors[i * 4 + 3] = toColor;
        //triangles
        triangles[i * 6 + 0] = i * 4 + 0;
        triangles[i * 6 + 1] = i * 4 + 2;
        triangles[i * 6 + 2] = i * 4 + 1;

        triangles[i * 6 + 3] = i * 4 + 2;
        triangles[i * 6 + 4] = i * 4 + 3;
        triangles[i * 6 + 5] = i * 4 + 1;
    }
    Mesh m = new Mesh();
    m.vertices = vertices;
    m.uv = uvs;
    m.colors = colors;
    m.triangles = triangles;
    return m;
}

改めてみる汚ねぇ・・・(突貫なのでゆるして
やってることは

  1. 「from」から「to」へのベクトル「dir」を取得
  2. ベクトル「dir」に直行するベクトルの一つ「dirVertical」を取得
  3. メッシュを構成する「頂点」、「uv」、「頂点カラー」、「トライアングルインデックス」の配列を準備
  4. それぞれの値を設定する
  5. メッシュにそれぞれの値を設定して返す

となってます。

ふぅ・・・

使い方とかエディタ拡張のコードとかシェーダとかは次回やると思いまふ。

ではではノ

最終的なコード

ブログ始め

新しくブログを始めてみました。

前のブログはやる気そこそこ出始めたのですが長続きしなかったので、

今度は頑張って書いていけたらいいなぁ。

 

書けたらいいなぁ・・・(届かぬ思い

 

主に書いていく内容は備忘録的な要素が多くなると思います。

 

 

10秒チンしたバターロールみたいな暖かさで見守っていてください。

バターロール美味しいよね。

 

ではでは。