うら干物書き

ゲームを作っています。

【Unity】扇形のギズモを作ってみる

 前回ギズモの描画方法を学びました。
 今回は自分でメッシュを作ってそのメッシュをギズモとして描画することに挑戦します。
f:id:urahimono:20171022182755p:plain


この記事にはUnity2017.2.0f3を使用しています。

下準備

 さて、前回のギズモの表示の仕方を参考にして作っていきましょう。
www.urablog.xyz

 まずコンポーネントを作成します。

RangeObject.cs

using UnityEngine;

public class RangeObject : MonoBehaviour
{
    [SerializeField, Range( 0.0f, 360.0f ) ]
    private float   m_widthAngle    = 0.0f;
    [SerializeField, Range( 0.0f, 360.0f ) ]
    private float   m_heightAngle   = 0.0f;
    [SerializeField, Range( 0.0f, 15.0f )]
    private float   m_length        = 0.0f;

    public float WidthAngle     { get{ return m_widthAngle; } }
    public float HeightAngle    { get{ return m_heightAngle; } }
    public float Length         { get{ return m_length; } }

} // class RangeObject

f:id:urahimono:20171022182734p:plain

 このコンポーネントの情報をギズモとしてシーンビューに表示させるようにしましょう。
 ギズモ描画用のスクリプトをEditorフォルダに作ります。

RangeObjectGizmosEditor.cs

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

public static class RangeObjectGizmosEditor
{
    [DrawGizmo( GizmoType.NonSelected | GizmoType.Selected )]
    private static void DrawPointGizmos( RangeObject i_object, GizmoType i_gizmoType )
    {

    }

} // class RangeObjectGizmosEditor

 今回目指しているものはWidthAngleHeightAngleの角度の大きさの扇形のギズモが作りたいです。

 無いかな、そんなギズモを描画する関数は。
docs.unity3d.com

 無いですね。
 仕方ない、作りましょうか。

メッシュ作成

 こんな感じに作ればいいはずです。
 角度とポリゴン数を指定出来るようにしておきましょう。

RangeObjectGizmosEditor.cs

private static Mesh CreatFunMesh( float i_angle, int i_triangleCount )
{
    var mesh    = new Mesh();

    mesh.vertices   = /* 何らかの方法で頂点の配列を作る */null;
    mesh.triangles  = /* 何らかの方法でポリゴンインデックス配列を作る */null;

    mesh.RecalculateNormals();

    return mesh;
}

 まずは頂点の配列を作りましょう。
 頂点の位置を指定するには、サイン値とコサイン値を使って計算しましょう。
 一応、引数が変な場合の対応も入れておいてっと。

RangeObjectGizmosEditor.cs

private static Vector3[] CreateFanVertices( float i_angle, int i_triangleCount )
{
    if( i_angle <= 0.0f )
    {
        throw new System.ArgumentException( string.Format( "角度がおかしい! i_angle={0}", i_angle ) );
    }

    if( i_triangleCount <= 0 )
    {
        throw new System.ArgumentException( string.Format( "数がおかしい! i_triangleCount={0}", i_triangleCount ) );
    }

    i_angle = Mathf.Min( i_angle, 360.0f );

    var vertices    = new List<Vector3>( i_triangleCount + 2 );

    // 始点
    vertices.Add( Vector3.zero );

    // Mathf.Sin()とMathf.Cos()で使用するのは角度ではなくラジアンなので変換しておく。
    float radian    = i_angle * Mathf.Deg2Rad;
    float startRad  = -radian / 2;
    float incRad    = radian / i_triangleCount;

    for( int i = 0; i < i_triangleCount + 1; ++i )
    {
        float currentRad    = startRad + ( incRad * i );

        Vector3 vertex = new Vector3( Mathf.Sin( currentRad ), 0.0f, Mathf.Cos( currentRad ) );
        vertices.Add( vertex );
    }

    return vertices.ToArray();
}

 あとは三角形の頂点順に頂点インデックス配列を作りましょう。

RangeObjectGizmosEditor.cs

private static Mesh CreateFanMesh( float i_angle, int i_triangleCount )
{
    var mesh    = new Mesh();

    var vertices    = CreateFanVertices( i_angle, i_triangleCount );

    var triangleIndexes = new List<int>( i_triangleCount * 3 );

    for( int i = 0; i < i_triangleCount; ++i )
    {
        triangleIndexes.Add( 0 );
        triangleIndexes.Add( i + 1 );
        triangleIndexes.Add( i + 2 );
    }

    mesh.vertices   = vertices;
    mesh.triangles  = triangleIndexes.ToArray();

    mesh.RecalculateNormals();

    return mesh;
}

 これでメッシュが作れたと思いますよ。たぶん。

