Mycetozoa 開発記録 #3

ナビゲーションメッシュの生成 その①

先に自前実装のクラスとインターフェースを簡単に説明。

Vertexクラス
  • 座標、IDなどを保持
Edgeクラス
  • 辺を表すクラス
  • 開始頂点と終了頂点をVertex型で保持
  • IVertexCollectionを継承
Triangleクラス
  • 三角形を表すクラス
  • 3つの頂点をVertex型で保持
  • 3辺をEdge型で保持
  • IEdgeCollectionを継承
IVertexCollectionインターフェース
  • Vertexの配列をメンバに持つ
  • 拡張メソッドとしてインターフェースを提供

主要インターフェース

public static bool HasVertex(this IVertexCollection self, Vertex vertex);
public static bool HasVertex(this IVertexCollection self, Vertex vertex, IEqualityComparer<Vertex> comparer)
public static bool HasAnyVertex(this IVertexCollection self, IEnumerable<Vertex> vertices);
public static bool HasAnyVertex(this IVertexCollection self, IEnumerable<Vertex> vertices, IEqualityComparer<Vertex> comparer;
public static bool HasVertices(this IVertexCollection self, IEnumerable<Vertex> vertices);
public static bool HasVertices(this IVertexCollection self, IEnumerable<Vertex> vertices, IEqualityComparer<Vertex> comparer);
public static bool HasCommonVertex(this IVertexCollection self, IVertexCollection other);
public static bool HasCommonVertex(this IVertexCollection self, IVertexCollection other, IEqualityComparer<Vertex> comparer);
public static IEnumerable<Vertex> GetCommonVertices(this IVertexCollection self, IVertexCollection other);
public static IEnumerable<Vertex> GetCommonVertices(this IVertexCollection self, IVertexCollection other, IEqualityComparer<Vertex> comparer);
public static IEnumerable<Vertex> GetOtherVertices(this IVertexCollection self, Vertex vertex);
public static IEnumerable<Vertex> GetOtherVertices(this IVertexCollection self, Vertex vertex, IEqualityComparer<Vertex> comparer);
IEdgeCollectionインターフェース
  • IVertexCollectionを継承
  • Edgeの配列をメンバに持つ
  • 拡張メソッドとしてインターフェースを提供

主要インターフェース

public static bool HasEdge(this IEdgeCollection self, Edge edge);
public static bool HasEdge(this IEdgeCollection self, Edge edge, IEqualityComparer<Edge> comparer);
public static bool HasAnyEdge(this IEdgeCollection self, IEnumerable<Edge> edges);
public static bool HasAnyEdge(this IEdgeCollection self, IEnumerable<Edge> edges, IEqualityComparer<Edge> comparer);
public static bool HasEdges(this IEdgeCollection self, IEnumerable<Edge> edges);
public static bool HasEdges(this IEdgeCollection self, IEnumerable<Edge> edges, IEqualityComparer<Edge> comparer);
public static bool HasCommonEdge<TEdge, TVertex>(this IEdgeCollection self, IEdgeCollection other);
public static bool HasCommonEdge(this IEdgeCollection self, IEdgeCollection other, IEqualityComparer<Edge> comparer);
public static IEnumerable<Edge> GetCommonEdges(this IEdgeCollection self, IEdgeCollection other);
public static IEnumerable<Edge> GetCommonEdges(this IEdgeCollection self, IEdgeCollection other, IEqualityComparer<Edge> comparer);
public static IEnumerable<Edge> GetOtherEdges(this IEdgeCollection self, Edge edge);
public static IEnumerable<Edge> GetOtherEdges(this IEdgeCollection self, Edge edge, IEqualityComparer<Edge> comparer);


各クラス、インターフェースの実装はそのうち載せる予定です。

プログレスバーの表示

ナビゲーションメッシュを生成する各工程は時間がかかるのでプログレスバーを表示します。
使いまわせるようにプログレスバーの更新処理を一か所にまとめました。

EditorCommonUtility.cs (Editor拡張で共通で使用するユーティリティメソッドを集約したクラス)

/// <summary>
/// ループで処理するプロセスのデリゲート
/// </summary>
/// <param name="count">現在のループカウント</param>
public delegate void ProcessDelegate(ref int count);

/// <summary>
/// プログレスバーを更新しながらプロセスを実行する
/// </summary>
/// <param name="process">実行するプロセス</param>
/// <param name="count">カウント</param>
/// <param name="title">プログレスバーに表示するタイトル</param>
/// <param name="interval">プログレスバーを更新する頻度</param>
/// <returns></returns>
public static bool ExecuteProcess(ProcessDelegate process, int count, string title, int interval)
{
    int i = 0;
    while (i < count)
    {
	if (i % interval == 0)
	{
	    float progress = (float)i / count;
	    if (!UpdateProgessBar(progress, title))
		return false;
	}

	process(ref i);
    }

    EditorUtility.ClearProgressBar();

    return true;
}

/// <summary>
/// プログレスバーを更新する
/// </summary>
/// <param name="progress">現在の進展</param>
/// <param name="title">タイトル</param>
/// <returns>キャンセルしたか</returns>
private static bool UpdateProgessBar(float progress, string title)
{
    if (EditorUtility.DisplayCancelableProgressBar(title, string.Format("{0}%", (progress * 100).ToString("F2")), progress))
    {
	EditorUtility.ClearProgressBar();
	return false;
    }

    return true;
}

Triangleリストの生成

まずはUnityのメッシュからTriangleのリストを生成します。
メッシュの情報をそのまま使用しても問題ないのですが、一度三角形の構造にしたほうが分かりやすいのでそうしています。

NavigationMeshGenerator.cs

/// <summary>
/// MeshからTriangleのリストを生成する
/// </summary>
/// <returns>キャンセルしたか</returns>
private bool CreateTriangles(Mesh mesh)
{
    EditorCommonUtility.ProcessDelegate p = (ref int i) =>
     {
	 var posA = mesh.vertices[mesh.triangles[i++]];
	 var posB = mesh.vertices[mesh.triangles[i++]];
	 var posC = mesh.vertices[mesh.triangles[i++]];

	 // 各頂点をVertexに変換する
	 var vertexA = posA.ToVertex(vertexId++);
	 var vertexB = posB.ToVertex(vertexId++);
	 var vertexC = posC.ToVertex(vertexId++);

	 var triangle = new Triangle(vertexA, vertexB, vertexC);
	 triangle.Id = triangleId++;

	 // メンバ変数として定義しているsourceTrianglesに格納する
	 sourceTriangles.Add(triangle);
     };

    return EditorCommonUtility.ExecuteProcess(p, mesh.triangles.Length, "Creating triangles...", 10);
}
       

これでTriangleのリスト生成は完了です。
次回はここからユニークな辺のリストを生成していきます。

おわりに

現状のナビゲーションメッシュを生成するデモを載せときます。

generate navmesh