Unity3d导出Recast geomset.txt
(金庆的专栏)
Recast Demo 输入须要 geomset.txt 文件来指定区域类型。
以ObjExporter.cs为基础,编写Unity3d地图导出工具,同一时候导出obj文件和geomset.txt.
geomset.txt 演示样例
# Obj file.
f scene_level_0101.unity_56.obj
# Convex volumes.
# v nVertex area hmin hmax
# Area:
# 0: Default
# 1: Not Walkable
# 2: Jump
# 3: Enemy
# wall
v 4 1 -19.10179 6.521788
-77.54077 -19.10179 60.79453
-74.40015 6.521788 57.64596
-73.83923 6.521788 58.20547
-76.97985 -19.10179 61.35404
场景:
Recast 载入:
代码:
/* Based on ObjExporter.cs: http://wiki.unity3d.com/index.php?title=ObjExporter This "wrapper" lets you export map to .OBJ and geomset.txt directly from the editor menu. Obj mesh file and geomset.txt are used to generate nav mesh for server in Recast. This should be put in your "Editor"-folder. Use menu "Custom->Export map". Exported models are put in a folder called "ExportedMap" in the root of your Unity-project. */ /* 输出 obj 文件是mesh保存文件, geomset.txt 是RecastDemo工具的输入文件, * 将这2个文件拷贝到 RecastDeme 运行文件夹, 按数字 0 载入. */ using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using System; public class EditorMapExporter : ScriptableObject { private static int vertexOffset = 0; private static string targetFolder = "ExportedMap"; private static StringBuilder sbLog = new StringBuilder (); private static string geomsetFileName = "geomset.txt"; private static void FlushLog () { if (!CreateTargetFolder ()) return; using (StreamWriter sw = new StreamWriter(targetFolder + "/" + "MapExporter.log", true)) { sw.Write (sbLog.ToString ()); } sbLog = new StringBuilder (); } private static string MeshToString (MeshFilter mf) { Mesh m = mf.sharedMesh; StringBuilder sb = new StringBuilder (); sb.Append ("g ").Append (mf.name).Append (" "); sb.Append (" "); foreach (Vector3 lv in m.vertices) { Vector3 wv = mf.transform.TransformPoint (lv); //This is sort of ugly - inverting x-component since we're in //a different coordinate system than "everyone" is "used to". sb.Append (string.Format ("v {0} {1} {2} ", -wv.x, wv.y, wv.z)); } sb.Append (" "); for (int material=0; material < m.subMeshCount; material ++) { int[] triangles = m.GetTriangles (material); for (int i=0; i<triangles.Length; i+=3) { //Because we inverted the x-component, we also needed to alter the triangle winding. sb.Append (string.Format ("f {1} {0} {2} ", triangles [i] + 1 + vertexOffset, triangles [i + 1] + 1 + vertexOffset, triangles [i + 2] + 1 + vertexOffset)); } } sb.Append (" "); vertexOffset += m.vertices.Length; return sb.ToString (); } private static void Clear () { vertexOffset = 0; } private static void MeshesToFile (MeshFilter[] mf, string filename) { Clear (); string folder = targetFolder; using (StreamWriter sw = new StreamWriter(folder + "/" + filename)) { for (int i = 0; i < mf.Length; i++) { sw.Write (MeshToString (mf [i])); } } EditorUtility.DisplayDialog ("Map exported", "Exported " + mf.Length + " objects to " + filename, "OK"); } private static bool CreateTargetFolder () { try { System.IO.Directory.CreateDirectory (targetFolder); } catch { EditorUtility.DisplayDialog ("Error!", "Failed to create target folder!", "OK"); return false; } return true; } // 获取场景中的全部游戏对象, 选出全部NavigationStatic对象. // 静态光源等无MeshFilter的将被忽略. private static GameObject[] GetNavStaticObjects () { sbLog.Append ("Get navigation static objects: "); ArrayList navStaticObjList = new ArrayList (); GameObject [] gos = (GameObject[])FindObjectsOfType (typeof(GameObject)); foreach (GameObject go in gos) { // 暂无OffMeshLinkGeneration if (!GameObjectUtility.AreStaticEditorFlagsSet (go, StaticEditorFlags.NavigationStatic)) continue; Component[] meshfilter = go.transform.GetComponents (typeof(MeshFilter)); if (0 == meshfilter.Length) continue; navStaticObjList.Add (go); sbLog.AppendFormat ("Name: {0}, MeshFilters: {1} ", go.name, meshfilter.Length); } return (GameObject[])navStaticObjList.ToArray (typeof(GameObject)); } // 获取场景中的全部游戏对象, 选出全部NavigationStatic对象. // 静态光源等无MeshFilter的将被忽略. private static MeshFilter[] GetMeshFilters (GameObject[] gos) { ArrayList mfList = new ArrayList (); for (int i = 0; i < gos.Length; i++) { GameObject go = gos [i]; Component[] meshfilter = go.transform.GetComponents (typeof(MeshFilter)); for (int m = 0; m < meshfilter.Length; m++) { mfList.Add (meshfilter [m]); } } return (MeshFilter[])mfList.ToArray (typeof(MeshFilter)); } [MenuItem ("Custom/Export Map v4")] static void ExportWholeSelectionToSingle () { if (!CreateTargetFolder ()) return; sbLog.AppendFormat ("NavMeshLayerNames: "); string[] nmLayerNames = GameObjectUtility.GetNavMeshLayerNames (); for (int i = 0; i < nmLayerNames.Length; i++) { sbLog.AppendFormat ("{0}: {1} ", nmLayerNames[i], GameObjectUtility.GetNavMeshLayerFromName(nmLayerNames[i])); } sbLog.Append (" "); //获取场景中的全部游戏对象, 选出全部NavigationStatic对象 GameObject[] gos = GetNavStaticObjects (); MeshFilter[] mf = GetMeshFilters (gos); string objFileName = GetObjSaveFileName (mf.Length); MeshesToFile (mf, objFileName); SaveGeomsetFile (mf, objFileName); FlushLog (); } private static string GetObjSaveFileName(int exportedObjects) { string filename = EditorApplication.currentScene + "_" + exportedObjects; int stripIndex = filename.LastIndexOf ('/');//FIXME: Should be Path.PathSeparator if (stripIndex >= 0) filename = filename.Substring (stripIndex + 1).Trim (); return filename + ".obj"; } private static void SaveGeomsetFile(MeshFilter[] mf, string objFileName) { using (StreamWriter sw = new StreamWriter(targetFolder + "/" + geomsetFileName)) { sw.Write ("# Obj file. "); sw.Write ("f " + objFileName + " "); // 暂无 Offmesh connector // Convex volumes ConvexVolumes cvs = new ConvexVolumes(mf); sw.Write (cvs.ToString ()); } } class ConvexVolumes { private ArrayList cvList = new ArrayList (); public ConvexVolumes(MeshFilter[] mf) { for (int i = 0; i < mf.Length; i++) { cvList.Add (new ConvexVolume (mf[i])); } } public string ToString() { StringBuilder sb = new StringBuilder (); sb.Append ("# Convex volumes. "); sb.Append ("# v nVertex area hmin hmax "); sb.Append ("# Area: "); string[] nmLayerNames = GameObjectUtility.GetNavMeshLayerNames (); for (int i = 0; i < nmLayerNames.Length; i++) { sb.AppendFormat ("# {0}: {1} ", GameObjectUtility.GetNavMeshLayerFromName(nmLayerNames[i]), nmLayerNames[i]); } sb.Append (" &q