浏览代码
feat: Add NavMeshComponents
feat: Add NavMeshComponents
NavMeshComponents are needed to fix some of our navmesh issues. Mainly NavMeshSurface helps us to patch the iron bar tiles with lava underneath which weren't captured in the bake./main
Luke Stampfli
4 年前
当前提交
3d4a7c76
共有 33 个文件被更改,包括 2447 次插入 和 0 次删除
-
21Assets/BossRoom/UnityTechnologies/LICENSE
-
7Assets/BossRoom/UnityTechnologies/LICENSE.meta
-
8Assets/BossRoom/UnityTechnologies/NavMeshComponents.meta
-
9Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor.meta
-
334Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshAssetManager.cs
-
11Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshAssetManager.cs.meta
-
16Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshComponentsEditor.asmdef
-
7Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshComponentsEditor.asmdef.meta
-
258Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshComponentsGUIUtility.cs
-
12Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshComponentsGUIUtility.cs.meta
-
279Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshLinkEditor.cs
-
12Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshLinkEditor.cs.meta
-
49Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshModifierEditor.cs
-
12Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshModifierEditor.cs.meta
-
146Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshModifierVolumeEditor.cs
-
12Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshModifierVolumeEditor.cs.meta
-
400Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs
-
12Assets/BossRoom/UnityTechnologies/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs.meta
-
9Assets/BossRoom/UnityTechnologies/NavMeshComponents/Scripts.meta
-
12Assets/BossRoom/UnityTechnologies/NavMeshComponents/Scripts/NavMeshComponents.asmdef
-
7Assets/BossRoom/UnityTechnologies/NavMeshComponents/Scripts/NavMeshComponents.asmdef.meta
-
172Assets/BossRoom/UnityTechnologies/NavMeshComponents/Scripts/NavMeshLink.cs
-
12Assets/BossRoom/UnityTechnologies/NavMeshComponents/Scripts/NavMeshLink.cs.meta
-
54Assets/BossRoom/UnityTechnologies/NavMeshComponents/Scripts/NavMeshModifier.cs
-
12Assets/BossRoom/UnityTechnologies/NavMeshComponents/Scripts/NavMeshModifier.cs.meta
-
54Assets/BossRoom/UnityTechnologies/NavMeshComponents/Scripts/NavMeshModifierVolume.cs
-
12Assets/BossRoom/UnityTechnologies/NavMeshComponents/Scripts/NavMeshModifierVolume.cs.meta
-
486Assets/BossRoom/UnityTechnologies/NavMeshComponents/Scripts/NavMeshSurface.cs
-
12Assets/BossRoom/UnityTechnologies/NavMeshComponents/Scripts/NavMeshSurface.cs.meta
|
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2016, Unity Technologies |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
THE SOFTWARE. |
|
|||
fileFormatVersion: 2 |
|||
guid: aa7da96d5cc1d8242900573be77f6c14 |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: afc82e0115e53084e8fabe6eace87738 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 63b588f3892bb4b5eb73ad3d2791e05c |
|||
folderAsset: yes |
|||
timeCreated: 1477656493 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
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) |
|||
{ |
|||
#if UNITY_2020_1_OR_NEWER
|
|||
var assetPath = prefabStage.assetPath; |
|||
#else
|
|||
var assetPath = prefabStage.prefabAssetPath; |
|||
#endif
|
|||
if (!string.IsNullOrEmpty(assetPath)) |
|||
{ |
|||
var prefabDirectoryName = Path.GetDirectoryName(assetPath); |
|||
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; |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 178d8366aa1616849b91b66285c51454 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
{ |
|||
"name": "NavMeshComponentsEditor", |
|||
"references": [ |
|||
"NavMeshComponents" |
|||
], |
|||
"optionalUnityReferences": [], |
|||
"includePlatforms": [ |
|||
"Editor" |
|||
], |
|||
"excludePlatforms": [], |
|||
"allowUnsafeCode": false, |
|||
"overrideReferences": false, |
|||
"precompiledReferences": [], |
|||
"autoReferenced": true, |
|||
"defineConstraints": [] |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 86c9d8e67265f41469be06142c397d17 |
|||
AssemblyDefinitionImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
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; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 77fba670b979046f18d52d751e0d4659 |
|||
timeCreated: 1480524815 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
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); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: ece1e865d1ad84587872fe8580ab5a20 |
|||
timeCreated: 1477036743 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
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(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6fa04b4743e3947eba4d7b9e5832ea69 |
|||
timeCreated: 1477036742 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
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); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: c0f3bef2a67ae4e139538afec3e59b03 |
|||
timeCreated: 1477036743 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
#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); |
|||
|