mirror of
				https://github.com/DerTyp7/grow-ai-unity.git
				synced 2025-10-30 21:07:09 +01:00 
			
		
		
		
	init
This commit is contained in:
		| @@ -0,0 +1,325 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using UnityEditor.Experimental.SceneManagement; | ||||
| using UnityEditor.SceneManagement; | ||||
| using UnityEngine.AI; | ||||
| using UnityEngine; | ||||
|  | ||||
| namespace UnityEditor.AI | ||||
| { | ||||
|     public class NavMeshAssetManager : ScriptableSingleton<NavMeshAssetManager> | ||||
|     { | ||||
|         internal struct AsyncBakeOperation | ||||
|         { | ||||
|             public NavMeshSurface surface; | ||||
|             public NavMeshData bakeData; | ||||
|             public AsyncOperation bakeOperation; | ||||
|         } | ||||
|  | ||||
|         List<AsyncBakeOperation> m_BakeOperations = new List<AsyncBakeOperation>(); | ||||
|         internal List<AsyncBakeOperation> GetBakeOperations() { return m_BakeOperations; } | ||||
|  | ||||
|         struct SavedPrefabNavMeshData | ||||
|         { | ||||
|             public NavMeshSurface surface; | ||||
|             public NavMeshData navMeshData; | ||||
|         } | ||||
|  | ||||
|         List<SavedPrefabNavMeshData> m_PrefabNavMeshDataAssets = new List<SavedPrefabNavMeshData>(); | ||||
|  | ||||
|         static string GetAndEnsureTargetPath(NavMeshSurface surface) | ||||
|         { | ||||
|             // Create directory for the asset if it does not exist yet. | ||||
|             var activeScenePath = surface.gameObject.scene.path; | ||||
|  | ||||
|             var targetPath = "Assets"; | ||||
|             if (!string.IsNullOrEmpty(activeScenePath)) | ||||
|             { | ||||
|                 targetPath = Path.Combine(Path.GetDirectoryName(activeScenePath), Path.GetFileNameWithoutExtension(activeScenePath)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var prefabStage = PrefabStageUtility.GetPrefabStage(surface.gameObject); | ||||
|                 var isPartOfPrefab = prefabStage != null && prefabStage.IsPartOfPrefabContents(surface.gameObject); | ||||
|                 if (isPartOfPrefab && !string.IsNullOrEmpty(prefabStage.prefabAssetPath)) | ||||
|                 { | ||||
|                     var prefabDirectoryName = Path.GetDirectoryName(prefabStage.prefabAssetPath); | ||||
|                     if (!string.IsNullOrEmpty(prefabDirectoryName)) | ||||
|                         targetPath = prefabDirectoryName; | ||||
|                 } | ||||
|             } | ||||
|             if (!Directory.Exists(targetPath)) | ||||
|                 Directory.CreateDirectory(targetPath); | ||||
|             return targetPath; | ||||
|         } | ||||
|  | ||||
|         static void CreateNavMeshAsset(NavMeshSurface surface) | ||||
|         { | ||||
|             var targetPath = GetAndEnsureTargetPath(surface); | ||||
|  | ||||
|             var combinedAssetPath = Path.Combine(targetPath, "NavMesh-" + surface.name + ".asset"); | ||||
|             combinedAssetPath = AssetDatabase.GenerateUniqueAssetPath(combinedAssetPath); | ||||
|             AssetDatabase.CreateAsset(surface.navMeshData, combinedAssetPath); | ||||
|         } | ||||
|  | ||||
|         NavMeshData GetNavMeshAssetToDelete(NavMeshSurface navSurface) | ||||
|         { | ||||
|             if (PrefabUtility.IsPartOfPrefabInstance(navSurface) && !PrefabUtility.IsPartOfModelPrefab(navSurface)) | ||||
|             { | ||||
|                 // Don't allow deleting the asset belonging to the prefab parent | ||||
|                 var parentSurface = PrefabUtility.GetCorrespondingObjectFromSource(navSurface) as NavMeshSurface; | ||||
|                 if (parentSurface && navSurface.navMeshData == parentSurface.navMeshData) | ||||
|                     return null; | ||||
|             } | ||||
|  | ||||
|             // Do not delete the NavMeshData asset referenced from a prefab until the prefab is saved | ||||
|             var prefabStage = PrefabStageUtility.GetPrefabStage(navSurface.gameObject); | ||||
|             var isPartOfPrefab = prefabStage != null && prefabStage.IsPartOfPrefabContents(navSurface.gameObject); | ||||
|             if (isPartOfPrefab && IsCurrentPrefabNavMeshDataStored(navSurface)) | ||||
|                 return null; | ||||
|  | ||||
|             return navSurface.navMeshData; | ||||
|         } | ||||
|  | ||||
|         void ClearSurface(NavMeshSurface navSurface) | ||||
|         { | ||||
|             var hasNavMeshData = navSurface.navMeshData != null; | ||||
|             StoreNavMeshDataIfInPrefab(navSurface); | ||||
|  | ||||
|             var assetToDelete = GetNavMeshAssetToDelete(navSurface); | ||||
|             navSurface.RemoveData(); | ||||
|  | ||||
|             if (hasNavMeshData) | ||||
|             { | ||||
|                 SetNavMeshData(navSurface, null); | ||||
|                 EditorSceneManager.MarkSceneDirty(navSurface.gameObject.scene); | ||||
|             } | ||||
|  | ||||
|             if (assetToDelete) | ||||
|                 AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(assetToDelete)); | ||||
|         } | ||||
|  | ||||
|         public void StartBakingSurfaces(UnityEngine.Object[] surfaces) | ||||
|         { | ||||
|             // Remove first to avoid double registration of the callback | ||||
|             EditorApplication.update -= UpdateAsyncBuildOperations; | ||||
|             EditorApplication.update += UpdateAsyncBuildOperations; | ||||
|  | ||||
|             foreach (NavMeshSurface surf in surfaces) | ||||
|             { | ||||
|                 StoreNavMeshDataIfInPrefab(surf); | ||||
|  | ||||
|                 var oper = new AsyncBakeOperation(); | ||||
|  | ||||
|                 oper.bakeData = InitializeBakeData(surf); | ||||
|                 oper.bakeOperation = surf.UpdateNavMesh(oper.bakeData); | ||||
|                 oper.surface = surf; | ||||
|  | ||||
|                 m_BakeOperations.Add(oper); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         static NavMeshData InitializeBakeData(NavMeshSurface surface) | ||||
|         { | ||||
|             var emptySources = new List<NavMeshBuildSource>(); | ||||
|             var emptyBounds = new Bounds(); | ||||
|             return UnityEngine.AI.NavMeshBuilder.BuildNavMeshData(surface.GetBuildSettings(), emptySources, emptyBounds | ||||
|                 , surface.transform.position, surface.transform.rotation); | ||||
|         } | ||||
|  | ||||
|         void UpdateAsyncBuildOperations() | ||||
|         { | ||||
|             foreach (var oper in m_BakeOperations) | ||||
|             { | ||||
|                 if (oper.surface == null || oper.bakeOperation == null) | ||||
|                     continue; | ||||
|  | ||||
|                 if (oper.bakeOperation.isDone) | ||||
|                 { | ||||
|                     var surface = oper.surface; | ||||
|                     var delete = GetNavMeshAssetToDelete(surface); | ||||
|                     if (delete != null) | ||||
|                         AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(delete)); | ||||
|  | ||||
|                     surface.RemoveData(); | ||||
|                     SetNavMeshData(surface, oper.bakeData); | ||||
|  | ||||
|                     if (surface.isActiveAndEnabled) | ||||
|                         surface.AddData(); | ||||
|                     CreateNavMeshAsset(surface); | ||||
|                     EditorSceneManager.MarkSceneDirty(surface.gameObject.scene); | ||||
|                 } | ||||
|             } | ||||
|             m_BakeOperations.RemoveAll(o => o.bakeOperation == null || o.bakeOperation.isDone); | ||||
|             if (m_BakeOperations.Count == 0) | ||||
|                 EditorApplication.update -= UpdateAsyncBuildOperations; | ||||
|         } | ||||
|  | ||||
|         public bool IsSurfaceBaking(NavMeshSurface surface) | ||||
|         { | ||||
|             if (surface == null) | ||||
|                 return false; | ||||
|  | ||||
|             foreach (var oper in m_BakeOperations) | ||||
|             { | ||||
|                 if (oper.surface == null || oper.bakeOperation == null) | ||||
|                     continue; | ||||
|  | ||||
|                 if (oper.surface == surface) | ||||
|                     return true; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         public void ClearSurfaces(UnityEngine.Object[] surfaces) | ||||
|         { | ||||
|             foreach (NavMeshSurface s in surfaces) | ||||
|                 ClearSurface(s); | ||||
|         } | ||||
|  | ||||
|         static void SetNavMeshData(NavMeshSurface navSurface, NavMeshData navMeshData) | ||||
|         { | ||||
|             var so = new SerializedObject(navSurface); | ||||
|             var navMeshDataProperty = so.FindProperty("m_NavMeshData"); | ||||
|             navMeshDataProperty.objectReferenceValue = navMeshData; | ||||
|             so.ApplyModifiedPropertiesWithoutUndo(); | ||||
|         } | ||||
|  | ||||
|         void StoreNavMeshDataIfInPrefab(NavMeshSurface surfaceToStore) | ||||
|         { | ||||
|             var prefabStage = PrefabStageUtility.GetPrefabStage(surfaceToStore.gameObject); | ||||
|             var isPartOfPrefab = prefabStage != null && prefabStage.IsPartOfPrefabContents(surfaceToStore.gameObject); | ||||
|             if (!isPartOfPrefab) | ||||
|                 return; | ||||
|  | ||||
|             // check if data has already been stored for this surface | ||||
|             foreach (var storedAssetInfo in m_PrefabNavMeshDataAssets) | ||||
|                 if (storedAssetInfo.surface == surfaceToStore) | ||||
|                     return; | ||||
|  | ||||
|             if (m_PrefabNavMeshDataAssets.Count == 0) | ||||
|             { | ||||
|                 PrefabStage.prefabSaving -= DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
|                 PrefabStage.prefabSaving += DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
|  | ||||
|                 PrefabStage.prefabStageClosing -= ForgetUnsavedNavMeshDataChanges; | ||||
|                 PrefabStage.prefabStageClosing += ForgetUnsavedNavMeshDataChanges; | ||||
|             } | ||||
|  | ||||
|             var isDataOwner = true; | ||||
|             if (PrefabUtility.IsPartOfPrefabInstance(surfaceToStore) && !PrefabUtility.IsPartOfModelPrefab(surfaceToStore)) | ||||
|             { | ||||
|                 var basePrefabSurface = PrefabUtility.GetCorrespondingObjectFromSource(surfaceToStore) as NavMeshSurface; | ||||
|                 isDataOwner = basePrefabSurface == null || surfaceToStore.navMeshData != basePrefabSurface.navMeshData; | ||||
|             } | ||||
|             m_PrefabNavMeshDataAssets.Add(new SavedPrefabNavMeshData { surface = surfaceToStore, navMeshData = isDataOwner ? surfaceToStore.navMeshData : null }); | ||||
|         } | ||||
|  | ||||
|         bool IsCurrentPrefabNavMeshDataStored(NavMeshSurface surface) | ||||
|         { | ||||
|             if (surface == null) | ||||
|                 return false; | ||||
|  | ||||
|             foreach (var storedAssetInfo in m_PrefabNavMeshDataAssets) | ||||
|             { | ||||
|                 if (storedAssetInfo.surface == surface) | ||||
|                     return storedAssetInfo.navMeshData == surface.navMeshData; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         void DeleteStoredNavMeshDataAssetsForOwnedSurfaces(GameObject gameObjectInPrefab) | ||||
|         { | ||||
|             // Debug.LogFormat("DeleteStoredNavMeshDataAsset() when saving prefab {0}", gameObjectInPrefab.name); | ||||
|  | ||||
|             var surfaces = gameObjectInPrefab.GetComponentsInChildren<NavMeshSurface>(true); | ||||
|             foreach (var surface in surfaces) | ||||
|                 DeleteStoredPrefabNavMeshDataAsset(surface); | ||||
|         } | ||||
|  | ||||
|         void DeleteStoredPrefabNavMeshDataAsset(NavMeshSurface surface) | ||||
|         { | ||||
|             for (var i = m_PrefabNavMeshDataAssets.Count - 1; i >= 0; i--) | ||||
|             { | ||||
|                 var storedAssetInfo = m_PrefabNavMeshDataAssets[i]; | ||||
|                 if (storedAssetInfo.surface == surface) | ||||
|                 { | ||||
|                     var storedNavMeshData = storedAssetInfo.navMeshData; | ||||
|                     if (storedNavMeshData != null && storedNavMeshData != surface.navMeshData) | ||||
|                     { | ||||
|                         var assetPath = AssetDatabase.GetAssetPath(storedNavMeshData); | ||||
|                         AssetDatabase.DeleteAsset(assetPath); | ||||
|                     } | ||||
|  | ||||
|                     m_PrefabNavMeshDataAssets.RemoveAt(i); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (m_PrefabNavMeshDataAssets.Count == 0) | ||||
|             { | ||||
|                 PrefabStage.prefabSaving -= DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
|                 PrefabStage.prefabStageClosing -= ForgetUnsavedNavMeshDataChanges; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         void ForgetUnsavedNavMeshDataChanges(PrefabStage prefabStage) | ||||
|         { | ||||
|             // Debug.Log("On prefab closing - forget about this object's surfaces and stop caring about prefab saving"); | ||||
|  | ||||
|             if (prefabStage == null) | ||||
|                 return; | ||||
|  | ||||
|             var allSurfacesInPrefab = prefabStage.prefabContentsRoot.GetComponentsInChildren<NavMeshSurface>(true); | ||||
|             NavMeshSurface surfaceInPrefab = null; | ||||
|             var index = 0; | ||||
|             do | ||||
|             { | ||||
|                 if (allSurfacesInPrefab.Length > 0) | ||||
|                     surfaceInPrefab = allSurfacesInPrefab[index]; | ||||
|  | ||||
|                 for (var i = m_PrefabNavMeshDataAssets.Count - 1; i >= 0; i--) | ||||
|                 { | ||||
|                     var storedPrefabInfo = m_PrefabNavMeshDataAssets[i]; | ||||
|                     if (storedPrefabInfo.surface == null) | ||||
|                     { | ||||
|                         // Debug.LogFormat("A surface from the prefab got deleted after it has baked a new NavMesh but it hasn't saved it. Now the unsaved asset gets deleted. ({0})", storedPrefabInfo.navMeshData); | ||||
|  | ||||
|                         // surface got deleted, thus delete its initial NavMeshData asset | ||||
|                         if (storedPrefabInfo.navMeshData != null) | ||||
|                         { | ||||
|                             var assetPath = AssetDatabase.GetAssetPath(storedPrefabInfo.navMeshData); | ||||
|                             AssetDatabase.DeleteAsset(assetPath); | ||||
|                         } | ||||
|  | ||||
|                         m_PrefabNavMeshDataAssets.RemoveAt(i); | ||||
|                     } | ||||
|                     else if (surfaceInPrefab != null && storedPrefabInfo.surface == surfaceInPrefab) | ||||
|                     { | ||||
|                         //Debug.LogFormat("The surface {0} from the prefab was storing the original navmesh data and now will be forgotten", surfaceInPrefab); | ||||
|  | ||||
|                         var baseSurface = PrefabUtility.GetCorrespondingObjectFromSource(surfaceInPrefab) as NavMeshSurface; | ||||
|                         if (baseSurface == null || surfaceInPrefab.navMeshData != baseSurface.navMeshData) | ||||
|                         { | ||||
|                             var assetPath = AssetDatabase.GetAssetPath(surfaceInPrefab.navMeshData); | ||||
|                             AssetDatabase.DeleteAsset(assetPath); | ||||
|  | ||||
|                             //Debug.LogFormat("The surface {0} from the prefab has baked new NavMeshData but did not save this change so the asset has been now deleted. ({1})", | ||||
|                             //    surfaceInPrefab, assetPath); | ||||
|                         } | ||||
|  | ||||
|                         m_PrefabNavMeshDataAssets.RemoveAt(i); | ||||
|                     } | ||||
|                 } | ||||
|             } while (++index < allSurfacesInPrefab.Length); | ||||
|  | ||||
|             if (m_PrefabNavMeshDataAssets.Count == 0) | ||||
|             { | ||||
|                 PrefabStage.prefabSaving -= DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
|                 PrefabStage.prefabStageClosing -= ForgetUnsavedNavMeshDataChanges; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,11 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 178d8366aa1616849b91b66285c51454 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,325 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using UnityEditor.Experimental.SceneManagement; | ||||
| using UnityEditor.SceneManagement; | ||||
| using UnityEngine.AI; | ||||
| using UnityEngine; | ||||
|  | ||||
| namespace UnityEditor.AI | ||||
| { | ||||
|     public class NavMeshAssetManager2d : ScriptableSingleton<NavMeshAssetManager2d> | ||||
|     { | ||||
|         internal struct AsyncBakeOperation | ||||
|         { | ||||
|             public NavMeshSurface2d surface; | ||||
|             public NavMeshData bakeData; | ||||
|             public AsyncOperation bakeOperation; | ||||
|         } | ||||
|  | ||||
|         List<AsyncBakeOperation> m_BakeOperations = new List<AsyncBakeOperation>(); | ||||
|         internal List<AsyncBakeOperation> GetBakeOperations() { return m_BakeOperations; } | ||||
|  | ||||
|         struct SavedPrefabNavMeshData | ||||
|         { | ||||
|             public NavMeshSurface2d surface; | ||||
|             public NavMeshData navMeshData; | ||||
|         } | ||||
|  | ||||
|         List<SavedPrefabNavMeshData> m_PrefabNavMeshDataAssets = new List<SavedPrefabNavMeshData>(); | ||||
|  | ||||
|         static string GetAndEnsureTargetPath(NavMeshSurface2d surface) | ||||
|         { | ||||
|             // Create directory for the asset if it does not exist yet. | ||||
|             var activeScenePath = surface.gameObject.scene.path; | ||||
|  | ||||
|             var targetPath = "Assets"; | ||||
|             if (!string.IsNullOrEmpty(activeScenePath)) | ||||
|             { | ||||
|                 targetPath = Path.Combine(Path.GetDirectoryName(activeScenePath), Path.GetFileNameWithoutExtension(activeScenePath)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var prefabStage = PrefabStageUtility.GetPrefabStage(surface.gameObject); | ||||
|                 var isPartOfPrefab = prefabStage != null && prefabStage.IsPartOfPrefabContents(surface.gameObject); | ||||
|                 if (isPartOfPrefab && !string.IsNullOrEmpty(prefabStage.prefabAssetPath)) | ||||
|                 { | ||||
|                     var prefabDirectoryName = Path.GetDirectoryName(prefabStage.prefabAssetPath); | ||||
|                     if (!string.IsNullOrEmpty(prefabDirectoryName)) | ||||
|                         targetPath = prefabDirectoryName; | ||||
|                 } | ||||
|             } | ||||
|             if (!Directory.Exists(targetPath)) | ||||
|                 Directory.CreateDirectory(targetPath); | ||||
|             return targetPath; | ||||
|         } | ||||
|  | ||||
|         static void CreateNavMeshAsset(NavMeshSurface2d surface) | ||||
|         { | ||||
|             var targetPath = GetAndEnsureTargetPath(surface); | ||||
|  | ||||
|             var combinedAssetPath = Path.Combine(targetPath, "NavMesh-" + surface.name + ".asset"); | ||||
|             combinedAssetPath = AssetDatabase.GenerateUniqueAssetPath(combinedAssetPath); | ||||
|             AssetDatabase.CreateAsset(surface.navMeshData, combinedAssetPath); | ||||
|         } | ||||
|  | ||||
|         NavMeshData GetNavMeshAssetToDelete(NavMeshSurface2d navSurface) | ||||
|         { | ||||
|             if (PrefabUtility.IsPartOfPrefabInstance(navSurface) && !PrefabUtility.IsPartOfModelPrefab(navSurface)) | ||||
|             { | ||||
|                 // Don't allow deleting the asset belonging to the prefab parent | ||||
|                 var parentSurface = PrefabUtility.GetCorrespondingObjectFromSource(navSurface) as NavMeshSurface2d; | ||||
|                 if (parentSurface && navSurface.navMeshData == parentSurface.navMeshData) | ||||
|                     return null; | ||||
|             } | ||||
|  | ||||
|             // Do not delete the NavMeshData asset referenced from a prefab until the prefab is saved | ||||
|             var prefabStage = PrefabStageUtility.GetPrefabStage(navSurface.gameObject); | ||||
|             var isPartOfPrefab = prefabStage != null && prefabStage.IsPartOfPrefabContents(navSurface.gameObject); | ||||
|             if (isPartOfPrefab && IsCurrentPrefabNavMeshDataStored(navSurface)) | ||||
|                 return null; | ||||
|  | ||||
|             return navSurface.navMeshData; | ||||
|         } | ||||
|  | ||||
|         void ClearSurface(NavMeshSurface2d navSurface) | ||||
|         { | ||||
|             var hasNavMeshData = navSurface.navMeshData != null; | ||||
|             StoreNavMeshDataIfInPrefab(navSurface); | ||||
|  | ||||
|             var assetToDelete = GetNavMeshAssetToDelete(navSurface); | ||||
|             navSurface.RemoveData(); | ||||
|  | ||||
|             if (hasNavMeshData) | ||||
|             { | ||||
|                 SetNavMeshData(navSurface, null); | ||||
|                 EditorSceneManager.MarkSceneDirty(navSurface.gameObject.scene); | ||||
|             } | ||||
|  | ||||
|             if (assetToDelete) | ||||
|                 AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(assetToDelete)); | ||||
|         } | ||||
|  | ||||
|         public void StartBakingSurfaces(UnityEngine.Object[] surfaces) | ||||
|         { | ||||
|             // Remove first to avoid double registration of the callback | ||||
|             EditorApplication.update -= UpdateAsyncBuildOperations; | ||||
|             EditorApplication.update += UpdateAsyncBuildOperations; | ||||
|  | ||||
|             foreach (NavMeshSurface2d surf in surfaces) | ||||
|             { | ||||
|                 StoreNavMeshDataIfInPrefab(surf); | ||||
|  | ||||
|                 var oper = new AsyncBakeOperation(); | ||||
|  | ||||
|                 oper.bakeData = InitializeBakeData(surf); | ||||
|                 oper.bakeOperation = surf.UpdateNavMesh(oper.bakeData); | ||||
|                 oper.surface = surf; | ||||
|  | ||||
|                 m_BakeOperations.Add(oper); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         static NavMeshData InitializeBakeData(NavMeshSurface2d surface) | ||||
|         { | ||||
|             var emptySources = new List<NavMeshBuildSource>(); | ||||
|             var emptyBounds = new Bounds(); | ||||
|             return UnityEngine.AI.NavMeshBuilder.BuildNavMeshData(surface.GetBuildSettings(), emptySources, emptyBounds | ||||
|                 , surface.transform.position, surface.transform.rotation); | ||||
|         } | ||||
|  | ||||
|         void UpdateAsyncBuildOperations() | ||||
|         { | ||||
|             foreach (var oper in m_BakeOperations) | ||||
|             { | ||||
|                 if (oper.surface == null || oper.bakeOperation == null) | ||||
|                     continue; | ||||
|  | ||||
|                 if (oper.bakeOperation.isDone) | ||||
|                 { | ||||
|                     var surface = oper.surface; | ||||
|                     var delete = GetNavMeshAssetToDelete(surface); | ||||
|                     if (delete != null) | ||||
|                         AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(delete)); | ||||
|  | ||||
|                     surface.RemoveData(); | ||||
|                     SetNavMeshData(surface, oper.bakeData); | ||||
|  | ||||
|                     if (surface.isActiveAndEnabled) | ||||
|                         surface.AddData(); | ||||
|                     CreateNavMeshAsset(surface); | ||||
|                     EditorSceneManager.MarkSceneDirty(surface.gameObject.scene); | ||||
|                 } | ||||
|             } | ||||
|             m_BakeOperations.RemoveAll(o => o.bakeOperation == null || o.bakeOperation.isDone); | ||||
|             if (m_BakeOperations.Count == 0) | ||||
|                 EditorApplication.update -= UpdateAsyncBuildOperations; | ||||
|         } | ||||
|  | ||||
|         public bool IsSurfaceBaking(NavMeshSurface2d surface) | ||||
|         { | ||||
|             if (surface == null) | ||||
|                 return false; | ||||
|  | ||||
|             foreach (var oper in m_BakeOperations) | ||||
|             { | ||||
|                 if (oper.surface == null || oper.bakeOperation == null) | ||||
|                     continue; | ||||
|  | ||||
|                 if (oper.surface == surface) | ||||
|                     return true; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         public void ClearSurfaces(UnityEngine.Object[] surfaces) | ||||
|         { | ||||
|             foreach (NavMeshSurface2d s in surfaces) | ||||
|                 ClearSurface(s); | ||||
|         } | ||||
|  | ||||
|         static void SetNavMeshData(NavMeshSurface2d navSurface, NavMeshData navMeshData) | ||||
|         { | ||||
|             var so = new SerializedObject(navSurface); | ||||
|             var navMeshDataProperty = so.FindProperty("m_NavMeshData"); | ||||
|             navMeshDataProperty.objectReferenceValue = navMeshData; | ||||
|             so.ApplyModifiedPropertiesWithoutUndo(); | ||||
|         } | ||||
|  | ||||
|         void StoreNavMeshDataIfInPrefab(NavMeshSurface2d surfaceToStore) | ||||
|         { | ||||
|             var prefabStage = PrefabStageUtility.GetPrefabStage(surfaceToStore.gameObject); | ||||
|             var isPartOfPrefab = prefabStage != null && prefabStage.IsPartOfPrefabContents(surfaceToStore.gameObject); | ||||
|             if (!isPartOfPrefab) | ||||
|                 return; | ||||
|  | ||||
|             // check if data has already been stored for this surface | ||||
|             foreach (var storedAssetInfo in m_PrefabNavMeshDataAssets) | ||||
|                 if (storedAssetInfo.surface == surfaceToStore) | ||||
|                     return; | ||||
|  | ||||
|             if (m_PrefabNavMeshDataAssets.Count == 0) | ||||
|             { | ||||
|                 PrefabStage.prefabSaving -= DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
|                 PrefabStage.prefabSaving += DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
|  | ||||
|                 PrefabStage.prefabStageClosing -= ForgetUnsavedNavMeshDataChanges; | ||||
|                 PrefabStage.prefabStageClosing += ForgetUnsavedNavMeshDataChanges; | ||||
|             } | ||||
|  | ||||
|             var isDataOwner = true; | ||||
|             if (PrefabUtility.IsPartOfPrefabInstance(surfaceToStore) && !PrefabUtility.IsPartOfModelPrefab(surfaceToStore)) | ||||
|             { | ||||
|                 var basePrefabSurface = PrefabUtility.GetCorrespondingObjectFromSource(surfaceToStore) as NavMeshSurface2d; | ||||
|                 isDataOwner = basePrefabSurface == null || surfaceToStore.navMeshData != basePrefabSurface.navMeshData; | ||||
|             } | ||||
|             m_PrefabNavMeshDataAssets.Add(new SavedPrefabNavMeshData { surface = surfaceToStore, navMeshData = isDataOwner ? surfaceToStore.navMeshData : null }); | ||||
|         } | ||||
|  | ||||
|         bool IsCurrentPrefabNavMeshDataStored(NavMeshSurface2d surface) | ||||
|         { | ||||
|             if (surface == null) | ||||
|                 return false; | ||||
|  | ||||
|             foreach (var storedAssetInfo in m_PrefabNavMeshDataAssets) | ||||
|             { | ||||
|                 if (storedAssetInfo.surface == surface) | ||||
|                     return storedAssetInfo.navMeshData == surface.navMeshData; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         void DeleteStoredNavMeshDataAssetsForOwnedSurfaces(GameObject gameObjectInPrefab) | ||||
|         { | ||||
|             // Debug.LogFormat("DeleteStoredNavMeshDataAsset() when saving prefab {0}", gameObjectInPrefab.name); | ||||
|  | ||||
|             var surfaces = gameObjectInPrefab.GetComponentsInChildren<NavMeshSurface2d>(true); | ||||
|             foreach (var surface in surfaces) | ||||
|                 DeleteStoredPrefabNavMeshDataAsset(surface); | ||||
|         } | ||||
|  | ||||
|         void DeleteStoredPrefabNavMeshDataAsset(NavMeshSurface2d surface) | ||||
|         { | ||||
|             for (var i = m_PrefabNavMeshDataAssets.Count - 1; i >= 0; i--) | ||||
|             { | ||||
|                 var storedAssetInfo = m_PrefabNavMeshDataAssets[i]; | ||||
|                 if (storedAssetInfo.surface == surface) | ||||
|                 { | ||||
|                     var storedNavMeshData = storedAssetInfo.navMeshData; | ||||
|                     if (storedNavMeshData != null && storedNavMeshData != surface.navMeshData) | ||||
|                     { | ||||
|                         var assetPath = AssetDatabase.GetAssetPath(storedNavMeshData); | ||||
|                         AssetDatabase.DeleteAsset(assetPath); | ||||
|                     } | ||||
|  | ||||
|                     m_PrefabNavMeshDataAssets.RemoveAt(i); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (m_PrefabNavMeshDataAssets.Count == 0) | ||||
|             { | ||||
|                 PrefabStage.prefabSaving -= DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
|                 PrefabStage.prefabStageClosing -= ForgetUnsavedNavMeshDataChanges; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         void ForgetUnsavedNavMeshDataChanges(PrefabStage prefabStage) | ||||
|         { | ||||
|             // Debug.Log("On prefab closing - forget about this object's surfaces and stop caring about prefab saving"); | ||||
|  | ||||
|             if (prefabStage == null) | ||||
|                 return; | ||||
|  | ||||
|             var allSurfacesInPrefab = prefabStage.prefabContentsRoot.GetComponentsInChildren<NavMeshSurface2d>(true); | ||||
|             NavMeshSurface2d surfaceInPrefab = null; | ||||
|             var index = 0; | ||||
|             do | ||||
|             { | ||||
|                 if (allSurfacesInPrefab.Length > 0) | ||||
|                     surfaceInPrefab = allSurfacesInPrefab[index]; | ||||
|  | ||||
|                 for (var i = m_PrefabNavMeshDataAssets.Count - 1; i >= 0; i--) | ||||
|                 { | ||||
|                     var storedPrefabInfo = m_PrefabNavMeshDataAssets[i]; | ||||
|                     if (storedPrefabInfo.surface == null) | ||||
|                     { | ||||
|                         // Debug.LogFormat("A surface from the prefab got deleted after it has baked a new NavMesh but it hasn't saved it. Now the unsaved asset gets deleted. ({0})", storedPrefabInfo.navMeshData); | ||||
|  | ||||
|                         // surface got deleted, thus delete its initial NavMeshData asset | ||||
|                         if (storedPrefabInfo.navMeshData != null) | ||||
|                         { | ||||
|                             var assetPath = AssetDatabase.GetAssetPath(storedPrefabInfo.navMeshData); | ||||
|                             AssetDatabase.DeleteAsset(assetPath); | ||||
|                         } | ||||
|  | ||||
|                         m_PrefabNavMeshDataAssets.RemoveAt(i); | ||||
|                     } | ||||
|                     else if (surfaceInPrefab != null && storedPrefabInfo.surface == surfaceInPrefab) | ||||
|                     { | ||||
|                         //Debug.LogFormat("The surface {0} from the prefab was storing the original navmesh data and now will be forgotten", surfaceInPrefab); | ||||
|  | ||||
|                         var baseSurface = PrefabUtility.GetCorrespondingObjectFromSource(surfaceInPrefab) as NavMeshSurface2d; | ||||
|                         if (baseSurface == null || surfaceInPrefab.navMeshData != baseSurface.navMeshData) | ||||
|                         { | ||||
|                             var assetPath = AssetDatabase.GetAssetPath(surfaceInPrefab.navMeshData); | ||||
|                             AssetDatabase.DeleteAsset(assetPath); | ||||
|  | ||||
|                             //Debug.LogFormat("The surface {0} from the prefab has baked new NavMeshData but did not save this change so the asset has been now deleted. ({1})", | ||||
|                             //    surfaceInPrefab, assetPath); | ||||
|                         } | ||||
|  | ||||
|                         m_PrefabNavMeshDataAssets.RemoveAt(i); | ||||
|                     } | ||||
|                 } | ||||
|             } while (++index < allSurfacesInPrefab.Length); | ||||
|  | ||||
|             if (m_PrefabNavMeshDataAssets.Count == 0) | ||||
|             { | ||||
|                 PrefabStage.prefabSaving -= DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
|                 PrefabStage.prefabStageClosing -= ForgetUnsavedNavMeshDataChanges; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,11 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: ce18366e52b6ecd4bb22b17b7c92a9b9 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,12 @@ | ||||
| { | ||||
|     "name": "NavMeshComponentsEditor", | ||||
|     "references": [ | ||||
|         "NavMeshComponents" | ||||
|     ], | ||||
|     "optionalUnityReferences": [], | ||||
|     "includePlatforms": [ | ||||
|         "Editor" | ||||
|     ], | ||||
|     "excludePlatforms": [], | ||||
|     "allowUnsafeCode": false | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 86c9d8e67265f41469be06142c397d17 | ||||
| AssemblyDefinitionImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,258 @@ | ||||
| using UnityEngine; | ||||
| using UnityEngine.AI; | ||||
|  | ||||
| namespace UnityEditor.AI | ||||
| { | ||||
|     public static class NavMeshComponentsGUIUtility | ||||
|     { | ||||
|         public static void AreaPopup(string labelName, SerializedProperty areaProperty) | ||||
|         { | ||||
|             var areaIndex = -1; | ||||
|             var areaNames = GameObjectUtility.GetNavMeshAreaNames(); | ||||
|             for (var i = 0; i < areaNames.Length; i++) | ||||
|             { | ||||
|                 var areaValue = GameObjectUtility.GetNavMeshAreaFromName(areaNames[i]); | ||||
|                 if (areaValue == areaProperty.intValue) | ||||
|                     areaIndex = i; | ||||
|             } | ||||
|             ArrayUtility.Add(ref areaNames, ""); | ||||
|             ArrayUtility.Add(ref areaNames, "Open Area Settings..."); | ||||
|  | ||||
|             var rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); | ||||
|             EditorGUI.BeginProperty(rect, GUIContent.none, areaProperty); | ||||
|  | ||||
|             EditorGUI.BeginChangeCheck(); | ||||
|             areaIndex = EditorGUI.Popup(rect, labelName, areaIndex, areaNames); | ||||
|  | ||||
|             if (EditorGUI.EndChangeCheck()) | ||||
|             { | ||||
|                 if (areaIndex >= 0 && areaIndex < areaNames.Length - 2) | ||||
|                     areaProperty.intValue = GameObjectUtility.GetNavMeshAreaFromName(areaNames[areaIndex]); | ||||
|                 else if (areaIndex == areaNames.Length - 1) | ||||
|                     NavMeshEditorHelpers.OpenAreaSettings(); | ||||
|             } | ||||
|  | ||||
|             EditorGUI.EndProperty(); | ||||
|         } | ||||
|  | ||||
|         public static void AgentTypePopup(string labelName, SerializedProperty agentTypeID) | ||||
|         { | ||||
|             var index = -1; | ||||
|             var count = NavMesh.GetSettingsCount(); | ||||
|             var agentTypeNames = new string[count + 2]; | ||||
|             for (var i = 0; i < count; i++) | ||||
|             { | ||||
|                 var id = NavMesh.GetSettingsByIndex(i).agentTypeID; | ||||
|                 var name = NavMesh.GetSettingsNameFromID(id); | ||||
|                 agentTypeNames[i] = name; | ||||
|                 if (id == agentTypeID.intValue) | ||||
|                     index = i; | ||||
|             } | ||||
|             agentTypeNames[count] = ""; | ||||
|             agentTypeNames[count + 1] = "Open Agent Settings..."; | ||||
|  | ||||
|             bool validAgentType = index != -1; | ||||
|             if (!validAgentType) | ||||
|             { | ||||
|                 EditorGUILayout.HelpBox("Agent Type invalid.", MessageType.Warning); | ||||
|             } | ||||
|  | ||||
|             var rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); | ||||
|             EditorGUI.BeginProperty(rect, GUIContent.none, agentTypeID); | ||||
|  | ||||
|             EditorGUI.BeginChangeCheck(); | ||||
|             index = EditorGUI.Popup(rect, labelName, index, agentTypeNames); | ||||
|             if (EditorGUI.EndChangeCheck()) | ||||
|             { | ||||
|                 if (index >= 0 && index < count) | ||||
|                 { | ||||
|                     var id = NavMesh.GetSettingsByIndex(index).agentTypeID; | ||||
|                     agentTypeID.intValue = id; | ||||
|                 } | ||||
|                 else if (index == count + 1) | ||||
|                 { | ||||
|                     NavMeshEditorHelpers.OpenAgentSettings(-1); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             EditorGUI.EndProperty(); | ||||
|         } | ||||
|  | ||||
|         // Agent mask is a set (internally array/list) of agentTypeIDs. | ||||
|         // It is used to describe which agents modifiers apply to. | ||||
|         // There is a special case of "None" which is an empty array. | ||||
|         // There is a special case of "All" which is an array of length 1, and value of -1. | ||||
|         public static void AgentMaskPopup(string labelName, SerializedProperty agentMask) | ||||
|         { | ||||
|             // Contents of the dropdown box. | ||||
|             string popupContent = ""; | ||||
|  | ||||
|             if (agentMask.hasMultipleDifferentValues) | ||||
|                 popupContent = "\u2014"; | ||||
|             else | ||||
|                 popupContent = GetAgentMaskLabelName(agentMask); | ||||
|  | ||||
|             var content = new GUIContent(popupContent); | ||||
|             var popupRect = GUILayoutUtility.GetRect(content, EditorStyles.popup); | ||||
|  | ||||
|             EditorGUI.BeginProperty(popupRect, GUIContent.none, agentMask); | ||||
|             popupRect = EditorGUI.PrefixLabel(popupRect, 0, new GUIContent(labelName)); | ||||
|             bool pressed = GUI.Button(popupRect, content, EditorStyles.popup); | ||||
|  | ||||
|             if (pressed) | ||||
|             { | ||||
|                 var show = !agentMask.hasMultipleDifferentValues; | ||||
|                 var showNone = show && agentMask.arraySize == 0; | ||||
|                 var showAll = show && IsAll(agentMask); | ||||
|  | ||||
|                 var menu = new GenericMenu(); | ||||
|                 menu.AddItem(new GUIContent("None"), showNone, SetAgentMaskNone, agentMask); | ||||
|                 menu.AddItem(new GUIContent("All"), showAll, SetAgentMaskAll, agentMask); | ||||
|                 menu.AddSeparator(""); | ||||
|  | ||||
|                 var count = NavMesh.GetSettingsCount(); | ||||
|                 for (var i = 0; i < count; i++) | ||||
|                 { | ||||
|                     var id = NavMesh.GetSettingsByIndex(i).agentTypeID; | ||||
|                     var sname = NavMesh.GetSettingsNameFromID(id); | ||||
|  | ||||
|                     var showSelected = show && AgentMaskHasSelectedAgentTypeID(agentMask, id); | ||||
|                     var userData = new object[] { agentMask, id, !showSelected }; | ||||
|                     menu.AddItem(new GUIContent(sname), showSelected, ToggleAgentMaskItem, userData); | ||||
|                 } | ||||
|  | ||||
|                 menu.DropDown(popupRect); | ||||
|             } | ||||
|  | ||||
|             EditorGUI.EndProperty(); | ||||
|         } | ||||
|  | ||||
|         public static GameObject CreateAndSelectGameObject(string suggestedName, GameObject parent) | ||||
|         { | ||||
|             var parentTransform = parent != null ? parent.transform : null; | ||||
|             var uniqueName = GameObjectUtility.GetUniqueNameForSibling(parentTransform, suggestedName); | ||||
|             var child = new GameObject(uniqueName); | ||||
|  | ||||
|             Undo.RegisterCreatedObjectUndo(child, "Create " + uniqueName); | ||||
|             if (parentTransform != null) | ||||
|                 Undo.SetTransformParent(child.transform, parentTransform, "Parent " + uniqueName); | ||||
|  | ||||
|             Selection.activeGameObject = child; | ||||
|  | ||||
|             return child; | ||||
|         } | ||||
|  | ||||
|         static bool IsAll(SerializedProperty agentMask) | ||||
|         { | ||||
|             return agentMask.arraySize == 1 && agentMask.GetArrayElementAtIndex(0).intValue == -1; | ||||
|         } | ||||
|  | ||||
|         static void ToggleAgentMaskItem(object userData) | ||||
|         { | ||||
|             var args = (object[])userData; | ||||
|             var agentMask = (SerializedProperty)args[0]; | ||||
|             var agentTypeID = (int)args[1]; | ||||
|             var value = (bool)args[2]; | ||||
|  | ||||
|             ToggleAgentMaskItem(agentMask, agentTypeID, value); | ||||
|         } | ||||
|  | ||||
|         static void ToggleAgentMaskItem(SerializedProperty agentMask, int agentTypeID, bool value) | ||||
|         { | ||||
|             if (agentMask.hasMultipleDifferentValues) | ||||
|             { | ||||
|                 agentMask.ClearArray(); | ||||
|                 agentMask.serializedObject.ApplyModifiedProperties(); | ||||
|             } | ||||
|  | ||||
|             // Find which index this agent type is in the agentMask array. | ||||
|             int idx = -1; | ||||
|             for (var j = 0; j < agentMask.arraySize; j++) | ||||
|             { | ||||
|                 var elem = agentMask.GetArrayElementAtIndex(j); | ||||
|                 if (elem.intValue == agentTypeID) | ||||
|                     idx = j; | ||||
|             } | ||||
|  | ||||
|             // Handle "All" special case. | ||||
|             if (IsAll(agentMask)) | ||||
|             { | ||||
|                 agentMask.DeleteArrayElementAtIndex(0); | ||||
|             } | ||||
|  | ||||
|             // Toggle value. | ||||
|             if (value) | ||||
|             { | ||||
|                 if (idx == -1) | ||||
|                 { | ||||
|                     agentMask.InsertArrayElementAtIndex(agentMask.arraySize); | ||||
|                     agentMask.GetArrayElementAtIndex(agentMask.arraySize - 1).intValue = agentTypeID; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (idx != -1) | ||||
|                 { | ||||
|                     agentMask.DeleteArrayElementAtIndex(idx); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             agentMask.serializedObject.ApplyModifiedProperties(); | ||||
|         } | ||||
|  | ||||
|         static void SetAgentMaskNone(object data) | ||||
|         { | ||||
|             var agentMask = (SerializedProperty)data; | ||||
|             agentMask.ClearArray(); | ||||
|             agentMask.serializedObject.ApplyModifiedProperties(); | ||||
|         } | ||||
|  | ||||
|         static void SetAgentMaskAll(object data) | ||||
|         { | ||||
|             var agentMask = (SerializedProperty)data; | ||||
|             agentMask.ClearArray(); | ||||
|             agentMask.InsertArrayElementAtIndex(0); | ||||
|             agentMask.GetArrayElementAtIndex(0).intValue = -1; | ||||
|             agentMask.serializedObject.ApplyModifiedProperties(); | ||||
|         } | ||||
|  | ||||
|         static string GetAgentMaskLabelName(SerializedProperty agentMask) | ||||
|         { | ||||
|             if (agentMask.arraySize == 0) | ||||
|                 return "None"; | ||||
|  | ||||
|             if (IsAll(agentMask)) | ||||
|                 return "All"; | ||||
|  | ||||
|             if (agentMask.arraySize <= 3) | ||||
|             { | ||||
|                 var labelName = ""; | ||||
|                 for (var j = 0; j < agentMask.arraySize; j++) | ||||
|                 { | ||||
|                     var elem = agentMask.GetArrayElementAtIndex(j); | ||||
|                     var settingsName = NavMesh.GetSettingsNameFromID(elem.intValue); | ||||
|                     if (string.IsNullOrEmpty(settingsName)) | ||||
|                         continue; | ||||
|  | ||||
|                     if (labelName.Length > 0) | ||||
|                         labelName += ", "; | ||||
|                     labelName += settingsName; | ||||
|                 } | ||||
|                 return labelName; | ||||
|             } | ||||
|  | ||||
|             return "Mixed..."; | ||||
|         } | ||||
|  | ||||
|         static bool AgentMaskHasSelectedAgentTypeID(SerializedProperty agentMask, int agentTypeID) | ||||
|         { | ||||
|             for (var j = 0; j < agentMask.arraySize; j++) | ||||
|             { | ||||
|                 var elem = agentMask.GetArrayElementAtIndex(j); | ||||
|                 if (elem.intValue == agentTypeID) | ||||
|                     return true; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 77fba670b979046f18d52d751e0d4659 | ||||
| timeCreated: 1480524815 | ||||
| licenseType: Pro | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
							
								
								
									
										279
									
								
								Assets/NavMeshPlus/NavMeshComponents/Editor/NavMeshLinkEditor.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								Assets/NavMeshPlus/NavMeshComponents/Editor/NavMeshLinkEditor.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,279 @@ | ||||
| using UnityEngine; | ||||
| using UnityEngine.AI; | ||||
|  | ||||
| namespace UnityEditor.AI | ||||
| { | ||||
|     [CanEditMultipleObjects] | ||||
|     [CustomEditor(typeof(NavMeshLink))] | ||||
|     class NavMeshLinkEditor : Editor | ||||
|     { | ||||
|         SerializedProperty m_AgentTypeID; | ||||
|         SerializedProperty m_Area; | ||||
|         SerializedProperty m_CostModifier; | ||||
|         SerializedProperty m_AutoUpdatePosition; | ||||
|         SerializedProperty m_Bidirectional; | ||||
|         SerializedProperty m_EndPoint; | ||||
|         SerializedProperty m_StartPoint; | ||||
|         SerializedProperty m_Width; | ||||
|  | ||||
|         static int s_SelectedID; | ||||
|         static int s_SelectedPoint = -1; | ||||
|  | ||||
|         static Color s_HandleColor = new Color(255f, 167f, 39f, 210f) / 255; | ||||
|         static Color s_HandleColorDisabled = new Color(255f * 0.75f, 167f * 0.75f, 39f * 0.75f, 100f) / 255; | ||||
|  | ||||
|         void OnEnable() | ||||
|         { | ||||
|             m_AgentTypeID = serializedObject.FindProperty("m_AgentTypeID"); | ||||
|             m_Area = serializedObject.FindProperty("m_Area"); | ||||
|             m_CostModifier = serializedObject.FindProperty("m_CostModifier"); | ||||
|             m_AutoUpdatePosition = serializedObject.FindProperty("m_AutoUpdatePosition"); | ||||
|             m_Bidirectional = serializedObject.FindProperty("m_Bidirectional"); | ||||
|             m_EndPoint = serializedObject.FindProperty("m_EndPoint"); | ||||
|             m_StartPoint = serializedObject.FindProperty("m_StartPoint"); | ||||
|             m_Width = serializedObject.FindProperty("m_Width"); | ||||
|  | ||||
|             s_SelectedID = 0; | ||||
|             s_SelectedPoint = -1; | ||||
|  | ||||
|             NavMeshVisualizationSettings.showNavigation++; | ||||
|         } | ||||
|  | ||||
|         void OnDisable() | ||||
|         { | ||||
|             NavMeshVisualizationSettings.showNavigation--; | ||||
|         } | ||||
|  | ||||
|         static Matrix4x4 UnscaledLocalToWorldMatrix(Transform t) | ||||
|         { | ||||
|             return Matrix4x4.TRS(t.position, t.rotation, Vector3.one); | ||||
|         } | ||||
|  | ||||
|         void AlignTransformToEndPoints(NavMeshLink navLink) | ||||
|         { | ||||
|             var mat = UnscaledLocalToWorldMatrix(navLink.transform); | ||||
|  | ||||
|             var worldStartPt = mat.MultiplyPoint(navLink.startPoint); | ||||
|             var worldEndPt = mat.MultiplyPoint(navLink.endPoint); | ||||
|  | ||||
|             var forward = worldEndPt - worldStartPt; | ||||
|             var up = navLink.transform.up; | ||||
|  | ||||
|             // Flatten | ||||
|             forward -= Vector3.Dot(up, forward) * up; | ||||
|  | ||||
|             var transform = navLink.transform; | ||||
|             transform.rotation = Quaternion.LookRotation(forward, up); | ||||
|             transform.position = (worldEndPt + worldStartPt) * 0.5f; | ||||
|             transform.localScale = Vector3.one; | ||||
|  | ||||
|             navLink.startPoint = transform.InverseTransformPoint(worldStartPt); | ||||
|             navLink.endPoint = transform.InverseTransformPoint(worldEndPt); | ||||
|         } | ||||
|  | ||||
|         public override void OnInspectorGUI() | ||||
|         { | ||||
|             serializedObject.Update(); | ||||
|  | ||||
|             NavMeshComponentsGUIUtility.AgentTypePopup("Agent Type", m_AgentTypeID); | ||||
|             EditorGUILayout.Space(); | ||||
|  | ||||
|             EditorGUILayout.PropertyField(m_StartPoint); | ||||
|             EditorGUILayout.PropertyField(m_EndPoint); | ||||
|  | ||||
|             GUILayout.BeginHorizontal(); | ||||
|             GUILayout.Space(EditorGUIUtility.labelWidth); | ||||
|             if (GUILayout.Button("Swap")) | ||||
|             { | ||||
|                 foreach (NavMeshLink navLink in targets) | ||||
|                 { | ||||
|                     var tmp = navLink.startPoint; | ||||
|                     navLink.startPoint = navLink.endPoint; | ||||
|                     navLink.endPoint = tmp; | ||||
|                 } | ||||
|                 SceneView.RepaintAll(); | ||||
|             } | ||||
|             if (GUILayout.Button("Align Transform")) | ||||
|             { | ||||
|                 foreach (NavMeshLink navLink in targets) | ||||
|                 { | ||||
|                     Undo.RecordObject(navLink.transform, "Align Transform to End Points"); | ||||
|                     Undo.RecordObject(navLink, "Align Transform to End Points"); | ||||
|                     AlignTransformToEndPoints(navLink); | ||||
|                 } | ||||
|                 SceneView.RepaintAll(); | ||||
|             } | ||||
|             GUILayout.EndHorizontal(); | ||||
|             EditorGUILayout.Space(); | ||||
|  | ||||
|             EditorGUILayout.PropertyField(m_Width); | ||||
|             EditorGUILayout.PropertyField(m_CostModifier); | ||||
|             EditorGUILayout.PropertyField(m_AutoUpdatePosition); | ||||
|             EditorGUILayout.PropertyField(m_Bidirectional); | ||||
|  | ||||
|             NavMeshComponentsGUIUtility.AreaPopup("Area Type", m_Area); | ||||
|  | ||||
|             serializedObject.ApplyModifiedProperties(); | ||||
|  | ||||
|             EditorGUILayout.Space(); | ||||
|         } | ||||
|  | ||||
|         static Vector3 CalcLinkRight(NavMeshLink navLink) | ||||
|         { | ||||
|             var dir = navLink.endPoint - navLink.startPoint; | ||||
|             return (new Vector3(-dir.z, 0.0f, dir.x)).normalized; | ||||
|         } | ||||
|  | ||||
|         static void DrawLink(NavMeshLink navLink) | ||||
|         { | ||||
|             var right = CalcLinkRight(navLink); | ||||
|             var rad = navLink.width * 0.5f; | ||||
|  | ||||
|             Gizmos.DrawLine(navLink.startPoint - right * rad, navLink.startPoint + right * rad); | ||||
|             Gizmos.DrawLine(navLink.endPoint - right * rad, navLink.endPoint + right * rad); | ||||
|             Gizmos.DrawLine(navLink.startPoint - right * rad, navLink.endPoint - right * rad); | ||||
|             Gizmos.DrawLine(navLink.startPoint + right * rad, navLink.endPoint + right * rad); | ||||
|         } | ||||
|  | ||||
|         [DrawGizmo(GizmoType.Selected | GizmoType.Active | GizmoType.Pickable)] | ||||
|         static void RenderBoxGizmo(NavMeshLink navLink, GizmoType gizmoType) | ||||
|         { | ||||
|             if (!EditorApplication.isPlaying) | ||||
|                 navLink.UpdateLink(); | ||||
|  | ||||
|             var color = s_HandleColor; | ||||
|             if (!navLink.enabled) | ||||
|                 color = s_HandleColorDisabled; | ||||
|  | ||||
|             var oldColor = Gizmos.color; | ||||
|             var oldMatrix = Gizmos.matrix; | ||||
|  | ||||
|             Gizmos.matrix = UnscaledLocalToWorldMatrix(navLink.transform); | ||||
|  | ||||
|             Gizmos.color = color; | ||||
|             DrawLink(navLink); | ||||
|  | ||||
|             Gizmos.matrix = oldMatrix; | ||||
|             Gizmos.color = oldColor; | ||||
|  | ||||
|             Gizmos.DrawIcon(navLink.transform.position, "NavMeshLink Icon", true); | ||||
|         } | ||||
|  | ||||
|         [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.Pickable)] | ||||
|         static void RenderBoxGizmoNotSelected(NavMeshLink navLink, GizmoType gizmoType) | ||||
|         { | ||||
|             if (NavMeshVisualizationSettings.showNavigation > 0) | ||||
|             { | ||||
|                 var color = s_HandleColor; | ||||
|                 if (!navLink.enabled) | ||||
|                     color = s_HandleColorDisabled; | ||||
|  | ||||
|                 var oldColor = Gizmos.color; | ||||
|                 var oldMatrix = Gizmos.matrix; | ||||
|  | ||||
|                 Gizmos.matrix = UnscaledLocalToWorldMatrix(navLink.transform); | ||||
|  | ||||
|                 Gizmos.color = color; | ||||
|                 DrawLink(navLink); | ||||
|  | ||||
|                 Gizmos.matrix = oldMatrix; | ||||
|                 Gizmos.color = oldColor; | ||||
|             } | ||||
|  | ||||
|             Gizmos.DrawIcon(navLink.transform.position, "NavMeshLink Icon", true); | ||||
|         } | ||||
|  | ||||
|         public void OnSceneGUI() | ||||
|         { | ||||
|             var navLink = (NavMeshLink)target; | ||||
|             if (!navLink.enabled) | ||||
|                 return; | ||||
|  | ||||
|             var mat = UnscaledLocalToWorldMatrix(navLink.transform); | ||||
|  | ||||
|             var startPt = mat.MultiplyPoint(navLink.startPoint); | ||||
|             var endPt = mat.MultiplyPoint(navLink.endPoint); | ||||
|             var midPt = Vector3.Lerp(startPt, endPt, 0.35f); | ||||
|             var startSize = HandleUtility.GetHandleSize(startPt); | ||||
|             var endSize = HandleUtility.GetHandleSize(endPt); | ||||
|             var midSize = HandleUtility.GetHandleSize(midPt); | ||||
|  | ||||
|             var zup = Quaternion.FromToRotation(Vector3.forward, Vector3.up); | ||||
|             var right = mat.MultiplyVector(CalcLinkRight(navLink)); | ||||
|  | ||||
|             var oldColor = Handles.color; | ||||
|             Handles.color = s_HandleColor; | ||||
|  | ||||
|             Vector3 pos; | ||||
|  | ||||
|             if (navLink.GetInstanceID() == s_SelectedID && s_SelectedPoint == 0) | ||||
|             { | ||||
|                 EditorGUI.BeginChangeCheck(); | ||||
|                 Handles.CubeHandleCap(0, startPt, zup, 0.1f * startSize, Event.current.type); | ||||
|                 pos = Handles.PositionHandle(startPt, navLink.transform.rotation); | ||||
|                 if (EditorGUI.EndChangeCheck()) | ||||
|                 { | ||||
|                     Undo.RecordObject(navLink, "Move link point"); | ||||
|                     navLink.startPoint = mat.inverse.MultiplyPoint(pos); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (Handles.Button(startPt, zup, 0.1f * startSize, 0.1f * startSize, Handles.CubeHandleCap)) | ||||
|                 { | ||||
|                     s_SelectedPoint = 0; | ||||
|                     s_SelectedID = navLink.GetInstanceID(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (navLink.GetInstanceID() == s_SelectedID && s_SelectedPoint == 1) | ||||
|             { | ||||
|                 EditorGUI.BeginChangeCheck(); | ||||
|                 Handles.CubeHandleCap(0, endPt, zup, 0.1f * startSize, Event.current.type); | ||||
|                 pos = Handles.PositionHandle(endPt, navLink.transform.rotation); | ||||
|                 if (EditorGUI.EndChangeCheck()) | ||||
|                 { | ||||
|                     Undo.RecordObject(navLink, "Move link point"); | ||||
|                     navLink.endPoint = mat.inverse.MultiplyPoint(pos); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (Handles.Button(endPt, zup, 0.1f * endSize, 0.1f * endSize, Handles.CubeHandleCap)) | ||||
|                 { | ||||
|                     s_SelectedPoint = 1; | ||||
|                     s_SelectedID = navLink.GetInstanceID(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             EditorGUI.BeginChangeCheck(); | ||||
|             pos = Handles.Slider(midPt + right * navLink.width * 0.5f, right, midSize * 0.03f, Handles.DotHandleCap, 0); | ||||
|             if (EditorGUI.EndChangeCheck()) | ||||
|             { | ||||
|                 Undo.RecordObject(navLink, "Adjust link width"); | ||||
|                 navLink.width = Mathf.Max(0.0f, 2.0f * Vector3.Dot(right, (pos - midPt))); | ||||
|             } | ||||
|  | ||||
|             EditorGUI.BeginChangeCheck(); | ||||
|             pos = Handles.Slider(midPt - right * navLink.width * 0.5f, -right, midSize * 0.03f, Handles.DotHandleCap, 0); | ||||
|             if (EditorGUI.EndChangeCheck()) | ||||
|             { | ||||
|                 Undo.RecordObject(navLink, "Adjust link width"); | ||||
|                 navLink.width = Mathf.Max(0.0f, 2.0f * Vector3.Dot(-right, (pos - midPt))); | ||||
|             } | ||||
|  | ||||
|             Handles.color = oldColor; | ||||
|         } | ||||
|  | ||||
|         [MenuItem("GameObject/AI/NavMesh Link", false, 2002)] | ||||
|         static public void CreateNavMeshLink(MenuCommand menuCommand) | ||||
|         { | ||||
|             var parent = menuCommand.context as GameObject; | ||||
|             GameObject go = NavMeshComponentsGUIUtility.CreateAndSelectGameObject("NavMesh Link", parent); | ||||
|             go.AddComponent<NavMeshLink>(); | ||||
|             var view = SceneView.lastActiveSceneView; | ||||
|             if (view != null) | ||||
|                 view.MoveToView(go.transform); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: ece1e865d1ad84587872fe8580ab5a20 | ||||
| timeCreated: 1477036743 | ||||
| licenseType: Pro | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,49 @@ | ||||
| using UnityEngine.AI; | ||||
|  | ||||
| namespace UnityEditor.AI | ||||
| { | ||||
|     [CanEditMultipleObjects] | ||||
|     [CustomEditor(typeof(NavMeshModifier))] | ||||
|     class NavMeshModifierEditor : Editor | ||||
|     { | ||||
|         SerializedProperty m_AffectedAgents; | ||||
|         SerializedProperty m_Area; | ||||
|         SerializedProperty m_IgnoreFromBuild; | ||||
|         SerializedProperty m_OverrideArea; | ||||
|  | ||||
|         void OnEnable() | ||||
|         { | ||||
|             m_AffectedAgents = serializedObject.FindProperty("m_AffectedAgents"); | ||||
|             m_Area = serializedObject.FindProperty("m_Area"); | ||||
|             m_IgnoreFromBuild = serializedObject.FindProperty("m_IgnoreFromBuild"); | ||||
|             m_OverrideArea = serializedObject.FindProperty("m_OverrideArea"); | ||||
|  | ||||
|             NavMeshVisualizationSettings.showNavigation++; | ||||
|         } | ||||
|  | ||||
|         void OnDisable() | ||||
|         { | ||||
|             NavMeshVisualizationSettings.showNavigation--; | ||||
|         } | ||||
|  | ||||
|         public override void OnInspectorGUI() | ||||
|         { | ||||
|             serializedObject.Update(); | ||||
|  | ||||
|             EditorGUILayout.PropertyField(m_IgnoreFromBuild); | ||||
|  | ||||
|             EditorGUILayout.PropertyField(m_OverrideArea); | ||||
|             if (m_OverrideArea.boolValue) | ||||
|             { | ||||
|                 EditorGUI.indentLevel++; | ||||
|                 NavMeshComponentsGUIUtility.AreaPopup("Area Type", m_Area); | ||||
|                 EditorGUI.indentLevel--; | ||||
|             } | ||||
|  | ||||
|             NavMeshComponentsGUIUtility.AgentMaskPopup("Affected Agents", m_AffectedAgents); | ||||
|             EditorGUILayout.Space(); | ||||
|  | ||||
|             serializedObject.ApplyModifiedProperties(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 6fa04b4743e3947eba4d7b9e5832ea69 | ||||
| timeCreated: 1477036742 | ||||
| licenseType: Pro | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,146 @@ | ||||
| using UnityEditor.IMGUI.Controls; | ||||
| using UnityEditorInternal; | ||||
| using UnityEngine.AI; | ||||
| using UnityEngine; | ||||
|  | ||||
| namespace UnityEditor.AI | ||||
| { | ||||
|     [CanEditMultipleObjects] | ||||
|     [CustomEditor(typeof(NavMeshModifierVolume))] | ||||
|     class NavMeshModifierVolumeEditor : Editor | ||||
|     { | ||||
|         SerializedProperty m_AffectedAgents; | ||||
|         SerializedProperty m_Area; | ||||
|         SerializedProperty m_Center; | ||||
|         SerializedProperty m_Size; | ||||
|  | ||||
|         static Color s_HandleColor = new Color(187f, 138f, 240f, 210f) / 255; | ||||
|         static Color s_HandleColorDisabled = new Color(187f * 0.75f, 138f * 0.75f, 240f * 0.75f, 100f) / 255; | ||||
|  | ||||
|         BoxBoundsHandle m_BoundsHandle = new BoxBoundsHandle(); | ||||
|  | ||||
|         bool editingCollider | ||||
|         { | ||||
|             get { return EditMode.editMode == EditMode.SceneViewEditMode.Collider && EditMode.IsOwner(this); } | ||||
|         } | ||||
|  | ||||
|         void OnEnable() | ||||
|         { | ||||
|             m_AffectedAgents = serializedObject.FindProperty("m_AffectedAgents"); | ||||
|             m_Area = serializedObject.FindProperty("m_Area"); | ||||
|             m_Center = serializedObject.FindProperty("m_Center"); | ||||
|             m_Size = serializedObject.FindProperty("m_Size"); | ||||
|  | ||||
|             NavMeshVisualizationSettings.showNavigation++; | ||||
|         } | ||||
|  | ||||
|         void OnDisable() | ||||
|         { | ||||
|             NavMeshVisualizationSettings.showNavigation--; | ||||
|         } | ||||
|  | ||||
|         Bounds GetBounds() | ||||
|         { | ||||
|             var navModifier = (NavMeshModifierVolume)target; | ||||
|             return new Bounds(navModifier.transform.position, navModifier.size); | ||||
|         } | ||||
|  | ||||
|         public override void OnInspectorGUI() | ||||
|         { | ||||
|             serializedObject.Update(); | ||||
|  | ||||
|             EditMode.DoEditModeInspectorModeButton(EditMode.SceneViewEditMode.Collider, "Edit Volume", | ||||
|                 EditorGUIUtility.IconContent("EditCollider"), GetBounds, this); | ||||
|  | ||||
|             EditorGUILayout.PropertyField(m_Size); | ||||
|             EditorGUILayout.PropertyField(m_Center); | ||||
|  | ||||
|             NavMeshComponentsGUIUtility.AreaPopup("Area Type", m_Area); | ||||
|             NavMeshComponentsGUIUtility.AgentMaskPopup("Affected Agents", m_AffectedAgents); | ||||
|             EditorGUILayout.Space(); | ||||
|  | ||||
|             serializedObject.ApplyModifiedProperties(); | ||||
|         } | ||||
|  | ||||
|         [DrawGizmo(GizmoType.Selected | GizmoType.Active)] | ||||
|         static void RenderBoxGizmo(NavMeshModifierVolume navModifier, GizmoType gizmoType) | ||||
|         { | ||||
|             var color = navModifier.enabled ? s_HandleColor : s_HandleColorDisabled; | ||||
|             var colorTrans = new Color(color.r * 0.75f, color.g * 0.75f, color.b * 0.75f, color.a * 0.15f); | ||||
|  | ||||
|             var oldColor = Gizmos.color; | ||||
|             var oldMatrix = Gizmos.matrix; | ||||
|  | ||||
|             Gizmos.matrix = navModifier.transform.localToWorldMatrix; | ||||
|  | ||||
|             Gizmos.color = colorTrans; | ||||
|             Gizmos.DrawCube(navModifier.center, navModifier.size); | ||||
|  | ||||
|             Gizmos.color = color; | ||||
|             Gizmos.DrawWireCube(navModifier.center, navModifier.size); | ||||
|  | ||||
|             Gizmos.matrix = oldMatrix; | ||||
|             Gizmos.color = oldColor; | ||||
|  | ||||
|             Gizmos.DrawIcon(navModifier.transform.position, "NavMeshModifierVolume Icon", true); | ||||
|         } | ||||
|  | ||||
|         [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.Pickable)] | ||||
|         static void RenderBoxGizmoNotSelected(NavMeshModifierVolume navModifier, GizmoType gizmoType) | ||||
|         { | ||||
|             if (NavMeshVisualizationSettings.showNavigation > 0) | ||||
|             { | ||||
|                 var color = navModifier.enabled ? s_HandleColor : s_HandleColorDisabled; | ||||
|                 var oldColor = Gizmos.color; | ||||
|                 var oldMatrix = Gizmos.matrix; | ||||
|  | ||||
|                 Gizmos.matrix = navModifier.transform.localToWorldMatrix; | ||||
|  | ||||
|                 Gizmos.color = color; | ||||
|                 Gizmos.DrawWireCube(navModifier.center, navModifier.size); | ||||
|  | ||||
|                 Gizmos.matrix = oldMatrix; | ||||
|                 Gizmos.color = oldColor; | ||||
|             } | ||||
|  | ||||
|             Gizmos.DrawIcon(navModifier.transform.position, "NavMeshModifierVolume Icon", true); | ||||
|         } | ||||
|  | ||||
|         void OnSceneGUI() | ||||
|         { | ||||
|             if (!editingCollider) | ||||
|                 return; | ||||
|  | ||||
|             var vol = (NavMeshModifierVolume)target; | ||||
|             var color = vol.enabled ? s_HandleColor : s_HandleColorDisabled; | ||||
|             using (new Handles.DrawingScope(color, vol.transform.localToWorldMatrix)) | ||||
|             { | ||||
|                 m_BoundsHandle.center = vol.center; | ||||
|                 m_BoundsHandle.size = vol.size; | ||||
|  | ||||
|                 EditorGUI.BeginChangeCheck(); | ||||
|                 m_BoundsHandle.DrawHandle(); | ||||
|                 if (EditorGUI.EndChangeCheck()) | ||||
|                 { | ||||
|                     Undo.RecordObject(vol, "Modified NavMesh Modifier Volume"); | ||||
|                     Vector3 center = m_BoundsHandle.center; | ||||
|                     Vector3 size = m_BoundsHandle.size; | ||||
|                     vol.center = center; | ||||
|                     vol.size = size; | ||||
|                     EditorUtility.SetDirty(target); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [MenuItem("GameObject/AI/NavMesh Modifier Volume", false, 2001)] | ||||
|         static public void CreateNavMeshModifierVolume(MenuCommand menuCommand) | ||||
|         { | ||||
|             var parent = menuCommand.context as GameObject; | ||||
|             var go = NavMeshComponentsGUIUtility.CreateAndSelectGameObject("NavMesh Modifier Volume", parent); | ||||
|             go.AddComponent<NavMeshModifierVolume>(); | ||||
|             var view = SceneView.lastActiveSceneView; | ||||
|             if (view != null) | ||||
|                 view.MoveToView(go.transform); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: c0f3bef2a67ae4e139538afec3e59b03 | ||||
| timeCreated: 1477036743 | ||||
| licenseType: Pro | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,400 @@ | ||||
| #define NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF | ||||
|  | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using UnityEditor.Experimental.SceneManagement; | ||||
| using UnityEditor.IMGUI.Controls; | ||||
| using UnityEditor.SceneManagement; | ||||
| using UnityEditorInternal; | ||||
| using UnityEngine.AI; | ||||
| using UnityEngine; | ||||
|  | ||||
| namespace UnityEditor.AI | ||||
| { | ||||
|     [CanEditMultipleObjects] | ||||
|     [CustomEditor(typeof(NavMeshSurface))] | ||||
|     class NavMeshSurfaceEditor : Editor | ||||
|     { | ||||
|         SerializedProperty m_AgentTypeID; | ||||
|         SerializedProperty m_BuildHeightMesh; | ||||
|         SerializedProperty m_Center; | ||||
|         SerializedProperty m_CollectObjects; | ||||
|         SerializedProperty m_DefaultArea; | ||||
|         SerializedProperty m_LayerMask; | ||||
|         SerializedProperty m_OverrideTileSize; | ||||
|         SerializedProperty m_OverrideVoxelSize; | ||||
|         SerializedProperty m_Size; | ||||
|         SerializedProperty m_TileSize; | ||||
|         SerializedProperty m_UseGeometry; | ||||
|         SerializedProperty m_VoxelSize; | ||||
|  | ||||
| #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF | ||||
|         SerializedProperty m_NavMeshData; | ||||
| #endif | ||||
|         class Styles | ||||
|         { | ||||
|             public readonly GUIContent m_LayerMask = new GUIContent("Include Layers"); | ||||
|  | ||||
|             public readonly GUIContent m_ShowInputGeom = new GUIContent("Show Input Geom"); | ||||
|             public readonly GUIContent m_ShowVoxels = new GUIContent("Show Voxels"); | ||||
|             public readonly GUIContent m_ShowRegions = new GUIContent("Show Regions"); | ||||
|             public readonly GUIContent m_ShowRawContours = new GUIContent("Show Raw Contours"); | ||||
|             public readonly GUIContent m_ShowContours = new GUIContent("Show Contours"); | ||||
|             public readonly GUIContent m_ShowPolyMesh = new GUIContent("Show Poly Mesh"); | ||||
|             public readonly GUIContent m_ShowPolyMeshDetail = new GUIContent("Show Poly Mesh Detail"); | ||||
|         } | ||||
|  | ||||
|         static Styles s_Styles; | ||||
|  | ||||
|         static bool s_ShowDebugOptions; | ||||
|  | ||||
|         static Color s_HandleColor = new Color(127f, 214f, 244f, 100f) / 255; | ||||
|         static Color s_HandleColorSelected = new Color(127f, 214f, 244f, 210f) / 255; | ||||
|         static Color s_HandleColorDisabled = new Color(127f * 0.75f, 214f * 0.75f, 244f * 0.75f, 100f) / 255; | ||||
|  | ||||
|         BoxBoundsHandle m_BoundsHandle = new BoxBoundsHandle(); | ||||
|  | ||||
|         bool editingCollider | ||||
|         { | ||||
|             get { return EditMode.editMode == EditMode.SceneViewEditMode.Collider && EditMode.IsOwner(this); } | ||||
|         } | ||||
|  | ||||
|         void OnEnable() | ||||
|         { | ||||
|             m_AgentTypeID = serializedObject.FindProperty("m_AgentTypeID"); | ||||
|             m_BuildHeightMesh = serializedObject.FindProperty("m_BuildHeightMesh"); | ||||
|             m_Center = serializedObject.FindProperty("m_Center"); | ||||
|             m_CollectObjects = serializedObject.FindProperty("m_CollectObjects"); | ||||
|             m_DefaultArea = serializedObject.FindProperty("m_DefaultArea"); | ||||
|             m_LayerMask = serializedObject.FindProperty("m_LayerMask"); | ||||
|             m_OverrideTileSize = serializedObject.FindProperty("m_OverrideTileSize"); | ||||
|             m_OverrideVoxelSize = serializedObject.FindProperty("m_OverrideVoxelSize"); | ||||
|             m_Size = serializedObject.FindProperty("m_Size"); | ||||
|             m_TileSize = serializedObject.FindProperty("m_TileSize"); | ||||
|             m_UseGeometry = serializedObject.FindProperty("m_UseGeometry"); | ||||
|             m_VoxelSize = serializedObject.FindProperty("m_VoxelSize"); | ||||
|  | ||||
| #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF | ||||
|             m_NavMeshData = serializedObject.FindProperty("m_NavMeshData"); | ||||
| #endif | ||||
|             NavMeshVisualizationSettings.showNavigation++; | ||||
|         } | ||||
|  | ||||
|         void OnDisable() | ||||
|         { | ||||
|             NavMeshVisualizationSettings.showNavigation--; | ||||
|         } | ||||
|  | ||||
|         Bounds GetBounds() | ||||
|         { | ||||
|             var navSurface = (NavMeshSurface)target; | ||||
|             return new Bounds(navSurface.transform.position, navSurface.size); | ||||
|         } | ||||
|  | ||||
|         public override void OnInspectorGUI() | ||||
|         { | ||||
|             if (s_Styles == null) | ||||
|                 s_Styles = new Styles(); | ||||
|  | ||||
|             serializedObject.Update(); | ||||
|  | ||||
|             var bs = NavMesh.GetSettingsByID(m_AgentTypeID.intValue); | ||||
|  | ||||
|             if (bs.agentTypeID != -1) | ||||
|             { | ||||
|                 // Draw image | ||||
|                 const float diagramHeight = 80.0f; | ||||
|                 Rect agentDiagramRect = EditorGUILayout.GetControlRect(false, diagramHeight); | ||||
|                 NavMeshEditorHelpers.DrawAgentDiagram(agentDiagramRect, bs.agentRadius, bs.agentHeight, bs.agentClimb, bs.agentSlope); | ||||
|             } | ||||
|             NavMeshComponentsGUIUtility.AgentTypePopup("Agent Type", m_AgentTypeID); | ||||
|  | ||||
|             EditorGUILayout.Space(); | ||||
|  | ||||
|             EditorGUILayout.PropertyField(m_CollectObjects); | ||||
|             if ((CollectObjects)m_CollectObjects.enumValueIndex == CollectObjects.Volume) | ||||
|             { | ||||
|                 EditorGUI.indentLevel++; | ||||
|  | ||||
|                 EditMode.DoEditModeInspectorModeButton(EditMode.SceneViewEditMode.Collider, "Edit Volume", | ||||
|                     EditorGUIUtility.IconContent("EditCollider"), GetBounds, this); | ||||
|                 EditorGUILayout.PropertyField(m_Size); | ||||
|                 EditorGUILayout.PropertyField(m_Center); | ||||
|  | ||||
|                 EditorGUI.indentLevel--; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (editingCollider) | ||||
|                     EditMode.QuitEditMode(); | ||||
|             } | ||||
|  | ||||
|             EditorGUILayout.PropertyField(m_LayerMask, s_Styles.m_LayerMask); | ||||
|             EditorGUILayout.PropertyField(m_UseGeometry); | ||||
|  | ||||
|             EditorGUILayout.Space(); | ||||
|  | ||||
|             m_OverrideVoxelSize.isExpanded = EditorGUILayout.Foldout(m_OverrideVoxelSize.isExpanded, "Advanced"); | ||||
|             if (m_OverrideVoxelSize.isExpanded) | ||||
|             { | ||||
|                 EditorGUI.indentLevel++; | ||||
|  | ||||
|                 NavMeshComponentsGUIUtility.AreaPopup("Default Area", m_DefaultArea); | ||||
|  | ||||
|                 // Override voxel size. | ||||
|                 EditorGUILayout.PropertyField(m_OverrideVoxelSize); | ||||
|  | ||||
|                 using (new EditorGUI.DisabledScope(!m_OverrideVoxelSize.boolValue || m_OverrideVoxelSize.hasMultipleDifferentValues)) | ||||
|                 { | ||||
|                     EditorGUI.indentLevel++; | ||||
|  | ||||
|                     EditorGUILayout.PropertyField(m_VoxelSize); | ||||
|  | ||||
|                     if (!m_OverrideVoxelSize.hasMultipleDifferentValues) | ||||
|                     { | ||||
|                         if (!m_AgentTypeID.hasMultipleDifferentValues) | ||||
|                         { | ||||
|                             float voxelsPerRadius = m_VoxelSize.floatValue > 0.0f ? (bs.agentRadius / m_VoxelSize.floatValue) : 0.0f; | ||||
|                             EditorGUILayout.LabelField(" ", voxelsPerRadius.ToString("0.00") + " voxels per agent radius", EditorStyles.miniLabel); | ||||
|                         } | ||||
|                         if (m_OverrideVoxelSize.boolValue) | ||||
|                             EditorGUILayout.HelpBox("Voxel size controls how accurately the navigation mesh is generated from the level geometry. A good voxel size is 2-4 voxels per agent radius. Making voxel size smaller will increase build time.", MessageType.None); | ||||
|                     } | ||||
|                     EditorGUI.indentLevel--; | ||||
|                 } | ||||
|  | ||||
|                 // Override tile size | ||||
|                 EditorGUILayout.PropertyField(m_OverrideTileSize); | ||||
|  | ||||
|                 using (new EditorGUI.DisabledScope(!m_OverrideTileSize.boolValue || m_OverrideTileSize.hasMultipleDifferentValues)) | ||||
|                 { | ||||
|                     EditorGUI.indentLevel++; | ||||
|  | ||||
|                     EditorGUILayout.PropertyField(m_TileSize); | ||||
|  | ||||
|                     if (!m_TileSize.hasMultipleDifferentValues && !m_VoxelSize.hasMultipleDifferentValues) | ||||
|                     { | ||||
|                         float tileWorldSize = m_TileSize.intValue * m_VoxelSize.floatValue; | ||||
|                         EditorGUILayout.LabelField(" ", tileWorldSize.ToString("0.00") + " world units", EditorStyles.miniLabel); | ||||
|                     } | ||||
|  | ||||
|                     if (!m_OverrideTileSize.hasMultipleDifferentValues) | ||||
|                     { | ||||
|                         if (m_OverrideTileSize.boolValue) | ||||
|                             EditorGUILayout.HelpBox("Tile size controls the how local the changes to the world are (rebuild or carve). Small tile size allows more local changes, while potentially generating more data overall.", MessageType.None); | ||||
|                     } | ||||
|                     EditorGUI.indentLevel--; | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 // Height mesh | ||||
|                 using (new EditorGUI.DisabledScope(true)) | ||||
|                 { | ||||
|                     EditorGUILayout.PropertyField(m_BuildHeightMesh); | ||||
|                 } | ||||
|  | ||||
|                 EditorGUILayout.Space(); | ||||
|                 EditorGUI.indentLevel--; | ||||
|             } | ||||
|  | ||||
|             EditorGUILayout.Space(); | ||||
|  | ||||
|             serializedObject.ApplyModifiedProperties(); | ||||
|  | ||||
|             var hadError = false; | ||||
|             var multipleTargets = targets.Length > 1; | ||||
|             foreach (NavMeshSurface navSurface in targets) | ||||
|             { | ||||
|                 var settings = navSurface.GetBuildSettings(); | ||||
|                 // Calculating bounds is potentially expensive when unbounded - so here we just use the center/size. | ||||
|                 // It means the validation is not checking vertical voxel limit correctly when the surface is set to something else than "in volume". | ||||
|                 var bounds = new Bounds(Vector3.zero, Vector3.zero); | ||||
|                 if (navSurface.collectObjects == CollectObjects.Volume) | ||||
|                 { | ||||
|                     bounds = new Bounds(navSurface.center, navSurface.size); | ||||
|                 } | ||||
|  | ||||
|                 var errors = settings.ValidationReport(bounds); | ||||
|                 if (errors.Length > 0) | ||||
|                 { | ||||
|                     if (multipleTargets) | ||||
|                         EditorGUILayout.LabelField(navSurface.name); | ||||
|                     foreach (var err in errors) | ||||
|                     { | ||||
|                         EditorGUILayout.HelpBox(err, MessageType.Warning); | ||||
|                     } | ||||
|                     GUILayout.BeginHorizontal(); | ||||
|                     GUILayout.Space(EditorGUIUtility.labelWidth); | ||||
|                     if (GUILayout.Button("Open Agent Settings...", EditorStyles.miniButton)) | ||||
|                         NavMeshEditorHelpers.OpenAgentSettings(navSurface.agentTypeID); | ||||
|                     GUILayout.EndHorizontal(); | ||||
|                     hadError = true; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (hadError) | ||||
|                 EditorGUILayout.Space(); | ||||
|  | ||||
| #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF | ||||
|             var nmdRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); | ||||
|  | ||||
|             EditorGUI.BeginProperty(nmdRect, GUIContent.none, m_NavMeshData); | ||||
|             var rectLabel = EditorGUI.PrefixLabel(nmdRect, GUIUtility.GetControlID(FocusType.Passive), new GUIContent(m_NavMeshData.displayName)); | ||||
|             EditorGUI.EndProperty(); | ||||
|  | ||||
|             using (new EditorGUI.DisabledScope(true)) | ||||
|             { | ||||
|                 EditorGUI.BeginProperty(nmdRect, GUIContent.none, m_NavMeshData); | ||||
|                 EditorGUI.ObjectField(rectLabel, m_NavMeshData, GUIContent.none); | ||||
|                 EditorGUI.EndProperty(); | ||||
|             } | ||||
| #endif | ||||
|             using (new EditorGUI.DisabledScope(Application.isPlaying || m_AgentTypeID.intValue == -1)) | ||||
|             { | ||||
|                 GUILayout.BeginHorizontal(); | ||||
|                 GUILayout.Space(EditorGUIUtility.labelWidth); | ||||
|                 if (GUILayout.Button("Clear")) | ||||
|                 { | ||||
|                     NavMeshAssetManager.instance.ClearSurfaces(targets); | ||||
|                     SceneView.RepaintAll(); | ||||
|                 } | ||||
|  | ||||
|                 if (GUILayout.Button("Bake")) | ||||
|                 { | ||||
|                     NavMeshAssetManager.instance.StartBakingSurfaces(targets); | ||||
|                 } | ||||
|  | ||||
|                 GUILayout.EndHorizontal(); | ||||
|             } | ||||
|  | ||||
|             // Show progress for the selected targets | ||||
|             var bakeOperations = NavMeshAssetManager.instance.GetBakeOperations(); | ||||
|             for (int i = bakeOperations.Count - 1; i >= 0; --i) | ||||
|             { | ||||
|                 if (!targets.Contains(bakeOperations[i].surface)) | ||||
|                     continue; | ||||
|  | ||||
|                 var oper = bakeOperations[i].bakeOperation; | ||||
|                 if (oper == null) | ||||
|                     continue; | ||||
|  | ||||
|                 var p = oper.progress; | ||||
|                 if (oper.isDone) | ||||
|                 { | ||||
|                     SceneView.RepaintAll(); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 GUILayout.BeginHorizontal(); | ||||
|  | ||||
|                 if (GUILayout.Button("Cancel", EditorStyles.miniButton)) | ||||
|                 { | ||||
|                     var bakeData = bakeOperations[i].bakeData; | ||||
|                     UnityEngine.AI.NavMeshBuilder.Cancel(bakeData); | ||||
|                     bakeOperations.RemoveAt(i); | ||||
|                 } | ||||
|  | ||||
|                 EditorGUI.ProgressBar(EditorGUILayout.GetControlRect(), p, "Baking: " + (int)(100 * p) + "%"); | ||||
|                 if (p <= 1) | ||||
|                     Repaint(); | ||||
|  | ||||
|                 GUILayout.EndHorizontal(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [DrawGizmo(GizmoType.Selected | GizmoType.Active | GizmoType.Pickable)] | ||||
|         static void RenderBoxGizmoSelected(NavMeshSurface navSurface, GizmoType gizmoType) | ||||
|         { | ||||
|             RenderBoxGizmo(navSurface, gizmoType, true); | ||||
|         } | ||||
|  | ||||
|         [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.Pickable)] | ||||
|         static void RenderBoxGizmoNotSelected(NavMeshSurface navSurface, GizmoType gizmoType) | ||||
|         { | ||||
|             if (NavMeshVisualizationSettings.showNavigation > 0) | ||||
|                 RenderBoxGizmo(navSurface, gizmoType, false); | ||||
|             else | ||||
|                 Gizmos.DrawIcon(navSurface.transform.position, "NavMeshSurface Icon", true); | ||||
|         } | ||||
|  | ||||
|         static void RenderBoxGizmo(NavMeshSurface navSurface, GizmoType gizmoType, bool selected) | ||||
|         { | ||||
|             var color = selected ? s_HandleColorSelected : s_HandleColor; | ||||
|             if (!navSurface.enabled) | ||||
|                 color = s_HandleColorDisabled; | ||||
|  | ||||
|             var oldColor = Gizmos.color; | ||||
|             var oldMatrix = Gizmos.matrix; | ||||
|  | ||||
|             // Use the unscaled matrix for the NavMeshSurface | ||||
|             var localToWorld = Matrix4x4.TRS(navSurface.transform.position, navSurface.transform.rotation, Vector3.one); | ||||
|             Gizmos.matrix = localToWorld; | ||||
|  | ||||
|             if (navSurface.collectObjects == CollectObjects.Volume) | ||||
|             { | ||||
|                 Gizmos.color = color; | ||||
|                 Gizmos.DrawWireCube(navSurface.center, navSurface.size); | ||||
|  | ||||
|                 if (selected && navSurface.enabled) | ||||
|                 { | ||||
|                     var colorTrans = new Color(color.r * 0.75f, color.g * 0.75f, color.b * 0.75f, color.a * 0.15f); | ||||
|                     Gizmos.color = colorTrans; | ||||
|                     Gizmos.DrawCube(navSurface.center, navSurface.size); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (navSurface.navMeshData != null) | ||||
|                 { | ||||
|                     var bounds = navSurface.navMeshData.sourceBounds; | ||||
|                     Gizmos.color = Color.grey; | ||||
|                     Gizmos.DrawWireCube(bounds.center, bounds.size); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Gizmos.matrix = oldMatrix; | ||||
|             Gizmos.color = oldColor; | ||||
|  | ||||
|             Gizmos.DrawIcon(navSurface.transform.position, "NavMeshSurface Icon", true); | ||||
|         } | ||||
|  | ||||
|         void OnSceneGUI() | ||||
|         { | ||||
|             if (!editingCollider) | ||||
|                 return; | ||||
|  | ||||
|             var navSurface = (NavMeshSurface)target; | ||||
|             var color = navSurface.enabled ? s_HandleColor : s_HandleColorDisabled; | ||||
|             var localToWorld = Matrix4x4.TRS(navSurface.transform.position, navSurface.transform.rotation, Vector3.one); | ||||
|             using (new Handles.DrawingScope(color, localToWorld)) | ||||
|             { | ||||
|                 m_BoundsHandle.center = navSurface.center; | ||||
|                 m_BoundsHandle.size = navSurface.size; | ||||
|  | ||||
|                 EditorGUI.BeginChangeCheck(); | ||||
|                 m_BoundsHandle.DrawHandle(); | ||||
|                 if (EditorGUI.EndChangeCheck()) | ||||
|                 { | ||||
|                     Undo.RecordObject(navSurface, "Modified NavMesh Surface"); | ||||
|                     Vector3 center = m_BoundsHandle.center; | ||||
|                     Vector3 size = m_BoundsHandle.size; | ||||
|                     navSurface.center = center; | ||||
|                     navSurface.size = size; | ||||
|                     EditorUtility.SetDirty(target); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [MenuItem("GameObject/AI/NavMesh Surface", false, 2000)] | ||||
|         public static void CreateNavMeshSurface(MenuCommand menuCommand) | ||||
|         { | ||||
|             var parent = menuCommand.context as GameObject; | ||||
|             var go = NavMeshComponentsGUIUtility.CreateAndSelectGameObject("NavMesh Surface", parent); | ||||
|             go.AddComponent<NavMeshSurface>(); | ||||
|             var view = SceneView.lastActiveSceneView; | ||||
|             if (view != null) | ||||
|                 view.MoveToView(go.transform); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 1c32167dbf3314852b6006a288eb449b | ||||
| timeCreated: 1476968447 | ||||
| licenseType: Pro | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,448 @@ | ||||
| #define NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF | ||||
|  | ||||
| using System.Linq; | ||||
| using UnityEditor.IMGUI.Controls; | ||||
| using UnityEditorInternal; | ||||
| using UnityEngine.AI; | ||||
| using UnityEngine; | ||||
|  | ||||
| namespace UnityEditor.AI | ||||
| { | ||||
|     [CanEditMultipleObjects] | ||||
|     [CustomEditor(typeof(NavMeshSurface2d))] | ||||
|     class NavMeshSurfaceEditor2d : Editor | ||||
|     { | ||||
|         SerializedProperty m_AgentTypeID; | ||||
|         SerializedProperty m_BuildHeightMesh; | ||||
|         SerializedProperty m_Center; | ||||
|         SerializedProperty m_CollectObjects; | ||||
|         SerializedProperty m_DefaultArea; | ||||
|         SerializedProperty m_LayerMask; | ||||
|  | ||||
|         SerializedProperty m_OverrideByGrid; | ||||
|         SerializedProperty m_UseMeshPrefab; | ||||
|         SerializedProperty m_CompressBounds; | ||||
|         SerializedProperty m_OverrideVector; | ||||
|  | ||||
|         SerializedProperty m_OverrideTileSize; | ||||
|         SerializedProperty m_OverrideVoxelSize; | ||||
|         SerializedProperty m_Size; | ||||
|         SerializedProperty m_TileSize; | ||||
|         SerializedProperty m_UseGeometry; | ||||
|         SerializedProperty m_VoxelSize; | ||||
|  | ||||
|         SerializedProperty m_HideEditorLogs; | ||||
|  | ||||
| #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF | ||||
|         SerializedProperty m_NavMeshData; | ||||
| #endif | ||||
|         class Styles | ||||
|         { | ||||
|             public readonly GUIContent m_LayerMask = new GUIContent("Include Layers"); | ||||
|  | ||||
|             public readonly GUIContent m_ShowInputGeom = new GUIContent("Show Input Geom"); | ||||
|             public readonly GUIContent m_ShowVoxels = new GUIContent("Show Voxels"); | ||||
|             public readonly GUIContent m_ShowRegions = new GUIContent("Show Regions"); | ||||
|             public readonly GUIContent m_ShowRawContours = new GUIContent("Show Raw Contours"); | ||||
|             public readonly GUIContent m_ShowContours = new GUIContent("Show Contours"); | ||||
|             public readonly GUIContent m_ShowPolyMesh = new GUIContent("Show Poly Mesh"); | ||||
|             public readonly GUIContent m_ShowPolyMeshDetail = new GUIContent("Show Poly Mesh Detail"); | ||||
|         } | ||||
|  | ||||
|         static Styles s_Styles; | ||||
|  | ||||
|         static bool s_ShowDebugOptions; | ||||
|  | ||||
|         static Color s_HandleColor = new Color(127f, 214f, 244f, 100f) / 255; | ||||
|         static Color s_HandleColorSelected = new Color(127f, 214f, 244f, 210f) / 255; | ||||
|         static Color s_HandleColorDisabled = new Color(127f * 0.75f, 214f * 0.75f, 244f * 0.75f, 100f) / 255; | ||||
|  | ||||
|         BoxBoundsHandle m_BoundsHandle = new BoxBoundsHandle(); | ||||
|  | ||||
|         bool editingCollider | ||||
|         { | ||||
|             get { return EditMode.editMode == EditMode.SceneViewEditMode.Collider && EditMode.IsOwner(this); } | ||||
|         } | ||||
|  | ||||
|         void OnEnable() | ||||
|         { | ||||
|             m_AgentTypeID = serializedObject.FindProperty("m_AgentTypeID"); | ||||
|             m_BuildHeightMesh = serializedObject.FindProperty("m_BuildHeightMesh"); | ||||
|             m_Center = serializedObject.FindProperty("m_Center"); | ||||
|             m_CollectObjects = serializedObject.FindProperty("m_CollectObjects"); | ||||
|             m_DefaultArea = serializedObject.FindProperty("m_DefaultArea"); | ||||
|             m_LayerMask = serializedObject.FindProperty("m_LayerMask"); | ||||
|  | ||||
|             m_OverrideByGrid = serializedObject.FindProperty("m_OverrideByGrid"); | ||||
|             m_UseMeshPrefab = serializedObject.FindProperty("m_UseMeshPrefab"); | ||||
|             m_CompressBounds = serializedObject.FindProperty("m_CompressBounds"); | ||||
|             m_OverrideVector = serializedObject.FindProperty("m_OverrideVector"); | ||||
|  | ||||
|             m_OverrideTileSize = serializedObject.FindProperty("m_OverrideTileSize"); | ||||
|             m_OverrideVoxelSize = serializedObject.FindProperty("m_OverrideVoxelSize"); | ||||
|             m_Size = serializedObject.FindProperty("m_Size"); | ||||
|             m_TileSize = serializedObject.FindProperty("m_TileSize"); | ||||
|             m_UseGeometry = serializedObject.FindProperty("m_UseGeometry"); | ||||
|             m_VoxelSize = serializedObject.FindProperty("m_VoxelSize"); | ||||
|  | ||||
|             m_HideEditorLogs = serializedObject.FindProperty("m_HideEditorLogs"); | ||||
|  | ||||
| #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF | ||||
|             m_NavMeshData = serializedObject.FindProperty("m_NavMeshData"); | ||||
| #endif | ||||
|             NavMeshVisualizationSettings.showNavigation++; | ||||
|         } | ||||
|  | ||||
|         void OnDisable() | ||||
|         { | ||||
|             NavMeshVisualizationSettings.showNavigation--; | ||||
|         } | ||||
|  | ||||
|         Bounds GetBounds() | ||||
|         { | ||||
|             var navSurface = (NavMeshSurface2d)target; | ||||
|             return new Bounds(navSurface.transform.position, navSurface.size); | ||||
|         } | ||||
|  | ||||
|         public override void OnInspectorGUI() | ||||
|         { | ||||
|             if (s_Styles == null) | ||||
|                 s_Styles = new Styles(); | ||||
|  | ||||
|             serializedObject.Update(); | ||||
|  | ||||
|             var bs = NavMesh.GetSettingsByID(m_AgentTypeID.intValue); | ||||
|  | ||||
|             if (bs.agentTypeID != -1) | ||||
|             { | ||||
|                 // Draw image | ||||
|                 const float diagramHeight = 80.0f; | ||||
|                 Rect agentDiagramRect = EditorGUILayout.GetControlRect(false, diagramHeight); | ||||
|                 NavMeshEditorHelpers.DrawAgentDiagram(agentDiagramRect, bs.agentRadius, bs.agentHeight, bs.agentClimb, bs.agentSlope); | ||||
|             } | ||||
|             NavMeshComponentsGUIUtility.AgentTypePopup("Agent Type", m_AgentTypeID); | ||||
|  | ||||
|             EditorGUILayout.Space(); | ||||
|  | ||||
|             if ((CollectObjects)m_CollectObjects.enumValueIndex != CollectObjects.Children | ||||
|                 && GameObject.FindObjectOfType<Grid>() == null) | ||||
|             { | ||||
|                 EditorGUILayout.HelpBox($"{CollectObjects.All} or {CollectObjects.Volume} is not intended to be used without root Grid object in scene. Use {CollectObjects.Children} instead.", MessageType.Warning); | ||||
|             } | ||||
|  | ||||
|             EditorGUILayout.PropertyField(m_CollectObjects); | ||||
|             if ((CollectObjects)m_CollectObjects.enumValueIndex == CollectObjects.Volume) | ||||
|             { | ||||
|                 EditorGUI.indentLevel++; | ||||
|  | ||||
|                 EditMode.DoEditModeInspectorModeButton(EditMode.SceneViewEditMode.Collider, "Edit Volume", | ||||
|                     EditorGUIUtility.IconContent("EditCollider"), GetBounds, this); | ||||
|                 EditorGUILayout.PropertyField(m_Size); | ||||
|                 EditorGUILayout.PropertyField(m_Center); | ||||
|  | ||||
|                 EditorGUI.indentLevel--; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (editingCollider) | ||||
|                     EditMode.QuitEditMode(); | ||||
|             } | ||||
|  | ||||
|             EditorGUILayout.PropertyField(m_LayerMask, s_Styles.m_LayerMask); | ||||
|             EditorGUILayout.PropertyField(m_UseGeometry); | ||||
|  | ||||
|             EditorGUILayout.Space(); | ||||
|             EditorGUILayout.PropertyField(m_OverrideByGrid); | ||||
|             EditorGUILayout.PropertyField(m_UseMeshPrefab); | ||||
|             EditorGUILayout.PropertyField(m_CompressBounds); | ||||
|             EditorGUILayout.PropertyField(m_OverrideVector); | ||||
|  | ||||
|             EditorGUILayout.Space(); | ||||
|  | ||||
|             m_OverrideVoxelSize.isExpanded = EditorGUILayout.Foldout(m_OverrideVoxelSize.isExpanded, "Advanced"); | ||||
|             if (m_OverrideVoxelSize.isExpanded) | ||||
|             { | ||||
|                 EditorGUI.indentLevel++; | ||||
|  | ||||
|                 NavMeshComponentsGUIUtility.AreaPopup("Default Area", m_DefaultArea); | ||||
|  | ||||
|                 // Override voxel size. | ||||
|                 EditorGUILayout.PropertyField(m_OverrideVoxelSize); | ||||
|  | ||||
|                 using (new EditorGUI.DisabledScope(!m_OverrideVoxelSize.boolValue || m_OverrideVoxelSize.hasMultipleDifferentValues)) | ||||
|                 { | ||||
|                     EditorGUI.indentLevel++; | ||||
|  | ||||
|                     EditorGUILayout.PropertyField(m_VoxelSize); | ||||
|  | ||||
|                     if (!m_OverrideVoxelSize.hasMultipleDifferentValues) | ||||
|                     { | ||||
|                         if (!m_AgentTypeID.hasMultipleDifferentValues) | ||||
|                         { | ||||
|                             float voxelsPerRadius = m_VoxelSize.floatValue > 0.0f ? (bs.agentRadius / m_VoxelSize.floatValue) : 0.0f; | ||||
|                             EditorGUILayout.LabelField(" ", voxelsPerRadius.ToString("0.00") + " voxels per agent radius", EditorStyles.miniLabel); | ||||
|                         } | ||||
|                         if (m_OverrideVoxelSize.boolValue) | ||||
|                             EditorGUILayout.HelpBox("Voxel size controls how accurately the navigation mesh is generated from the level geometry. A good voxel size is 2-4 voxels per agent radius. Making voxel size smaller will increase build time.", MessageType.None); | ||||
|                     } | ||||
|                     EditorGUI.indentLevel--; | ||||
|                 } | ||||
|  | ||||
|                 // Override tile size | ||||
|                 EditorGUILayout.PropertyField(m_OverrideTileSize); | ||||
|  | ||||
|                 using (new EditorGUI.DisabledScope(!m_OverrideTileSize.boolValue || m_OverrideTileSize.hasMultipleDifferentValues)) | ||||
|                 { | ||||
|                     EditorGUI.indentLevel++; | ||||
|  | ||||
|                     EditorGUILayout.PropertyField(m_TileSize); | ||||
|  | ||||
|                     if (!m_TileSize.hasMultipleDifferentValues && !m_VoxelSize.hasMultipleDifferentValues) | ||||
|                     { | ||||
|                         float tileWorldSize = m_TileSize.intValue * m_VoxelSize.floatValue; | ||||
|                         EditorGUILayout.LabelField(" ", tileWorldSize.ToString("0.00") + " world units", EditorStyles.miniLabel); | ||||
|                     } | ||||
|  | ||||
|                     if (!m_OverrideTileSize.hasMultipleDifferentValues) | ||||
|                     { | ||||
|                         if (m_OverrideTileSize.boolValue) | ||||
|                             EditorGUILayout.HelpBox("Tile size controls the how local the changes to the world are (rebuild or carve). Small tile size allows more local changes, while potentially generating more data in overal.", MessageType.None); | ||||
|                     } | ||||
|                     EditorGUI.indentLevel--; | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 // Height mesh | ||||
|                 using (new EditorGUI.DisabledScope(true)) | ||||
|                 { | ||||
|                     EditorGUILayout.PropertyField(m_BuildHeightMesh); | ||||
|                 } | ||||
|  | ||||
|                 EditorGUILayout.Space(); | ||||
|                 EditorGUI.indentLevel--; | ||||
|             } | ||||
|  | ||||
|             EditorGUILayout.Space(); | ||||
|  | ||||
|             // Hide editor logs | ||||
|             EditorGUILayout.PropertyField(m_HideEditorLogs); | ||||
|  | ||||
|             EditorGUILayout.Space(); | ||||
|  | ||||
|             serializedObject.ApplyModifiedProperties(); | ||||
|  | ||||
|             var hadError = false; | ||||
|             var multipleTargets = targets.Length > 1; | ||||
|             foreach (NavMeshSurface2d navSurface in targets) | ||||
|             { | ||||
|                 var settings = navSurface.GetBuildSettings(); | ||||
|                 // Calculating bounds is potentially expensive when unbounded - so here we just use the center/size. | ||||
|                 // It means the validation is not checking vertical voxel limit correctly when the surface is set to something else than "in volume". | ||||
|                 var bounds = new Bounds(Vector3.zero, Vector3.zero); | ||||
|                 if (navSurface.collectObjects == CollectObjects2d.Volume) | ||||
|                 { | ||||
|                     bounds = new Bounds(navSurface.center, navSurface.size); | ||||
|                 } | ||||
|  | ||||
|                 var errors = settings.ValidationReport(bounds); | ||||
|                 if (errors.Length > 0) | ||||
|                 { | ||||
|                     if (multipleTargets) | ||||
|                         EditorGUILayout.LabelField(navSurface.name); | ||||
|                     foreach (var err in errors) | ||||
|                     { | ||||
|                         EditorGUILayout.HelpBox(err, MessageType.Warning); | ||||
|                     } | ||||
|                     GUILayout.BeginHorizontal(); | ||||
|                     GUILayout.Space(EditorGUIUtility.labelWidth); | ||||
|                     if (GUILayout.Button("Open Agent Settings...", EditorStyles.miniButton)) | ||||
|                         NavMeshEditorHelpers.OpenAgentSettings(navSurface.agentTypeID); | ||||
|                     GUILayout.EndHorizontal(); | ||||
|                     hadError = true; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (hadError) | ||||
|                 EditorGUILayout.Space(); | ||||
|  | ||||
| #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF | ||||
|             var nmdRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); | ||||
|  | ||||
|             EditorGUI.BeginProperty(nmdRect, GUIContent.none, m_NavMeshData); | ||||
|             var rectLabel = EditorGUI.PrefixLabel(nmdRect, GUIUtility.GetControlID(FocusType.Passive), new GUIContent(m_NavMeshData.displayName)); | ||||
|             EditorGUI.EndProperty(); | ||||
|  | ||||
|             using (new EditorGUI.DisabledScope(true)) | ||||
|             { | ||||
|                 EditorGUI.BeginProperty(nmdRect, GUIContent.none, m_NavMeshData); | ||||
|                 EditorGUI.ObjectField(rectLabel, m_NavMeshData, GUIContent.none); | ||||
|                 EditorGUI.EndProperty(); | ||||
|             } | ||||
| #endif | ||||
|             using (new EditorGUI.DisabledScope(Application.isPlaying || m_AgentTypeID.intValue == -1)) | ||||
|             { | ||||
|                 GUILayout.BeginHorizontal(); | ||||
|                 GUILayout.Space(EditorGUIUtility.labelWidth); | ||||
|                 if (GUILayout.Button("Clear")) | ||||
|                 { | ||||
|                     NavMeshAssetManager2d.instance.ClearSurfaces(targets); | ||||
|                     SceneView.RepaintAll(); | ||||
|                 } | ||||
|  | ||||
|                 if (GUILayout.Button("Bake")) | ||||
|                 { | ||||
|                     NavMeshAssetManager2d.instance.StartBakingSurfaces(targets); | ||||
|                 } | ||||
|  | ||||
|                 GUILayout.EndHorizontal(); | ||||
|  | ||||
|                 GUILayout.BeginHorizontal(); | ||||
|                 GUILayout.Space(EditorGUIUtility.labelWidth); | ||||
|                 if (GUILayout.Button("Rotate Surface to XY")) | ||||
|                 { | ||||
|                     foreach (var item in targets) | ||||
|                     { | ||||
|                         var o = item as NavMeshSurface2d; | ||||
|                         o.transform.rotation = Quaternion.Euler(-90f, 0f, 0f); | ||||
|                     } | ||||
|                 } | ||||
|                 GUILayout.EndHorizontal(); | ||||
|                 foreach (NavMeshSurface2d navSurface in targets) | ||||
|                 { | ||||
|                     if (!Mathf.Approximately(navSurface.transform.eulerAngles.x, 270f)) | ||||
|                     { | ||||
|                         EditorGUILayout.HelpBox("NavMeshSurface2d is not rotated respectively to (x-90;y0;z0). Apply rotation unless intended.", MessageType.Warning); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Show progress for the selected targets | ||||
|             var bakeOperations = NavMeshAssetManager2d.instance.GetBakeOperations(); | ||||
|             for (int i = bakeOperations.Count - 1; i >= 0; --i) | ||||
|             { | ||||
|                 if (!targets.Contains(bakeOperations[i].surface)) | ||||
|                     continue; | ||||
|  | ||||
|                 var oper = bakeOperations[i].bakeOperation; | ||||
|                 if (oper == null) | ||||
|                     continue; | ||||
|  | ||||
|                 var p = oper.progress; | ||||
|                 if (oper.isDone) | ||||
|                 { | ||||
|                     SceneView.RepaintAll(); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 GUILayout.BeginHorizontal(); | ||||
|  | ||||
|                 if (GUILayout.Button("Cancel", EditorStyles.miniButton)) | ||||
|                 { | ||||
|                     var bakeData = bakeOperations[i].bakeData; | ||||
|                     UnityEngine.AI.NavMeshBuilder.Cancel(bakeData); | ||||
|                     bakeOperations.RemoveAt(i); | ||||
|                 } | ||||
|  | ||||
|                 EditorGUI.ProgressBar(EditorGUILayout.GetControlRect(), p, "Baking: " + (int)(100 * p) + "%"); | ||||
|                 if (p <= 1) | ||||
|                     Repaint(); | ||||
|  | ||||
|                 GUILayout.EndHorizontal(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [DrawGizmo(GizmoType.Selected | GizmoType.Active | GizmoType.Pickable)] | ||||
|         static void RenderBoxGizmoSelected(NavMeshSurface2d navSurface, GizmoType gizmoType) | ||||
|         { | ||||
|             RenderBoxGizmo(navSurface, gizmoType, true); | ||||
|         } | ||||
|  | ||||
|         [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.Pickable)] | ||||
|         static void RenderBoxGizmoNotSelected(NavMeshSurface2d navSurface, GizmoType gizmoType) | ||||
|         { | ||||
|             if (NavMeshVisualizationSettings.showNavigation > 0) | ||||
|                 RenderBoxGizmo(navSurface, gizmoType, false); | ||||
|             else | ||||
|                 Gizmos.DrawIcon(navSurface.transform.position, "NavMeshSurface Icon", true); | ||||
|         } | ||||
|  | ||||
|         static void RenderBoxGizmo(NavMeshSurface2d navSurface, GizmoType gizmoType, bool selected) | ||||
|         { | ||||
|             var color = selected ? s_HandleColorSelected : s_HandleColor; | ||||
|             if (!navSurface.enabled) | ||||
|                 color = s_HandleColorDisabled; | ||||
|  | ||||
|             var oldColor = Gizmos.color; | ||||
|             var oldMatrix = Gizmos.matrix; | ||||
|  | ||||
|             // Use the unscaled matrix for the NavMeshSurface | ||||
|             var localToWorld = Matrix4x4.TRS(navSurface.transform.position, navSurface.transform.rotation, Vector3.one); | ||||
|             Gizmos.matrix = localToWorld; | ||||
|  | ||||
|             if (navSurface.collectObjects == CollectObjects2d.Volume) | ||||
|             { | ||||
|                 Gizmos.color = color; | ||||
|                 Gizmos.DrawWireCube(navSurface.center, navSurface.size); | ||||
|  | ||||
|                 if (selected && navSurface.enabled) | ||||
|                 { | ||||
|                     var colorTrans = new Color(color.r * 0.75f, color.g * 0.75f, color.b * 0.75f, color.a * 0.15f); | ||||
|                     Gizmos.color = colorTrans; | ||||
|                     Gizmos.DrawCube(navSurface.center, navSurface.size); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (navSurface.navMeshData != null) | ||||
|                 { | ||||
|                     var bounds = navSurface.navMeshData.sourceBounds; | ||||
|                     Gizmos.color = Color.grey; | ||||
|                     Gizmos.DrawWireCube(bounds.center, bounds.size); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Gizmos.matrix = oldMatrix; | ||||
|             Gizmos.color = oldColor; | ||||
|  | ||||
|             Gizmos.DrawIcon(navSurface.transform.position, "NavMeshSurface Icon", true); | ||||
|         } | ||||
|  | ||||
|         void OnSceneGUI() | ||||
|         { | ||||
|             if (!editingCollider) | ||||
|                 return; | ||||
|  | ||||
|             var navSurface = (NavMeshSurface2d)target; | ||||
|             var color = navSurface.enabled ? s_HandleColor : s_HandleColorDisabled; | ||||
|             var localToWorld = Matrix4x4.TRS(navSurface.transform.position, navSurface.transform.rotation, Vector3.one); | ||||
|             using (new Handles.DrawingScope(color, localToWorld)) | ||||
|             { | ||||
|                 m_BoundsHandle.center = navSurface.center; | ||||
|                 m_BoundsHandle.size = navSurface.size; | ||||
|  | ||||
|                 EditorGUI.BeginChangeCheck(); | ||||
|                 m_BoundsHandle.DrawHandle(); | ||||
|                 if (EditorGUI.EndChangeCheck()) | ||||
|                 { | ||||
|                     Undo.RecordObject(navSurface, "Modified NavMesh Surface"); | ||||
|                     Vector3 center = m_BoundsHandle.center; | ||||
|                     Vector3 size = m_BoundsHandle.size; | ||||
|                     navSurface.center = center; | ||||
|                     navSurface.size = size; | ||||
|                     EditorUtility.SetDirty(target); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [MenuItem("GameObject/AI/NavMesh Surface 2D", false, 2000)] | ||||
|         public static void CreateNavMeshSurface(MenuCommand menuCommand) | ||||
|         { | ||||
|             var parent = menuCommand.context as GameObject; | ||||
|             var go = NavMeshComponentsGUIUtility.CreateAndSelectGameObject("NavMesh Surface", parent); | ||||
|             go.AddComponent<NavMeshSurface2d>(); | ||||
|             var view = SceneView.lastActiveSceneView; | ||||
|             if (view != null) | ||||
|                 view.MoveToView(go.transform); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,11 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 69fa4f647e3f44d4fac10fd739f1fde5 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
		Reference in New Issue
	
	Block a user
	 j.mei7
					j.mei7