ギズモ描画

 先ほど作ったメッシュをGizmos.DrawMesh()を使ってこのメッシュを描画しましょう。

RangeObjectGizmosEditor.cs

private static readonly int TRIANGLE_COUNT  = 12;
private static readonly Color MESH_COLOR    = new Color( 1.0f, 1.0f, 0.0f, 0.7f );

[DrawGizmo( GizmoType.NonSelected | GizmoType.Selected )]
private static void DrawPointGizmos( RangeObject i_object, GizmoType i_gizmoType )
{
    if( i_object.Length <= 0.0f )
    {
        return;
    }

    Gizmos.color = MESH_COLOR;

    Transform transform = i_object.transform;
    Vector3 pos         = transform.position + Vector3.up * 0.01f; // 0.01fは地面と高さだと見づらいので調整用。
    Quaternion rot      = transform.rotation;
    Vector3 scale       = Vector3.one * i_object.Length;

    Mesh fanMesh = CreateFanMesh( i_object.WidthAngle, TRIANGLE_COUNT );
    Gizmos.DrawMesh( fanMesh, pos, rot, scale );
}

f:id:urahimono:20171022182755p:plain
f:id:urahimono:20171022182808g:plain

 出来ましたー。
 これでWidthAngleは対応できたのでHeightAngleも対応しましょう。
 同じようにメッシュを作って、回転させてしまえば大丈夫なはずです。

RangeObjectGizmosEditor.cs

[DrawGizmo( GizmoType.NonSelected | GizmoType.Selected )]
private static void DrawPointGizmos( RangeObject i_object, GizmoType i_gizmoType )
{
    if( i_object.Length <= 0.0f )
    {
        return;
    }

    Gizmos.color = MESH_COLOR;

    Transform transform = i_object.transform;
    Vector3 pos         = transform.position + Vector3.up * 0.01f; // 0.01fは地面と高さだと見づらいので調整用。
    Quaternion rot      = transform.rotation;
    Vector3 scale       = Vector3.one * i_object.Length;


    if( i_object.HeightAngle > 0.0f )
    {
        Mesh fanMesh    = CreateFanMesh( i_object.HeightAngle, TRIANGLE_COUNT );

        Gizmos.DrawMesh( fanMesh, pos, rot * Quaternion.AngleAxis( 90.0f, Vector3.forward ), scale );
    }

    if( i_object.WidthAngle > 0.0f )
    {
        Mesh fanMesh    = CreateFanMesh( i_object.WidthAngle, TRIANGLE_COUNT );

        Gizmos.DrawMesh( fanMesh, pos, rot, scale );
    }
}

f:id:urahimono:20171022182915p:plain

 うまくいったかのように見えましたが、反対から見ると表示されていませんね。
 回転させてもう一回描画するようにしましょう。

RangeObjectGizmosEditor.cs

[DrawGizmo( GizmoType.NonSelected | GizmoType.Selected )]
private static void DrawPointGizmos( RangeObject i_object, GizmoType i_gizmoType )
{
    if( i_object.Length <= 0.0f )
    {
        return;
    }

    Gizmos.color = MESH_COLOR;

    Transform transform = i_object.transform;
    Vector3 pos         = transform.position + Vector3.up * 0.01f; // 0.01fは地面と高さだと見づらいので調整用。
    Quaternion rot      = transform.rotation;
    Vector3 scale       = Vector3.one * i_object.Length;


    if( i_object.HeightAngle > 0.0f )
    {
        Mesh fanMesh    = CreateFanMesh( i_object.HeightAngle, TRIANGLE_COUNT );

        Gizmos.DrawMesh( fanMesh, pos, rot * Quaternion.AngleAxis( 90.0f, Vector3.forward ), scale );
        Gizmos.DrawMesh( fanMesh, pos, rot * Quaternion.AngleAxis( 270.0f, Vector3.forward ), scale );
    }

    if( i_object.WidthAngle > 0.0f )
    {
        Mesh fanMesh    = CreateFanMesh( i_object.WidthAngle, TRIANGLE_COUNT );

        Gizmos.DrawMesh( fanMesh, pos, rot, scale );
        Gizmos.DrawMesh( fanMesh, pos, rot * Quaternion.AngleAxis( 180.0f, Vector3.forward ), scale );
    }
}

f:id:urahimono:20171022182931g:plain

 出来ましたね。

 この調子でギズモをいろいろ作っていって、作業の効率化を目指します。