PathFinding done

This commit is contained in:
kimrdd 2024-02-02 23:21:12 +01:00
parent f31b2b900f
commit ab098c3370
107 changed files with 6025 additions and 3 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ab4f84282bf0bb6468d23369c4d7d5cc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d308d3c67696b1044b66e5bec5c89bde
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 23800000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2cd36024fd203f84d8cd3c7d4b6b7593
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class EnemyPathFinding : MonoBehaviour
{
[SerializeField] private Transform target;
NavMeshAgent agent;
private void Start()
{
agent = GetComponent<NavMeshAgent>();
agent.updateRotation = false;
agent.updateUpAxis = false;
}
private void Update()
{
agent.SetDestination(target.position);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6a3ffc4a3af9e0243ac9ee0c995bb82f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 38ce38f74cbf5b84fad41aa34e1af3f2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 15edf91cde5e0974faccaf09491aab0f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9f554f0dfb387d647ae9650f24589d6a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,98 @@
fileFormatVersion: 2
guid: 68ad4f5d6fe957c4789aedd21ff67ced
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,98 @@
fileFormatVersion: 2
guid: 273c8b5db6e39534781066db3444fe88
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,98 @@
fileFormatVersion: 2
guid: 444810ca896903c41adf617b35274dc4
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,98 @@
fileFormatVersion: 2
guid: d5b0e13ebe59cd64e9f67284457c6868
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,98 @@
fileFormatVersion: 2
guid: e8142b1daeea8d3419cd0ffbd7b17a37
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 h8man
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.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4aafea534cdccb843b27a8b6e839a76f
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b9669812fb313c842943ae58d9c50699
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 455bb0723c59a9145bc3c035376ca788
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,35 @@
using NavMeshPlus.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEditor;
namespace NavMeshPlus.Editors.Extensions
{
[CanEditMultipleObjects]
[CustomEditor(typeof(AgentOverride2d))]
internal class AgentOverride2dEditor : Editor
{
//SerializedProperty m_OverrideByGrid;
//SerializedProperty m_UseMeshPrefab;
//SerializedProperty m_CompressBounds;
//SerializedProperty m_OverrideVector;
void OnEnable()
{
//m_OverrideByGrid = serializedObject.FindProperty("m_OverrideByGrid");
//m_UseMeshPrefab = serializedObject.FindProperty("m_UseMeshPrefab");
//m_CompressBounds = serializedObject.FindProperty("m_CompressBounds");
//m_OverrideVector = serializedObject.FindProperty("m_OverrideVector");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
var agent = target as AgentOverride2d;
EditorGUILayout.LabelField("Agent Override", agent.agentOverride?.GetType().Name);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86cc6cf350c6f62469395948494f0945
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,73 @@
using UnityEngine.AI;
using UnityEngine;
using UnityEditor;
using NavMeshPlus.Extensions;
namespace NavMeshPlus.Editors.Extensions
{
[CanEditMultipleObjects]
[CustomEditor(typeof(CollectSources2d))]
internal class CollectSources2dEditor: Editor
{
SerializedProperty m_OverrideByGrid;
SerializedProperty m_UseMeshPrefab;
SerializedProperty m_CompressBounds;
SerializedProperty m_OverrideVector;
void OnEnable()
{
m_OverrideByGrid = serializedObject.FindProperty("m_OverrideByGrid");
m_UseMeshPrefab = serializedObject.FindProperty("m_UseMeshPrefab");
m_CompressBounds = serializedObject.FindProperty("m_CompressBounds");
m_OverrideVector = serializedObject.FindProperty("m_OverrideVector");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
var surf = target as CollectSources2d;
EditorGUILayout.PropertyField(m_OverrideByGrid);
using (new EditorGUI.DisabledScope(!m_OverrideByGrid.boolValue))
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_UseMeshPrefab);
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(m_CompressBounds);
EditorGUILayout.PropertyField(m_OverrideVector);
EditorGUILayout.Space();
serializedObject.ApplyModifiedProperties();
using (new EditorGUI.DisabledScope(Application.isPlaying))
{
GUILayout.BeginHorizontal();
if (GUILayout.Button(new GUIContent("Rotate Surface to XY", "Rotates Surface along XY plane to face toward standard 2d camera.")))
{
foreach (CollectSources2d item in targets)
{
item.transform.rotation = Quaternion.Euler(-90f, 0f, 0f);
}
}
if (GUILayout.Button(new GUIContent("Tilt Surface", "If your agent get stuck on vertical movement it may help to solve the issue. This will tilt Surface to -89.98. It may impact baking and navigation.")))
{
foreach (CollectSources2d item in targets)
{
item.transform.rotation = Quaternion.Euler(-89.98f, 0f, 0f);
}
}
GUILayout.EndHorizontal();
foreach (CollectSources2d navSurface in targets)
{
if (!Mathf.Approximately(navSurface.transform.eulerAngles.x, 270.0198f) && !Mathf.Approximately(navSurface.transform.eulerAngles.x, 270f))
{
EditorGUILayout.HelpBox("NavMeshSurface is not rotated respectively to (x-90;y0;z0). Apply rotation unless intended.", MessageType.Warning);
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3fc821cbc11a48745bc9edea6bfda007
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,42 @@
using UnityEngine.AI;
using UnityEngine;
using UnityEditor;
using NavMeshPlus.Extensions;
namespace NavMeshPlus.Editors.Extensions
{
[CanEditMultipleObjects]
[CustomEditor(typeof(CollectSourcesCache2d))]
internal class CollectSourcesCache2dEditor : Editor
{
public override void OnInspectorGUI()
{
serializedObject.Update();
var surf = target as CollectSourcesCache2d;
serializedObject.ApplyModifiedProperties();
using (new EditorGUI.DisabledScope(!Application.isPlaying))
{
GUILayout.BeginHorizontal();
GUILayout.Label("Sources:");
if (Application.isPlaying)
{
GUILayout.Label(surf.SourcesCount.ToString());
GUILayout.Label("Cached:");
GUILayout.Label(surf.CahcheCount.ToString());
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Actions:");
if (GUILayout.Button("Update Mesh"))
{
surf.UpdateNavMesh();
}
GUILayout.EndHorizontal();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3616009fd1ab770409e0321881372357
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
using UnityEditor;
using UnityEngine;
using NavMeshPlus.Extensions;
using NavMeshPlus.Editors.Components;
//***********************************************************************************
// Contributed by author jl-randazzo github.com/jl-randazzo
//***********************************************************************************
namespace NavMeshPlus.Editors.Extensions
{
[CustomPropertyDrawer(typeof(NavMeshAgentAttribute))]
public class NavMeshAgentAttributePropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
NavMeshComponentsGUIUtility.AgentTypePopup(position, label.text, property);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) => NavMeshComponentsGUIUtility.IsAgentSelectionValid(property) ? 20 : 40;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1d43a96ce80684449a8b4eee132e5d47
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
using UnityEditor;
using UnityEngine;
using NavMeshPlus.Extensions;
using NavMeshPlus.Editors.Components;
//***********************************************************************************
// Contributed by author jl-randazzo github.com/jl-randazzo
//***********************************************************************************
namespace NavMeshPlus.Editors.Extensions
{
[CustomPropertyDrawer(typeof(NavMeshAreaAttribute))]
public class NavMeshAreaAttributePropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
NavMeshComponentsGUIUtility.AreaPopup(position, label.text, property);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) => 20;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 51136e93cfad4dd7883ae6248247b6a2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: d5b0e13ebe59cd64e9f67284457c6868, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,337 @@
using System.Collections.Generic;
using System.IO;
using UnityEditor.Experimental.SceneManagement;
using UnityEditor.SceneManagement;
using UnityEngine.AI;
using UnityEngine;
using NavMeshPlus.Extensions;
using UnityEditor;
using NavMeshPlus.Components;
namespace NavMeshPlus.Editors.Components
{
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;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0d969266144f4fb47be21604dd1e7900
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
{
"name": "NavMeshPlusEditor",
"references": [
"NavMeshPlus"
],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a38ed33481bedd74d8c590f5043f49dc
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,276 @@
using UnityEditor;
using UnityEditor.AI;
using UnityEngine;
using UnityEngine.AI;
namespace NavMeshPlus.Editors.Components
{
public static class NavMeshComponentsGUIUtility
{
public static void AreaPopup(Rect rect, 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...");
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 bool IsAgentSelectionValid(SerializedProperty agentTypeID)
{
var count = NavMesh.GetSettingsCount();
for (var i = 0; i < count; i++)
{
var id = NavMesh.GetSettingsByIndex(i).agentTypeID;
var name = NavMesh.GetSettingsNameFromID(id);
if (id == agentTypeID.intValue)
return true;
}
return false;
}
public static void AgentTypePopup(Rect rect, 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)
{
Rect warningRect = rect;
warningRect.height *= .5f;
warningRect.y += warningRect.height;
EditorGUI.HelpBox(warningRect, "Agent Type invalid.", MessageType.Warning);
rect.height *= .5f;
}
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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d05c56cb29ad5240bc671605f95db0c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,281 @@
using NavMeshPlus.Components;
using UnityEditor;
using UnityEditor.AI;
using UnityEngine;
using UnityEngine.AI;
namespace NavMeshPlus.Editors.Components
{
[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();
EditorGUILayout.PropertyField(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);
EditorGUILayout.PropertyField(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/Navigation/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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 03832abc07e3394479eec5708b22e984
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,53 @@
using NavMeshPlus.Components;
using NavMeshPlus.Extensions;
using UnityEditor;
using UnityEditor.AI;
using UnityEngine.AI;
namespace NavMeshPlus.Editors.Components
{
[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++;
EditorGUILayout.PropertyField(m_Area);
EditorGUI.indentLevel--;
}
NavMeshComponentsGUIUtility.AgentMaskPopup("Affected Agents", m_AffectedAgents);
EditorGUILayout.Space();
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b8fce3c13f011874d92d75ca24a90702
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,158 @@
using NavMeshPlus.Components;
using NavMeshPlus.Extensions;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEditor.AI;
using UnityEngine.AI;
using UnityEngine.Tilemaps;
//***********************************************************************************
// Contributed by author jl-randazzo github.com/jl-randazzo
//***********************************************************************************
namespace NavMeshPlus.Editors.Components
{
[CanEditMultipleObjects]
[CustomEditor(typeof(NavMeshModifierTilemap))]
class NavMeshModifierTilemapEditor : Editor
{
SerializedProperty m_TileModifiers;
void OnEnable()
{
m_TileModifiers = serializedObject.FindProperty("m_TileModifiers");
}
public override void OnInspectorGUI()
{
NavMeshModifierTilemap modifierTilemap = target as NavMeshModifierTilemap;
serializedObject.Update();
EditorGUILayout.PropertyField(m_TileModifiers);
if (modifierTilemap.HasDuplicateTileModifiers())
{
EditorGUILayout.HelpBox("There are duplicate Tile entries in the tilemap modifiers! Only the first will be used.", MessageType.Warning);
}
EditorGUILayout.Space();
Tilemap tilemap = modifierTilemap.GetComponent<Tilemap>();
if (tilemap)
{
if (GUILayout.Button("Add Used Tiles"))
{
AddUsedTiles(tilemap, modifierTilemap);
}
}
else
{
EditorGUILayout.HelpBox("Missing required component 'Tilemap'", MessageType.Error);
}
if (serializedObject.ApplyModifiedProperties())
{
modifierTilemap.CacheModifiers();
}
}
private void AddUsedTiles(Tilemap tilemap, NavMeshModifierTilemap modifierTilemap)
{
Dictionary<TileBase, NavMeshModifierTilemap.TileModifier> tileModifiers = modifierTilemap.GetModifierMap();
BoundsInt bounds = tilemap.cellBounds;
for (int i = bounds.xMin; i <= bounds.xMax; i++)
{
for (int j = bounds.yMin; j <= bounds.yMax; j++)
{
for (int k = bounds.zMin; k <= bounds.zMax; k++)
{
if (tilemap.GetTile(new Vector3Int(i, j, k)) is TileBase tileBase)
{
if (!tileModifiers.ContainsKey(tileBase))
{
tileModifiers.Add(tileBase, new NavMeshModifierTilemap.TileModifier());
int idx = m_TileModifiers.arraySize;
m_TileModifiers.InsertArrayElementAtIndex(idx);
var newElem = m_TileModifiers.GetArrayElementAtIndex(idx);
var tileProperty = newElem.FindPropertyRelative(nameof(NavMeshModifierTilemap.TileModifier.tile));
tileProperty.objectReferenceValue = tileBase;
}
}
}
}
}
}
[CustomPropertyDrawer(typeof(NavMeshModifierTilemap.TileModifier))]
class TileModifierPropertyDrawer : PropertyDrawer
{
private Rect ClaimAdvance(ref Rect position, float height)
{
Rect retVal = position;
retVal.height = height;
position.y += height;
position.height -= height;
return retVal;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Rect expandRect = ClaimAdvance(ref position, 20);
property.isExpanded = EditorGUI.Foldout(expandRect, property.isExpanded, label);
if (property.isExpanded)
{
var tileProperty = property.FindPropertyRelative(nameof(NavMeshModifierTilemap.TileModifier.tile));
Rect tileRect = ClaimAdvance(ref position, 40);
tileRect.width -= 40;
Rect previewRect = tileRect;
previewRect.width = 40;
previewRect.x += tileRect.width;
tileRect.height /= 2;
// Adding the tile selector and a preview image.
EditorGUI.PropertyField(tileRect, tileProperty);
TileBase tileBase = tileProperty.objectReferenceValue as TileBase;
TileData tileData = new TileData();
tileBase?.GetTileData(Vector3Int.zero, null, ref tileData);
if (tileData.sprite)
{
EditorGUI.DrawPreviewTexture(previewRect, tileData.sprite?.texture, null, ScaleMode.ScaleToFit, 0);
}
Rect toggleRect = ClaimAdvance(ref position, 20);
var overrideAreaProperty = property.FindPropertyRelative(nameof(NavMeshModifierTilemap.TileModifier.overrideArea));
EditorGUI.PropertyField(toggleRect, overrideAreaProperty);
if (overrideAreaProperty.boolValue)
{
Rect areaRect = ClaimAdvance(ref position, 20);
var areaProperty = property.FindPropertyRelative(nameof(NavMeshModifierTilemap.TileModifier.area));
EditorGUI.indentLevel++;
EditorGUI.PropertyField(areaRect, areaProperty);
EditorGUI.indentLevel--;
}
}
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
if (property.isExpanded)
{
var overrideAreaProperty = property.FindPropertyRelative(nameof(NavMeshModifierTilemap.TileModifier.overrideArea));
if (overrideAreaProperty.boolValue)
{
return 100;
}
return 80;
}
return 20;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1fa3ed80a7c8401995efba10b64226e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,149 @@
using UnityEditor.IMGUI.Controls;
using UnityEditorInternal;
using UnityEngine.AI;
using UnityEngine;
using NavMeshPlus.Extensions;
using UnityEditor;
using UnityEditor.AI;
using NavMeshPlus.Components;
namespace NavMeshPlus.Editors.Components
{
[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);
EditorGUILayout.PropertyField(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/Navigation/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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5999826ec8e37f74a80d7f6ee2700a3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,399 @@
#define NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF
using System.Linq;
using UnityEditor.IMGUI.Controls;
using UnityEditorInternal;
using UnityEngine.AI;
using UnityEngine;
using UnityEditor;
using UnityEditor.AI;
using NavMeshPlus.Components;
namespace NavMeshPlus.Editors.Components
{
[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);
}
EditorGUILayout.PropertyField(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++;
EditorGUILayout.PropertyField(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/Navigation/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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3a7b2ae8284a9b14abd6d688cc877cef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,39 @@
using UnityEngine.AI;
using UnityEngine;
using UnityEditor;
using NavMeshPlus.Extensions;
using NavMeshPlus.Components;
namespace NavMeshPlus.Editors.Extensions
{
[CanEditMultipleObjects]
[CustomEditor(typeof(RootSources2d))]
internal class RootSources2dEditor: Editor
{
SerializedProperty _rootSources;
void OnEnable()
{
_rootSources = serializedObject.FindProperty("_rootSources");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
var surf = target as RootSources2d;
EditorGUILayout.HelpBox("Add GameObjects to create NavMesh form it and it's ancestors", MessageType.Info);
if (surf.NavMeshSurfaceOwner.collectObjects != CollectObjects.Children)
{
EditorGUILayout.Space();
EditorGUILayout.HelpBox("Root Sources are only suitable for 'CollectObjects - Children'", MessageType.Info);
EditorGUILayout.Space();
}
EditorGUILayout.PropertyField(_rootSources);
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e3d768af6ee1774cb39815bd8e5b221
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 29f130eb3fdf4574a8a586b2bc99f11a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,36 @@
using UnityEngine;
using UnityEngine.AI;
namespace NavMeshPlus.Extensions
{
public interface IAgentOverride
{
void UpdateAgent();
}
public class AgentDefaultOverride : IAgentOverride
{
public void UpdateAgent()
{
}
}
public class AgentOverride2d: MonoBehaviour
{
public NavMeshAgent Agent { get; private set; }
public IAgentOverride agentOverride { get; set; }
private void Awake()
{
Agent = GetComponent<NavMeshAgent>();
}
private void Start()
{
Agent.updateRotation = false;
Agent.updateUpAxis = false;
}
private void Update()
{
agentOverride?.UpdateAgent();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e39f6090724e4a4a8aadeb042afa2bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: d5b0e13ebe59cd64e9f67284457c6868, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,15 @@
using UnityEngine;
namespace NavMeshPlus.Extensions
{
public class AgentRotate2d: MonoBehaviour
{
private AgentOverride2d override2D;
private void Start()
{
override2D = GetComponent<AgentOverride2d>();
override2D.agentOverride = new RotateAgentInstantly(override2D.Agent, override2D);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8550f034611a06e4f88fc261204d8a7b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: d5b0e13ebe59cd64e9f67284457c6868, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
using UnityEngine;
namespace NavMeshPlus.Extensions
{
class AgentRotateSmooth2d: MonoBehaviour
{
public float angularSpeed;
private AgentOverride2d override2D;
private void Start()
{
override2D = GetComponent<AgentOverride2d>();
override2D.agentOverride = new RotateAgentSmoothly(override2D.Agent, override2D, angularSpeed);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c7f4db162bab4443818dd13bed4b1dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: d5b0e13ebe59cd64e9f67284457c6868, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,90 @@
using NavMeshPlus.Components;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Tilemaps;
namespace NavMeshPlus.Extensions
{
[ExecuteAlways]
[AddComponentMenu("Navigation/Navigation CollectSources2d", 30)]
public class CollectSources2d: NavMeshExtension
{
[SerializeField]
bool m_OverrideByGrid;
public bool overrideByGrid { get { return m_OverrideByGrid; } set { m_OverrideByGrid = value; } }
[SerializeField]
GameObject m_UseMeshPrefab;
public GameObject useMeshPrefab { get { return m_UseMeshPrefab; } set { m_UseMeshPrefab = value; } }
[SerializeField]
bool m_CompressBounds;
public bool compressBounds { get { return m_CompressBounds; } set { m_CompressBounds = value; } }
[SerializeField]
Vector3 m_OverrideVector = Vector3.one;
public Vector3 overrideVector { get { return m_OverrideVector; } set { m_OverrideVector = value; } }
public override void CalculateWorldBounds(NavMeshSurface surface, List<NavMeshBuildSource> sources, NavMeshBuilderState navNeshState)
{
if (surface.collectObjects != CollectObjects.Volume)
{
navNeshState.worldBounds.Encapsulate(CalculateGridWorldBounds(surface, navNeshState.worldToLocal, navNeshState.worldBounds));
}
}
private static Bounds CalculateGridWorldBounds(NavMeshSurface surface, Matrix4x4 worldToLocal, Bounds bounds)
{
var grid = FindObjectOfType<Grid>();
var tilemaps = grid?.GetComponentsInChildren<Tilemap>();
if (tilemaps == null || tilemaps.Length < 1)
{
return bounds;
}
foreach (var tilemap in tilemaps)
{
var lbounds = NavMeshSurface.GetWorldBounds(worldToLocal * tilemap.transform.localToWorldMatrix, tilemap.localBounds);
bounds.Encapsulate(lbounds);
if (!surface.hideEditorLogs)
{
Debug.Log($"From Local Bounds [{tilemap.name}]: {tilemap.localBounds}");
Debug.Log($"To World Bounds: {bounds}");
}
}
return bounds;
}
public override void CollectSources(NavMeshSurface surface, List<NavMeshBuildSource> sources, NavMeshBuilderState navNeshState)
{
if (!surface.hideEditorLogs)
{
if (!Mathf.Approximately(transform.eulerAngles.x, 270f))
{
Debug.LogWarning("NavMeshSurface is not rotated respectively to (x-90;y0;z0). Apply rotation unless intended.");
}
if (Application.isPlaying)
{
if (surface.useGeometry == NavMeshCollectGeometry.PhysicsColliders && Time.frameCount <= 1)
{
Debug.LogWarning("Use Geometry - Physics Colliders option in NavMeshSurface may cause inaccurate mesh bake if executed before Physics update.");
}
}
}
var builder = navNeshState.GetExtraState<NavMeshBuilder2dState>();
builder.defaultArea = surface.defaultArea;
builder.layerMask = surface.layerMask;
builder.agentID = surface.agentTypeID;
builder.useMeshPrefab = useMeshPrefab;
builder.overrideByGrid = overrideByGrid;
builder.compressBounds = compressBounds;
builder.overrideVector = overrideVector;
builder.CollectGeometry = surface.useGeometry;
builder.CollectObjects = (CollectObjects)(int)surface.collectObjects;
builder.parent = surface.gameObject;
builder.hideEditorLogs = surface.hideEditorLogs;
builder.SetRoot(navNeshState.roots);
NavMeshBuilder2d.CollectSources(sources, builder);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 70bd44a44cd62c64fbfc1eea95b24880
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: d5b0e13ebe59cd64e9f67284457c6868, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,115 @@
using NavMeshPlus.Components;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
namespace NavMeshPlus.Extensions
{
[ExecuteAlways]
[AddComponentMenu("Navigation/Navigation CacheSources2d", 30)]
public class CollectSourcesCache2d : NavMeshExtension
{
List<NavMeshBuildSource> _sources;
Dictionary<UnityEngine.Object, NavMeshBuildSource> _lookup;
private Bounds _sourcesBounds;
public bool IsDirty { get; protected set; }
private NavMeshBuilder2dState _state;
public int SourcesCount => _sources.Count;
public int CahcheCount => _lookup.Count;
public List<NavMeshBuildSource> Cache { get => _sources; }
protected override void Awake()
{
_lookup = new Dictionary<UnityEngine.Object, NavMeshBuildSource>();
_sources = new List<NavMeshBuildSource>();
IsDirty = false;
Order = -1000;
_sourcesBounds = new Bounds();
base.Awake();
}
protected override void OnDestroy()
{
_state?.Dispose();
base.OnDestroy();
}
public bool AddSource(GameObject gameObject, NavMeshBuildSource source)
{
var res = _lookup.ContainsKey(gameObject);
if (res)
{
return UpdateSource(gameObject);
}
_sources.Add(source);
_lookup.Add(gameObject, source);
IsDirty = true;
return true;
}
public bool UpdateSource(GameObject gameObject)
{
var res = _lookup.ContainsKey(gameObject);
if(res)
{
IsDirty = true;
var source = _lookup[gameObject];
var idx = _sources.IndexOf(source);
if (idx >= 0)
{
source.transform = Matrix4x4.TRS(gameObject.transform.position, gameObject.transform.rotation, gameObject.transform.lossyScale);
_sources[idx] = source;
_lookup[gameObject] = source;
}
}
return res;
}
public bool RemoveSource(GameObject gameObject)
{
var res = _lookup.ContainsKey(gameObject);
if (res)
{
IsDirty = true;
var source = _lookup[gameObject];
_lookup.Remove(gameObject);
_sources.Remove(source);
}
return res;
}
public AsyncOperation UpdateNavMesh(NavMeshData data)
{
IsDirty = false;
return NavMeshBuilder.UpdateNavMeshDataAsync(data, NavMeshSurfaceOwner.GetBuildSettings(), _sources, _sourcesBounds);
}
public AsyncOperation UpdateNavMesh()
{
return UpdateNavMesh(NavMeshSurfaceOwner.navMeshData);
}
public override void CollectSources(NavMeshSurface surface, List<NavMeshBuildSource> sources, NavMeshBuilderState navMeshState)
{
_lookup.Clear();
IsDirty = false;
_state?.Dispose();
_state = navMeshState.GetExtraState<NavMeshBuilder2dState>(false);
_state.lookupCallback = LookupCallback;
}
private void LookupCallback(UnityEngine.Object component, NavMeshBuildSource source)
{
if (component == null)
{
return;
}
_lookup.Add(component, source);
}
public override void PostCollectSources(NavMeshSurface surface, List<NavMeshBuildSource> sources, NavMeshBuilderState navNeshState)
{
_sourcesBounds = navNeshState.worldBounds;
_sources = sources;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5492b7ed96378624cbcd72212fce093d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: d5b0e13ebe59cd64e9f67284457c6868, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
using UnityEngine;
//***********************************************************************************
// Contributed by author jl-randazzo github.com/jl-randazzo
//***********************************************************************************
namespace NavMeshPlus.Extensions
{
[System.Serializable]
public class NavMeshAgentAttribute : PropertyAttribute
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f3fe2b336bf34749a146f6bf7d462d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,13 @@
using UnityEngine;
//***********************************************************************************
// Contributed by author jl-randazzo github.com/jl-randazzo
//***********************************************************************************
namespace NavMeshPlus.Extensions
{
[System.Serializable]
// See also NavMeshAreaAttributePropertyDrawer
public class NavMeshAreaAttribute : PropertyAttribute
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ed6d9f7764b9451f97a6658cdc760e00
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: d5b0e13ebe59cd64e9f67284457c6868, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,393 @@
using NavMeshPlus.Components;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;
using UnityEngine.Tilemaps;
using Object = UnityEngine.Object;
namespace NavMeshPlus.Extensions
{
class NavMeshBuilder2dState: IDisposable
{
public Dictionary<Sprite, Mesh> spriteMeshMap;
public Dictionary<uint, Mesh> coliderMeshMap;
public Action<UnityEngine.Object, NavMeshBuildSource> lookupCallback;
public int defaultArea;
public int layerMask;
public int agentID;
public bool overrideByGrid;
public GameObject useMeshPrefab;
public bool compressBounds;
public Vector3 overrideVector;
public NavMeshCollectGeometry CollectGeometry;
public CollectObjects CollectObjects;
public GameObject parent;
public bool hideEditorLogs;
protected IEnumerable<GameObject> _root;
private bool _disposed;
public IEnumerable<GameObject> Root => _root ?? GetRoot();
public NavMeshBuilder2dState()
{
spriteMeshMap = new Dictionary<Sprite, Mesh>();
coliderMeshMap = new Dictionary<uint, Mesh>();
_root = null;
}
public Mesh GetMesh(Sprite sprite)
{
Mesh mesh;
if (spriteMeshMap.ContainsKey(sprite))
{
mesh = spriteMeshMap[sprite];
}
else
{
mesh = new Mesh();
NavMeshBuilder2d.sprite2mesh(sprite, mesh);
spriteMeshMap.Add(sprite, mesh);
}
return mesh;
}
public Mesh GetMesh(Collider2D collider)
{
#if UNITY_2019_3_OR_NEWER
Mesh mesh;
uint hash = collider.GetShapeHash();
if (coliderMeshMap.ContainsKey(hash))
{
mesh = coliderMeshMap[hash];
}
else
{
mesh = collider.CreateMesh(false, false);
coliderMeshMap.Add(hash, mesh);
}
return mesh;
#else
throw new InvalidOperationException("PhysicsColliders supported in Unity 2019.3 and higher.");
#endif
}
public void SetRoot(IEnumerable<GameObject> root)
{
_root = root;
}
public IEnumerable<GameObject> GetRoot()
{
switch (CollectObjects)
{
case CollectObjects.Children: return new[] { parent };
case CollectObjects.Volume:
case CollectObjects.All:
default:
{
var list = new List<GameObject>();
var roots = new List<GameObject>();
for (int i = 0; i < SceneManager.sceneCount; ++i)
{
var s = SceneManager.GetSceneAt(i);
if (!s.isLoaded) continue;
s.GetRootGameObjects(list);
roots.AddRange(list);
}
return roots;
}
}
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
// TODO: dispose managed state (managed objects).
foreach (var item in spriteMeshMap)
{
#if UNITY_EDITOR
Object.DestroyImmediate(item.Value);
#else
Object.Destroy(item.Value);
#endif
}
foreach (var item in coliderMeshMap)
{
#if UNITY_EDITOR
Object.DestroyImmediate(item.Value);
#else
Object.Destroy(item.Value);
#endif
}
spriteMeshMap.Clear();
coliderMeshMap.Clear();
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
_disposed = true;
}
public void Dispose()
{
// Dispose of unmanaged resources.
Dispose(true);
// Suppress finalization.
GC.SuppressFinalize(this);
}
}
class NavMeshBuilder2d
{
public static void CollectSources(List<NavMeshBuildSource> sources, NavMeshBuilder2dState builder)
{
foreach (var it in builder.Root)
{
CollectSources(it, sources, builder);
}
if (!builder.hideEditorLogs) Debug.Log("Sources " + sources.Count);
}
public static void CollectSources(GameObject root, List<NavMeshBuildSource> sources, NavMeshBuilder2dState builder)
{
foreach (var modifier in root.GetComponentsInChildren<NavMeshModifier>())
{
if (((0x1 << modifier.gameObject.layer) & builder.layerMask) == 0)
{
continue;
}
if (!modifier.AffectsAgentType(builder.agentID))
{
continue;
}
int area = builder.defaultArea;
//if it is walkable
if (builder.defaultArea != 1 && !modifier.ignoreFromBuild)
{
AddDefaultWalkableTilemap(sources, builder, modifier);
}
if (modifier.overrideArea)
{
area = modifier.area;
}
if (!modifier.ignoreFromBuild)
{
CollectSources(sources, builder, modifier, area);
}
}
}
public static void CollectSources(List<NavMeshBuildSource> sources, NavMeshBuilder2dState builder, NavMeshModifier modifier, int area)
{
if (builder.CollectGeometry == NavMeshCollectGeometry.PhysicsColliders)
{
var collider = modifier.GetComponent<Collider2D>();
if (collider != null)
{
CollectSources(sources, collider, area, builder);
}
}
else
{
var tilemap = modifier.GetComponent<Tilemap>();
if (tilemap != null)
{
CollectTileSources(sources, tilemap, area, builder);
}
var sprite = modifier.GetComponent<SpriteRenderer>();
if (sprite != null)
{
CollectSources(sources, sprite, area, builder);
}
}
}
private static void AddDefaultWalkableTilemap(List<NavMeshBuildSource> sources, NavMeshBuilder2dState builder, NavMeshModifier modifier)
{
var tilemap = modifier.GetComponent<Tilemap>();
if (tilemap != null)
{
if (builder.compressBounds)
{
tilemap.CompressBounds();
}
if (!builder.hideEditorLogs) Debug.Log($"Walkable Bounds [{tilemap.name}]: {tilemap.localBounds}");
var box = BoxBoundSource(NavMeshSurface.GetWorldBounds(tilemap.transform.localToWorldMatrix, tilemap.localBounds));
box.area = builder.defaultArea;
sources.Add(box);
}
}
public static void CollectSources(List<NavMeshBuildSource> sources, SpriteRenderer spriteRenderer, int area, NavMeshBuilder2dState builder)
{
if (spriteRenderer == null || spriteRenderer.sprite == null)
{
return;
}
Mesh mesh;
mesh = builder.GetMesh(spriteRenderer.sprite);
if (mesh == null)
{
if (!builder.hideEditorLogs) Debug.Log($"{spriteRenderer.name} mesh is null");
return;
}
var src = new NavMeshBuildSource();
src.shape = NavMeshBuildSourceShape.Mesh;
src.component = spriteRenderer;
src.area = area;
src.transform = Matrix4x4.TRS(Vector3.Scale(spriteRenderer.transform.position, builder.overrideVector), spriteRenderer.transform.rotation, spriteRenderer.transform.lossyScale);
src.sourceObject = mesh;
sources.Add(src);
builder.lookupCallback?.Invoke(spriteRenderer.gameObject, src);
}
public static void CollectSources(List<NavMeshBuildSource> sources, Collider2D collider, int area, NavMeshBuilder2dState builder)
{
if (collider.usedByComposite)
{
collider = collider.GetComponent<CompositeCollider2D>();
}
Mesh mesh;
mesh = builder.GetMesh(collider);
if (mesh == null)
{
if (!builder.hideEditorLogs) Debug.Log($"{collider.name} mesh is null");
return;
}
var src = new NavMeshBuildSource();
src.shape = NavMeshBuildSourceShape.Mesh;
src.area = area;
src.component = collider;
src.sourceObject = mesh;
if (collider.attachedRigidbody)
{
src.transform = Matrix4x4.TRS(Vector3.Scale(collider.attachedRigidbody.transform.position, builder.overrideVector), collider.attachedRigidbody.transform.rotation, Vector3.one);
}
else
{
src.transform = Matrix4x4.identity;
}
sources.Add(src);
builder.lookupCallback?.Invoke(collider.gameObject, src);
}
public static void CollectTileSources(List<NavMeshBuildSource> sources, Tilemap tilemap, int area, NavMeshBuilder2dState builder)
{
var bound = tilemap.cellBounds;
var modifierTilemap = tilemap.GetComponent<NavMeshModifierTilemap>();
var vec3int = new Vector3Int(0, 0, 0);
var size = new Vector3(tilemap.layoutGrid.cellSize.x, tilemap.layoutGrid.cellSize.y, 0);
Mesh sharedMesh = null;
Quaternion rot = default;
if (builder.useMeshPrefab != null)
{
sharedMesh = builder.useMeshPrefab.GetComponent<MeshFilter>().sharedMesh;
size = builder.useMeshPrefab.transform.localScale;
rot = builder.useMeshPrefab.transform.rotation;
}
for (int i = bound.xMin; i < bound.xMax; i++)
{
for (int j = bound.yMin; j < bound.yMax; j++)
{
var src = new NavMeshBuildSource();
src.area = area;
vec3int.x = i;
vec3int.y = j;
if (!tilemap.HasTile(vec3int))
{
continue;
}
CollectTile(tilemap, builder, vec3int, size, sharedMesh, rot, ref src);
if (modifierTilemap && modifierTilemap.TryGetTileModifier(vec3int, tilemap, out NavMeshModifierTilemap.TileModifier tileModifier))
{
src.area = tileModifier.overrideArea ? tileModifier.area : area;
}
sources.Add(src);
builder.lookupCallback?.Invoke(tilemap.GetInstantiatedObject(vec3int), src);
}
}
}
private static void CollectTile(Tilemap tilemap, NavMeshBuilder2dState builder, Vector3Int vec3int, Vector3 size, Mesh sharedMesh, Quaternion rot, ref NavMeshBuildSource src)
{
if (!builder.overrideByGrid && tilemap.GetColliderType(vec3int) == Tile.ColliderType.Sprite)
{
var sprite = tilemap.GetSprite(vec3int);
if (sprite != null)
{
Mesh mesh = builder.GetMesh(sprite);
src.component = tilemap;
src.transform = GetCellTransformMatrix(tilemap, builder.overrideVector, vec3int);
src.shape = NavMeshBuildSourceShape.Mesh;
src.sourceObject = mesh;
}
}
else if (builder.useMeshPrefab != null || (builder.overrideByGrid && builder.useMeshPrefab != null))
{
src.transform = Matrix4x4.TRS(Vector3.Scale(tilemap.GetCellCenterWorld(vec3int), builder.overrideVector), rot, size);
src.shape = NavMeshBuildSourceShape.Mesh;
src.sourceObject = sharedMesh;
}
else //default to box
{
src.transform = GetCellTransformMatrix(tilemap, builder.overrideVector, vec3int);
src.shape = NavMeshBuildSourceShape.Box;
src.size = size;
}
}
public static Matrix4x4 GetCellTransformMatrix(Tilemap tilemap, Vector3 scale, Vector3Int vec3int)
{
return Matrix4x4.TRS(Vector3.Scale(tilemap.GetCellCenterWorld(vec3int), scale) - tilemap.layoutGrid.cellGap, tilemap.transform.rotation, tilemap.transform.lossyScale) * tilemap.orientationMatrix * tilemap.GetTransformMatrix(vec3int);
}
internal static void sprite2mesh(Sprite sprite, Mesh mesh)
{
Vector3[] vert = new Vector3[sprite.vertices.Length];
for (int i = 0; i < sprite.vertices.Length; i++)
{
vert[i] = new Vector3(sprite.vertices[i].x, sprite.vertices[i].y, 0);
}
mesh.vertices = vert;
mesh.uv = sprite.uv;
int[] tri = new int[sprite.triangles.Length];
for (int i = 0; i < sprite.triangles.Length; i++)
{
tri[i] = sprite.triangles[i];
}
mesh.triangles = tri;
}
static private NavMeshBuildSource BoxBoundSource(Bounds localBounds)
{
var src = new NavMeshBuildSource();
src.transform = Matrix4x4.Translate(localBounds.center);
src.shape = NavMeshBuildSourceShape.Box;
src.size = localBounds.size;
src.area = 0;
return src;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fc6dd3809c1761a4e9a227c70117ed54
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: e8142b1daeea8d3419cd0ffbd7b17a37, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace NavMeshPlus.Extensions
{
public class NavMeshBuilderState: IDisposable
{
public Matrix4x4 worldToLocal;
public Bounds worldBounds;
public IEnumerable<GameObject> roots;
private CompositeDisposable disposable;
private Dictionary<Type, System.Object> mExtraState;
private bool _disposed;
public T GetExtraState<T>(bool dispose = true) where T : class, new()
{
if (mExtraState == null)
{
mExtraState = new Dictionary<Type, System.Object>();
disposable = new CompositeDisposable();
}
if (!mExtraState.TryGetValue(typeof(T), out System.Object extra))
{
extra = mExtraState[typeof(T)] = new T();
if (dispose)
{
disposable.Add(extra);
}
}
return extra as T;
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
// TODO: dispose managed state (managed objects).
disposable?.Dispose();
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
_disposed = true;
}
public void Dispose()
{
// Dispose of unmanaged resources.
Dispose(true);
// Suppress finalization.
GC.SuppressFinalize(this);
}
}
partial class CompositeDisposable: IDisposable
{
private bool _disposed;
private List<IDisposable> extraStates = new List<IDisposable>();
public void Add(IDisposable dispose)
{
extraStates.Add(dispose);
}
public void Add(object dispose)
{
if(dispose is IDisposable)
{
extraStates.Add((IDisposable)dispose);
}
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
// TODO: dispose managed state (managed objects).
foreach (var item in extraStates)
{
item?.Dispose();
}
extraStates.Clear();
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
_disposed = true;
}
public void Dispose()
{
// Dispose of unmanaged resources.
Dispose(true);
// Suppress finalization.
GC.SuppressFinalize(this);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 710022065e740cf40bf86ccac60e3741
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: e8142b1daeea8d3419cd0ffbd7b17a37, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
{
"name": "NavMeshPlus",
"rootNamespace": "",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.modules.terrain",
"expression": "0",
"define": "IS_TERRAIN_USED"
}
],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 299beb10465c46f41abeaf10478b4727
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,58 @@
using NavMeshPlus.Components;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
namespace NavMeshPlus.Extensions
{
public abstract class NavMeshExtension: MonoBehaviour
{
public int Order { get; protected set; }
public virtual void CollectSources(NavMeshSurface surface, List<NavMeshBuildSource> sources, NavMeshBuilderState navNeshState) { }
public virtual void CalculateWorldBounds(NavMeshSurface surface, List<NavMeshBuildSource> sources, NavMeshBuilderState navNeshState) { }
public virtual void PostCollectSources(NavMeshSurface surface, List<NavMeshBuildSource> sources, NavMeshBuilderState navNeshState) { }
public NavMeshSurface NavMeshSurfaceOwner
{
get
{
if (m_navMeshOwner == null)
m_navMeshOwner = GetComponent<NavMeshSurface>();
return m_navMeshOwner;
}
}
NavMeshSurface m_navMeshOwner;
protected virtual void Awake()
{
ConnectToVcam(true);
}
#if UNITY_EDITOR
[UnityEditor.Callbacks.DidReloadScripts]
static void OnScriptReload()
{
var extensions = Resources.FindObjectsOfTypeAll(
typeof(NavMeshExtension)) as NavMeshExtension[];
foreach (var e in extensions)
e.ConnectToVcam(true);
}
#endif
protected virtual void OnEnable() { }
protected virtual void OnDestroy()
{
ConnectToVcam(false);
}
protected virtual void ConnectToVcam(bool connect)
{
if (connect && NavMeshSurfaceOwner == null)
Debug.LogError("NevMeshExtension requires a NavMeshSurface component");
if (NavMeshSurfaceOwner != null)
{
if (connect)
NavMeshSurfaceOwner.NevMeshExtensions.Add(this, Order);
else
NavMeshSurfaceOwner.NevMeshExtensions.Remove(this);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0528335b682da1f42901dc790b763830
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: e8142b1daeea8d3419cd0ffbd7b17a37, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace NavMeshPlus.Extensions
{
public interface INavMeshExtensionsProvider
{
int Count { get; }
NavMeshExtension this[int index] { get; }
void Add(NavMeshExtension extension, int order);
void Remove(NavMeshExtension extension);
}
internal class NavMeshExtensionMeta
{
public int order;
public NavMeshExtensionMeta(int order, NavMeshExtension extension)
{
this.order = order;
this.extension = extension;
}
public NavMeshExtension extension;
}
internal class NavMeshExtensionsProvider : INavMeshExtensionsProvider
{
List<NavMeshExtensionMeta> _extensions = new List<NavMeshExtensionMeta>();
static Comparer<NavMeshExtensionMeta> Comparer = Comparer<NavMeshExtensionMeta>.Create((x, y) => x.order > y.order ? 1 : x.order < y.order ? -1 : 0);
public NavMeshExtension this[int index] => _extensions[index].extension;
public int Count => _extensions.Count;
public void Add(NavMeshExtension extension, int order)
{
var meta = new NavMeshExtensionMeta(order, extension);
var at = _extensions.BinarySearch(meta, Comparer);
if (at < 0)
{
_extensions.Add(meta);
_extensions.Sort(Comparer);
}
else
{
_extensions.Insert(at, meta);
}
}
public void Remove(NavMeshExtension extension)
{
_extensions.RemoveAll(x => x.extension = extension);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dc9c5ff0af7a2f247a2c64921cf9d045
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: e8142b1daeea8d3419cd0ffbd7b17a37, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,175 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using NavMeshPlus.Extensions;
namespace NavMeshPlus.Components
{
[ExecuteInEditMode]
[DefaultExecutionOrder(-101)]
[AddComponentMenu("Navigation/Navigation Link", 33)]
[HelpURL("https://github.com/Unity-Technologies/NavMeshPlus#documentation-draft")]
public class NavMeshLink : MonoBehaviour
{
[SerializeField, NavMeshAgent]
int m_AgentTypeID;
public int agentTypeID { get { return m_AgentTypeID; } set { m_AgentTypeID = value; UpdateLink(); } }
[SerializeField]
Vector3 m_StartPoint = new Vector3(0.0f, 0.0f, -2.5f);
public Vector3 startPoint { get { return m_StartPoint; } set { m_StartPoint = value; UpdateLink(); } }
[SerializeField]
Vector3 m_EndPoint = new Vector3(0.0f, 0.0f, 2.5f);
public Vector3 endPoint { get { return m_EndPoint; } set { m_EndPoint = value; UpdateLink(); } }
[SerializeField]
float m_Width;
public float width { get { return m_Width; } set { m_Width = value; UpdateLink(); } }
[SerializeField]
int m_CostModifier = -1;
public int costModifier { get { return m_CostModifier; } set { m_CostModifier = value; UpdateLink(); } }
[SerializeField]
bool m_Bidirectional = true;
public bool bidirectional { get { return m_Bidirectional; } set { m_Bidirectional = value; UpdateLink(); } }
[SerializeField]
bool m_AutoUpdatePosition;
public bool autoUpdate { get { return m_AutoUpdatePosition; } set { SetAutoUpdate(value); } }
[SerializeField, NavMeshArea]
int m_Area;
public int area { get { return m_Area; } set { m_Area = value; UpdateLink(); } }
NavMeshLinkInstance m_LinkInstance = new NavMeshLinkInstance();
Vector3 m_LastPosition = Vector3.zero;
Quaternion m_LastRotation = Quaternion.identity;
static readonly List<NavMeshLink> s_Tracked = new List<NavMeshLink>();
void OnEnable()
{
AddLink();
if (m_AutoUpdatePosition && m_LinkInstance.valid)
AddTracking(this);
}
void OnDisable()
{
RemoveTracking(this);
m_LinkInstance.Remove();
}
public void UpdateLink()
{
m_LinkInstance.Remove();
AddLink();
}
static void AddTracking(NavMeshLink link)
{
#if UNITY_EDITOR
if (s_Tracked.Contains(link))
{
Debug.LogError("Link is already tracked: " + link);
return;
}
#endif
if (s_Tracked.Count == 0)
NavMesh.onPreUpdate += UpdateTrackedInstances;
s_Tracked.Add(link);
}
static void RemoveTracking(NavMeshLink link)
{
s_Tracked.Remove(link);
if (s_Tracked.Count == 0)
NavMesh.onPreUpdate -= UpdateTrackedInstances;
}
void SetAutoUpdate(bool value)
{
if (m_AutoUpdatePosition == value)
return;
m_AutoUpdatePosition = value;
if (value)
AddTracking(this);
else
RemoveTracking(this);
}
void AddLink()
{
#if UNITY_EDITOR
if (m_LinkInstance.valid)
{
Debug.LogError("Link is already added: " + this);
return;
}
#endif
var link = new NavMeshLinkData();
link.startPosition = m_StartPoint;
link.endPosition = m_EndPoint;
link.width = m_Width;
link.costModifier = m_CostModifier;
link.bidirectional = m_Bidirectional;
link.area = m_Area;
link.agentTypeID = m_AgentTypeID;
m_LinkInstance = NavMesh.AddLink(link, transform.position, transform.rotation);
if (m_LinkInstance.valid)
m_LinkInstance.owner = this;
m_LastPosition = transform.position;
m_LastRotation = transform.rotation;
}
bool HasTransformChanged()
{
if (m_LastPosition != transform.position) return true;
if (m_LastRotation != transform.rotation) return true;
return false;
}
void OnDidApplyAnimationProperties()
{
UpdateLink();
}
static void UpdateTrackedInstances()
{
foreach (var instance in s_Tracked)
{
if (instance.HasTransformChanged())
instance.UpdateLink();
}
}
#if UNITY_EDITOR
void OnValidate()
{
m_Width = Mathf.Max(0.0f, m_Width);
if (!m_LinkInstance.valid)
return;
UpdateLink();
if (!m_AutoUpdatePosition)
{
RemoveTracking(this);
}
else if (!s_Tracked.Contains(this))
{
AddTracking(this);
}
}
#endif
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b9acdb93a5f98644a2e18d4884f04e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 68ad4f5d6fe957c4789aedd21ff67ced, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,56 @@
using System.Collections.Generic;
using UnityEngine;
using NavMeshPlus.Extensions;
namespace NavMeshPlus.Components
{
[ExecuteInEditMode]
[AddComponentMenu("Navigation/Navigation Modifier", 32)]
[HelpURL("https://github.com/Unity-Technologies/NavMeshComponents#documentation-draft")]
public class NavMeshModifier : MonoBehaviour
{
[SerializeField]
bool m_OverrideArea;
public bool overrideArea { get { return m_OverrideArea; } set { m_OverrideArea = value; } }
[SerializeField, NavMeshArea]
int m_Area;
public int area { get { return m_Area; } set { m_Area = value; } }
[SerializeField]
bool m_IgnoreFromBuild;
public bool ignoreFromBuild { get { return m_IgnoreFromBuild; } set { m_IgnoreFromBuild = value; } }
// List of agent types the modifier is applied for.
// Special values: empty == None, m_AffectedAgents[0] =-1 == All.
[SerializeField]
List<int> m_AffectedAgents = new List<int>(new int[] { -1 }); // Default value is All
static readonly List<NavMeshModifier> s_NavMeshModifiers = new List<NavMeshModifier>();
public static List<NavMeshModifier> activeModifiers
{
get { return s_NavMeshModifiers; }
}
void OnEnable()
{
if (!s_NavMeshModifiers.Contains(this))
s_NavMeshModifiers.Add(this);
}
void OnDisable()
{
s_NavMeshModifiers.Remove(this);
}
public bool AffectsAgentType(int agentTypeID)
{
if (m_AffectedAgents.Count == 0)
return false;
if (m_AffectedAgents[0] == -1)
return true;
return m_AffectedAgents.IndexOf(agentTypeID) != -1;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9809bf1345abc5648af68b3a82653f08
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 273c8b5db6e39534781066db3444fe88, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,69 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using NavMeshPlus.Extensions;
using UnityEngine.Tilemaps;
//***********************************************************************************
// Contributed by author jl-randazzo github.com/jl-randazzo
//***********************************************************************************
namespace NavMeshPlus.Components
{
[AddComponentMenu("Navigation/Navigation Modifier Tilemap", 33)]
[HelpURL("https://github.com/Unity-Technologies/NavMeshComponents#documentation-draft")]
[RequireComponent(typeof(Tilemap))]
[RequireComponent(typeof(NavMeshModifier))]
[DisallowMultipleComponent]
[ExecuteInEditMode]
public class NavMeshModifierTilemap : MonoBehaviour
{
[System.Serializable]
public struct TileModifier
{
public TileBase tile;
public bool overrideArea;
[NavMeshArea] public int area;
}
private class MatchingTileComparator : IEqualityComparer<TileModifier>
{
public static readonly IEqualityComparer<TileModifier> Instance = new MatchingTileComparator();
public bool Equals(TileModifier a, TileModifier b) => a.tile == b.tile;
public int GetHashCode(TileModifier tileModifier) => tileModifier.GetHashCode();
}
[SerializeField]
List<TileModifier> m_TileModifiers = new List<TileModifier>();
private Dictionary<TileBase, TileModifier> m_ModifierMap;
public Dictionary<TileBase, TileModifier> GetModifierMap() => m_TileModifiers.Where(mod => mod.tile != null).Distinct(MatchingTileComparator.Instance).ToDictionary(mod => mod.tile);
void OnEnable()
{
CacheModifiers();
}
public void CacheModifiers()
{
m_ModifierMap = GetModifierMap();
}
#if UNITY_EDITOR
public bool HasDuplicateTileModifiers()
{
return m_TileModifiers.Count != m_TileModifiers.Distinct(MatchingTileComparator.Instance).Count();
}
#endif // UNITY_EDITOR
public virtual bool TryGetTileModifier(Vector3Int coords, Tilemap tilemap, out TileModifier modifier)
{
if (tilemap.GetTile(coords) is TileBase tileBase)
{
return m_ModifierMap.TryGetValue(tileBase, out modifier);
}
modifier = new TileModifier();
return false;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 709c8d6349be44c68dff3d220992400c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 273c8b5db6e39534781066db3444fe88, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,56 @@
using System.Collections.Generic;
using UnityEngine;
using NavMeshPlus.Extensions;
namespace NavMeshPlus.Components
{
[ExecuteInEditMode]
[AddComponentMenu("Navigation/Navigation ModifierVolume", 31)]
[HelpURL("https://github.com/Unity-Technologies/NavMeshComponents#documentation-draft")]
public class NavMeshModifierVolume : MonoBehaviour
{
[SerializeField]
Vector3 m_Size = new Vector3(4.0f, 3.0f, 4.0f);
public Vector3 size { get { return m_Size; } set { m_Size = value; } }
[SerializeField]
Vector3 m_Center = new Vector3(0, 1.0f, 0);
public Vector3 center { get { return m_Center; } set { m_Center = value; } }
[SerializeField, NavMeshArea]
int m_Area;
public int area { get { return m_Area; } set { m_Area = value; } }
// List of agent types the modifier is applied for.
// Special values: empty == None, m_AffectedAgents[0] =-1 == All.
[SerializeField]
List<int> m_AffectedAgents = new List<int>(new int[] { -1 }); // Default value is All
static readonly List<NavMeshModifierVolume> s_NavMeshModifiers = new List<NavMeshModifierVolume>();
public static List<NavMeshModifierVolume> activeModifiers
{
get { return s_NavMeshModifiers; }
}
void OnEnable()
{
if (!s_NavMeshModifiers.Contains(this))
s_NavMeshModifiers.Add(this);
}
void OnDisable()
{
s_NavMeshModifiers.Remove(this);
}
public bool AffectsAgentType(int agentTypeID)
{
if (m_AffectedAgents.Count == 0)
return false;
if (m_AffectedAgents[0] == -1)
return true;
return m_AffectedAgents.IndexOf(agentTypeID) != -1;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7fa0352a4d56abd42ad2fc69deb72448
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 273c8b5db6e39534781066db3444fe88, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,543 @@
using NavMeshPlus.Extensions;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
#endif
namespace NavMeshPlus.Components
{
public enum CollectObjects
{
All = 0,
Volume = 1,
Children = 2,
}
[ExecuteAlways]
[DefaultExecutionOrder(-102)]
[AddComponentMenu("Navigation/Navigation Surface", 30)]
[HelpURL("https://github.com/Unity-Technologies/NavMeshComponents#documentation-draft")]
public class NavMeshSurface : MonoBehaviour
{
[SerializeField, NavMeshAgent]
int m_AgentTypeID;
public int agentTypeID { get { return m_AgentTypeID; } set { m_AgentTypeID = value; } }
[SerializeField]
CollectObjects m_CollectObjects = CollectObjects.All;
public CollectObjects collectObjects { get { return m_CollectObjects; } set { m_CollectObjects = value; } }
[SerializeField]
Vector3 m_Size = new Vector3(10.0f, 10.0f, 10.0f);
public Vector3 size { get { return m_Size; } set { m_Size = value; } }
[SerializeField]
Vector3 m_Center = new Vector3(0, 2.0f, 0);
public Vector3 center { get { return m_Center; } set { m_Center = value; } }
[SerializeField]
LayerMask m_LayerMask = ~0;
public LayerMask layerMask { get { return m_LayerMask; } set { m_LayerMask = value; } }
[SerializeField]
NavMeshCollectGeometry m_UseGeometry = NavMeshCollectGeometry.RenderMeshes;
public NavMeshCollectGeometry useGeometry { get { return m_UseGeometry; } set { m_UseGeometry = value; } }
[SerializeField, NavMeshArea]
int m_DefaultArea;
public int defaultArea { get { return m_DefaultArea; } set { m_DefaultArea = value; } }
[SerializeField]
bool m_IgnoreNavMeshAgent = true;
public bool ignoreNavMeshAgent { get { return m_IgnoreNavMeshAgent; } set { m_IgnoreNavMeshAgent = value; } }
[SerializeField]
bool m_IgnoreNavMeshObstacle = true;
public bool ignoreNavMeshObstacle { get { return m_IgnoreNavMeshObstacle; } set { m_IgnoreNavMeshObstacle = value; } }
[SerializeField]
bool m_OverrideTileSize;
public bool overrideTileSize { get { return m_OverrideTileSize; } set { m_OverrideTileSize = value; } }
[SerializeField]
int m_TileSize = 256;
public int tileSize { get { return m_TileSize; } set { m_TileSize = value; } }
[SerializeField]
bool m_OverrideVoxelSize;
public bool overrideVoxelSize { get { return m_OverrideVoxelSize; } set { m_OverrideVoxelSize = value; } }
[SerializeField]
float m_VoxelSize;
public float voxelSize { get { return m_VoxelSize; } set { m_VoxelSize = value; } }
// Currently not supported advanced options
[SerializeField]
bool m_BuildHeightMesh;
public bool buildHeightMesh { get { return m_BuildHeightMesh; } set { m_BuildHeightMesh = value; } }
[SerializeField]
bool m_HideEditorLogs;
public bool hideEditorLogs { get { return m_HideEditorLogs; } set { m_HideEditorLogs = value; } }
// Reference to whole scene navmesh data asset.
[UnityEngine.Serialization.FormerlySerializedAs("m_BakedNavMeshData")]
[SerializeField]
NavMeshData m_NavMeshData;
public NavMeshData navMeshData { get { return m_NavMeshData; } set { m_NavMeshData = value; } }
// Do not serialize - runtime only state.
NavMeshDataInstance m_NavMeshDataInstance;
Vector3 m_LastPosition = Vector3.zero;
Quaternion m_LastRotation = Quaternion.identity;
static readonly List<NavMeshSurface> s_NavMeshSurfaces = new List<NavMeshSurface>();
public INavMeshExtensionsProvider NevMeshExtensions { get; set; } = new NavMeshExtensionsProvider();
public static List<NavMeshSurface> activeSurfaces
{
get { return s_NavMeshSurfaces; }
}
void OnEnable()
{
Register(this);
AddData();
}
void OnDisable()
{
RemoveData();
Unregister(this);
}
public void AddData()
{
#if UNITY_EDITOR
var isInPreviewScene = EditorSceneManager.IsPreviewSceneObject(this);
var isPrefab = isInPreviewScene || EditorUtility.IsPersistent(this);
if (isPrefab)
{
//Debug.LogFormat("NavMeshData from {0}.{1} will not be added to the NavMesh world because the gameObject is a prefab.",
// gameObject.name, name);
return;
}
#endif
if (m_NavMeshDataInstance.valid)
return;
if (m_NavMeshData != null)
{
m_NavMeshDataInstance = NavMesh.AddNavMeshData(m_NavMeshData, transform.position, transform.rotation);
m_NavMeshDataInstance.owner = this;
}
m_LastPosition = transform.position;
m_LastRotation = transform.rotation;
}
public void RemoveData()
{
m_NavMeshDataInstance.Remove();
m_NavMeshDataInstance = new NavMeshDataInstance();
}
public NavMeshBuildSettings GetBuildSettings()
{
var buildSettings = NavMesh.GetSettingsByID(m_AgentTypeID);
if (buildSettings.agentTypeID == -1)
{
if (!m_HideEditorLogs) Debug.LogWarning("No build settings for agent type ID " + agentTypeID, this);
buildSettings.agentTypeID = m_AgentTypeID;
}
if (overrideTileSize)
{
buildSettings.overrideTileSize = true;
buildSettings.tileSize = tileSize;
}
if (overrideVoxelSize)
{
buildSettings.overrideVoxelSize = true;
buildSettings.voxelSize = voxelSize;
}
return buildSettings;
}
public void BuildNavMesh()
{
using var builderState = new NavMeshBuilderState() { };
var sources = CollectSources(builderState);
// Use unscaled bounds - this differs in behaviour from e.g. collider components.
// But is similar to reflection probe - and since navmesh data has no scaling support - it is the right choice here.
var sourcesBounds = new Bounds(m_Center, Abs(m_Size));
if (m_CollectObjects == CollectObjects.All || m_CollectObjects == CollectObjects.Children)
{
sourcesBounds = CalculateWorldBounds(sources);
}
builderState.worldBounds = sourcesBounds;
for (int i = 0; i < NevMeshExtensions.Count; ++i)
{
NevMeshExtensions[i].PostCollectSources(this, sources, builderState);
}
var data = NavMeshBuilder.BuildNavMeshData(GetBuildSettings(),
sources, sourcesBounds, transform.position, transform.rotation);
if (data != null)
{
data.name = gameObject.name;
RemoveData();
m_NavMeshData = data;
if (isActiveAndEnabled)
AddData();
}
}
// Source: https://github.com/Unity-Technologies/NavMeshComponents/issues/97#issuecomment-528692289
public AsyncOperation BuildNavMeshAsync()
{
RemoveData();
m_NavMeshData = new NavMeshData(m_AgentTypeID)
{
name = gameObject.name,
position = transform.position,
rotation = transform.rotation
};
if (isActiveAndEnabled)
{
AddData();
}
return UpdateNavMesh(m_NavMeshData);
}
public AsyncOperation UpdateNavMesh(NavMeshData data)
{
using var builderState = new NavMeshBuilderState() { };
var sources = CollectSources(builderState);
// Use unscaled bounds - this differs in behaviour from e.g. collider components.
// But is similar to reflection probe - and since navmesh data has no scaling support - it is the right choice here.
var sourcesBounds = new Bounds(m_Center, Abs(m_Size));
if (m_CollectObjects == CollectObjects.All || m_CollectObjects == CollectObjects.Children)
{
sourcesBounds = CalculateWorldBounds(sources);
}
builderState.worldBounds = sourcesBounds;
for (int i = 0; i < NevMeshExtensions.Count; ++i)
{
NevMeshExtensions[i].PostCollectSources(this, sources, builderState);
}
return NavMeshBuilder.UpdateNavMeshDataAsync(data, GetBuildSettings(), sources, sourcesBounds);
}
static void Register(NavMeshSurface surface)
{
#if UNITY_EDITOR
var isInPreviewScene = EditorSceneManager.IsPreviewSceneObject(surface);
var isPrefab = isInPreviewScene || EditorUtility.IsPersistent(surface);
if (isPrefab)
{
//Debug.LogFormat("NavMeshData from {0}.{1} will not be added to the NavMesh world because the gameObject is a prefab.",
// surface.gameObject.name, surface.name);
return;
}
#endif
if (s_NavMeshSurfaces.Count == 0)
NavMesh.onPreUpdate += UpdateActive;
if (!s_NavMeshSurfaces.Contains(surface))
s_NavMeshSurfaces.Add(surface);
}
static void Unregister(NavMeshSurface surface)
{
s_NavMeshSurfaces.Remove(surface);
if (s_NavMeshSurfaces.Count == 0)
NavMesh.onPreUpdate -= UpdateActive;
}
static void UpdateActive()
{
for (var i = 0; i < s_NavMeshSurfaces.Count; ++i)
s_NavMeshSurfaces[i].UpdateDataIfTransformChanged();
}
void AppendModifierVolumes(ref List<NavMeshBuildSource> sources)
{
#if UNITY_EDITOR
var myStage = StageUtility.GetStageHandle(gameObject);
if (!myStage.IsValid())
return;
#endif
// Modifiers
List<NavMeshModifierVolume> modifiers;
if (m_CollectObjects == CollectObjects.Children)
{
modifiers = new List<NavMeshModifierVolume>(GetComponentsInChildren<NavMeshModifierVolume>());
modifiers.RemoveAll(x => !x.isActiveAndEnabled);
}
else
{
modifiers = NavMeshModifierVolume.activeModifiers;
}
foreach (var m in modifiers)
{
if ((m_LayerMask & (1 << m.gameObject.layer)) == 0)
continue;
if (!m.AffectsAgentType(m_AgentTypeID))
continue;
#if UNITY_EDITOR
if (!myStage.Contains(m.gameObject))
continue;
#endif
var mcenter = m.transform.TransformPoint(m.center);
var scale = m.transform.lossyScale;
var msize = new Vector3(m.size.x * Mathf.Abs(scale.x), m.size.y * Mathf.Abs(scale.y), m.size.z * Mathf.Abs(scale.z));
var src = new NavMeshBuildSource();
src.shape = NavMeshBuildSourceShape.ModifierBox;
src.transform = Matrix4x4.TRS(mcenter, m.transform.rotation, Vector3.one);
src.size = msize;
src.area = m.area;
sources.Add(src);
}
}
List<NavMeshBuildSource> CollectSources(NavMeshBuilderState builderState)
{
var sources = new List<NavMeshBuildSource>();
var markups = new List<NavMeshBuildMarkup>();
List<NavMeshModifier> modifiers;
if (m_CollectObjects == CollectObjects.Children)
{
modifiers = new List<NavMeshModifier>(GetComponentsInChildren<NavMeshModifier>());
modifiers.RemoveAll(x => !x.isActiveAndEnabled);
}
else
{
modifiers = NavMeshModifier.activeModifiers;
}
foreach (var m in modifiers)
{
if ((m_LayerMask & (1 << m.gameObject.layer)) == 0)
continue;
if (!m.AffectsAgentType(m_AgentTypeID))
continue;
var markup = new NavMeshBuildMarkup();
markup.root = m.transform;
markup.overrideArea = m.overrideArea;
markup.area = m.area;
markup.ignoreFromBuild = m.ignoreFromBuild;
markups.Add(markup);
}
#if UNITY_EDITOR
if (!EditorApplication.isPlaying)
{
if (m_CollectObjects == CollectObjects.All)
{
UnityEditor.AI.NavMeshBuilder.CollectSourcesInStage(
null, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, gameObject.scene, sources);
}
else if (m_CollectObjects == CollectObjects.Children)
{
UnityEditor.AI.NavMeshBuilder.CollectSourcesInStage(
transform, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, gameObject.scene, sources);
}
else if (m_CollectObjects == CollectObjects.Volume)
{
Matrix4x4 localToWorld = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
var worldBounds = GetWorldBounds(localToWorld, new Bounds(m_Center, m_Size));
UnityEditor.AI.NavMeshBuilder.CollectSourcesInStage(
worldBounds, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, gameObject.scene, sources);
}
for (int i = 0; i < NevMeshExtensions.Count; ++i)
{
NevMeshExtensions[i].CollectSources(this, sources, builderState);
}
}
else
#endif
{
if (m_CollectObjects == CollectObjects.All)
{
NavMeshBuilder.CollectSources(null, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
}
else if (m_CollectObjects == CollectObjects.Children)
{
NavMeshBuilder.CollectSources(transform, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
}
else if (m_CollectObjects == CollectObjects.Volume)
{
Matrix4x4 localToWorld = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
var worldBounds = GetWorldBounds(localToWorld, new Bounds(m_Center, m_Size));
NavMeshBuilder.CollectSources(worldBounds, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
}
for (int i = 0; i < NevMeshExtensions.Count; ++i)
{
NevMeshExtensions[i].CollectSources(this, sources, builderState);
}
}
if (m_IgnoreNavMeshAgent)
sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshAgent>() != null));
if (m_IgnoreNavMeshObstacle)
sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshObstacle>() != null));
AppendModifierVolumes(ref sources);
return sources;
}
static Vector3 Abs(Vector3 v)
{
return new Vector3(Mathf.Abs(v.x), Mathf.Abs(v.y), Mathf.Abs(v.z));
}
public static Bounds GetWorldBounds(Matrix4x4 mat, Bounds bounds)
{
var absAxisX = Abs(mat.MultiplyVector(Vector3.right));
var absAxisY = Abs(mat.MultiplyVector(Vector3.up));
var absAxisZ = Abs(mat.MultiplyVector(Vector3.forward));
var worldPosition = mat.MultiplyPoint(bounds.center);
var worldSize = absAxisX * bounds.size.x + absAxisY * bounds.size.y + absAxisZ * bounds.size.z;
return new Bounds(worldPosition, worldSize);
}
public Bounds CalculateWorldBounds(List<NavMeshBuildSource> sources)
{
// Use the unscaled matrix for the NavMeshSurface
Matrix4x4 worldToLocal = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
worldToLocal = worldToLocal.inverse;
var result = new Bounds();
var builderState = new NavMeshBuilderState() { worldBounds = result, worldToLocal = worldToLocal };
for (int i = 0; i < NevMeshExtensions.Count; ++i)
{
NevMeshExtensions[i].CalculateWorldBounds(this, sources, builderState);
result.Encapsulate(builderState.worldBounds);
}
foreach (var src in sources)
{
switch (src.shape)
{
case NavMeshBuildSourceShape.Mesh:
{
var m = src.sourceObject as Mesh;
result.Encapsulate(GetWorldBounds(worldToLocal * src.transform, m.bounds));
break;
}
case NavMeshBuildSourceShape.Terrain:
{
#if IS_TERRAIN_USED
// Terrain pivot is lower/left corner - shift bounds accordingly
var t = src.sourceObject as TerrainData;
result.Encapsulate(GetWorldBounds(worldToLocal * src.transform, new Bounds(0.5f * t.size, t.size)));
#endif
break;
}
case NavMeshBuildSourceShape.Box:
case NavMeshBuildSourceShape.Sphere:
case NavMeshBuildSourceShape.Capsule:
case NavMeshBuildSourceShape.ModifierBox:
result.Encapsulate(GetWorldBounds(worldToLocal * src.transform, new Bounds(Vector3.zero, src.size)));
break;
}
}
// Inflate the bounds a bit to avoid clipping co-planar sources
result.Expand(0.1f);
return result;
}
bool HasTransformChanged()
{
if (m_LastPosition != transform.position) return true;
if (m_LastRotation != transform.rotation) return true;
return false;
}
void UpdateDataIfTransformChanged()
{
if (HasTransformChanged())
{
RemoveData();
AddData();
}
}
#if UNITY_EDITOR
bool UnshareNavMeshAsset()
{
// Nothing to unshare
if (m_NavMeshData == null)
return false;
// Prefab parent owns the asset reference
var isInPreviewScene = EditorSceneManager.IsPreviewSceneObject(this);
var isPersistentObject = EditorUtility.IsPersistent(this);
if (isInPreviewScene || isPersistentObject)
return false;
// An instance can share asset reference only with its prefab parent
var prefab = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(this) as NavMeshSurface;
if (prefab != null && prefab.navMeshData == navMeshData)
return false;
// Don't allow referencing an asset that's assigned to another surface
for (var i = 0; i < s_NavMeshSurfaces.Count; ++i)
{
var surface = s_NavMeshSurfaces[i];
if (surface != this && surface.m_NavMeshData == m_NavMeshData)
return true;
}
// Asset is not referenced by known surfaces
return false;
}
void OnValidate()
{
if (UnshareNavMeshAsset())
{
if (!m_HideEditorLogs) Debug.LogWarning("Duplicating NavMeshSurface does not duplicate the referenced navmesh data", this);
m_NavMeshData = null;
}
var settings = NavMesh.GetSettingsByID(m_AgentTypeID);
if (settings.agentTypeID != -1)
{
// When unchecking the override control, revert to automatic value.
const float kMinVoxelSize = 0.01f;
if (!m_OverrideVoxelSize)
m_VoxelSize = settings.agentRadius / 3.0f;
if (m_VoxelSize < kMinVoxelSize)
m_VoxelSize = kMinVoxelSize;
// When unchecking the override control, revert to default value.
const int kMinTileSize = 16;
const int kMaxTileSize = 1024;
const int kDefaultTileSize = 256;
if (!m_OverrideTileSize)
m_TileSize = kDefaultTileSize;
// Make sure tilesize is in sane range.
if (m_TileSize < kMinTileSize)
m_TileSize = kMinTileSize;
if (m_TileSize > kMaxTileSize)
m_TileSize = kMaxTileSize;
}
}
#endif
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e04976799df50f54ba128eff723155a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 444810ca896903c41adf617b35274dc4, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
using NavMeshPlus.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AI;
namespace NavMeshPlus.Extensions
{
[ExecuteAlways]
[AddComponentMenu("Navigation/Navigation RootSources2d", 30)]
public class RootSources2d: NavMeshExtension
{
[SerializeField]
private List<GameObject> _rootSources;
public List<GameObject> RootSources { get => _rootSources; set => _rootSources = value; }
protected override void Awake()
{
Order = -1000;
base.Awake();
}
public override void CollectSources(NavMeshSurface surface, List<NavMeshBuildSource> sources, NavMeshBuilderState navNeshState)
{
navNeshState.roots = _rootSources;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6fec7b7904f76c4498a68fd145934563
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: d5b0e13ebe59cd64e9f67284457c6868, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,40 @@
using UnityEngine;
using UnityEngine.AI;
//***********************************************************************************
// Contributed by author @Lazy_Sloth from unity forum (https://forum.unity.com/)
//***********************************************************************************
namespace NavMeshPlus.Extensions
{
public class RotateAgentInstantly: IAgentOverride
{
public RotateAgentInstantly(NavMeshAgent agent, AgentOverride2d owner)
{
this.agent = agent;
this.owner = owner;
}
private NavMeshAgent agent;
private AgentOverride2d owner;
private Vector3 nextWaypoint;
public void UpdateAgent()
{
if (agent.hasPath && agent.path.corners.Length > 1)
{
if (nextWaypoint != agent.path.corners[1])
{
RotateToPoint(agent.path.corners[1], agent.transform);
nextWaypoint = agent.path.corners[1];
}
}
}
private static void RotateToPoint(Vector3 targetPoint, Transform transform)
{
Vector3 targetVector = targetPoint - transform.position;
float angleDifference = Vector2.SignedAngle(transform.up, targetVector);
transform.rotation = Quaternion.Euler(0, 0, transform.localEulerAngles.z + angleDifference);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b304bdab91a28c469562d02b8225df3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: e8142b1daeea8d3419cd0ffbd7b17a37, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,57 @@
using System.Collections;
using UnityEngine;
using UnityEngine.AI;
//***********************************************************************************
// Contributed by author @Lazy_Sloth from unity forum (https://forum.unity.com/)
//***********************************************************************************
namespace NavMeshPlus.Extensions
{
public class RotateAgentSmoothly: IAgentOverride
{
public RotateAgentSmoothly(NavMeshAgent agent, AgentOverride2d owner, float rotateSpeed)
{
this.agent = agent;
this.owner = owner;
this.rotateSpeed = rotateSpeed;
}
private NavMeshAgent agent;
private AgentOverride2d owner;
private Vector2 nextWaypoint;
private float angleDifference;
private float targetAngle;
public float rotateSpeed;
public void UpdateAgent()
{
if (agent.hasPath && agent.path.corners.Length > 1)
{
if (nextWaypoint != (Vector2)agent.path.corners[1])
{
owner.StartCoroutine(_RotateCoroutine());
nextWaypoint = agent.path.corners[1];
}
}
}
protected IEnumerator _RotateCoroutine()
{
yield return RotateToWaypoints(agent.transform);
}
protected IEnumerator RotateToWaypoints(Transform transform)
{
Vector2 targetVector = agent.path.corners[1] - transform.position;
angleDifference = Vector2.SignedAngle(transform.up, targetVector);
targetAngle = transform.localEulerAngles.z + angleDifference;
if (targetAngle >= 360) { targetAngle -= 360; }
else if (targetAngle < 0) { targetAngle += 360; }
while (transform.localEulerAngles.z < targetAngle - 0.1f || transform.localEulerAngles.z > targetAngle + 0.1f)
{
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(0, 0, targetAngle), rotateSpeed * Time.deltaTime);
yield return null;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f8df5c105d35bd4787c3b4ce3e1c56b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: e8142b1daeea8d3419cd0ffbd7b17a37, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,54 @@
# NavMeshPlus
NavMeshComponents.Extensions provides you with ability to create navigation meshes that are generated automatically from your Scene
geometry, which allows characters to move intelligently around the game world.
![NavMesh](https://github.com/h8man/NavMeshPlus/wiki/images/Tittle-01.png)
# Unity 2D Pathfinding
This repo is fork of Unity NavMeshComponents enhanced with Extensions system for 2d Pathfinding and more. [[link]](https://github.com/Unity-Technologies/NavMeshComponents)
#### Wiki [[here]](https://github.com/h8man/NavMeshPlus/wiki)
#### How To [[here]](https://github.com/h8man/NavMeshPlus/wiki/HOW-TO).
#### Demo [[github]](https://github.com/h8man/RedHotSweetPepper ).
#### Discuss [[unityforum]](https://forum.unity.com/threads/2d-navmesh-pathfinding.503596/ ).
# 2D NavMesh
In repo you will find implementation of NavMeshSurface and 2d Extensions for tilemap, sprites and collider2d top down games.
To use it in your project:
1. Copy repo into your Asset folder (or install as a package).
2. Create Empty Object in scene root.
3. Add "Navigation Surface" component to Empty Object and add NavMeshCollecSources2d component after.
4. Click Rotate Surface to XY (to face surface toward standard 2d camera x-90;y0;z0)
5. Add "Navigation Modifier" component to scene objects obstacles, override the area.
6. In "Navigation Surface" hit Bake.
How does it works:
1. It uses [NavMeshSurface](https://docs.unity3d.com/Manual/class-NavMeshSurface.html) as base implementation.
2. Implements world bound calculation.
3. Implements source collector of tiles, sprites and 2d colliders
4. Creates walkable mesh box from world bounds.
5. Convert tiles, sprites and 2d colliders to sources as `NavMeshBuilder` would do.
# Setup
You can use this in two different ways: downloading this repository or adding it to your project's Package Manager manifest. [Git](https://git-scm.com/) must be installed and added to your path.
Alternatively, you can pick scripts and place in your project's `Assets` folder.
#### Method 1. Download
Download or clone this repository into your project in the folder `Packages/com.h8man.2d.navmeshplus`.
#### Method 2. Package Manager Manifest
The following line needs to be added to your `Packages/manifest.json` file in your Unity Project under the `dependencies` section:
```
"com.h8man.2d.navmeshplus": "https://github.com/h8man/NavMeshPlus.git#master"
```
#### Method 3. Add Package form git URL
Go to `Package Manager` click `+` and then select `Add Package form git URL` paste url:
```
https://github.com/h8man/NavMeshPlus.git
```

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2f77fe52aa3396942bf2eecff964b4d0
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2d085361c3a06be448c4127d3d5e22f4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
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.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4919e823dd64ccf498a5a5e4da60a79e
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,60 @@
> Please use the branch matching the version of your Unity editor: [master](../../tree/master) for the latest released version, [2018.3](../../tree/2018.3), [2018.2](../../tree/2018.2), [2018.1](../../tree/2018.1), [2017.2](../../tree/2017.2) for up to 2017.4-LTS, [2017.1](../../tree/2017.1), [5.6](../../tree/5.6).
# Components for Runtime NavMesh Building
Here we introduce four components for the navigation system:
* __NavMeshSurface__ for building and enabling a NavMesh surface for one agent type.
* __NavMeshModifier__ affects the NavMesh generation of NavMesh area types, based on the transform hierarchy.
* __NavMeshModifierVolume__ affects the NavMesh generation of NavMesh area types, based on volume.
* __NavMeshLink__ connects same or different NavMesh surfaces for one agent type.
These components comprise the high level controls for building and using NavMeshes at runtime as well as edit time.
Detailed information can be found in the [Documentation](Documentation) section or in the [NavMesh building components](https://docs.unity3d.com/Manual/NavMesh-BuildingComponents.html) section of the Unity Manual.
# How To Get Started
Download and install Unity 5.6 or newer.
Clone or download this repository and open the project in Unity.
Alternatively, you can copy the contents of `Assets/NavMeshComponents` to an existing project. Make sure to select a branch of the repository that matches the Unity version.
Additional examples are available in the `Assets/Examples` folder.
The examples are provided "as is". They are neither generic nor robust, but serve as inspiration.
_Note: During the beta cycle features and API are subject to change.\
**Make sure to backup an existing project before opening it with a beta build.**_
# FAQ
Q: Can I bake a NavMesh at runtime?
A: Yes.
Q: Can I use NavMesh'es for more than one agent size?
A: Yes.
Q: Can I put a NavMesh in a prefab?
A: Yes - with some limitations.
Q: How do I connect two NavMesh surfaces?
A: Use the NavMeshLink to connect the two sides.
Q: How do I query the NavMesh for one specific size of agent?
A: Use the NavMeshQuery filter when querying the NavMesh.
Q: What's the deal with the 'DefaultExecutionOrder' attribute?
A: It gives a way of controlling the order of execution of scripts - specifically it allows us to build a NavMesh before the
(native) NavMeshAgent component is enabled.
Q: What's the use of the new delegate 'NavMesh.onPreUpdate'?
A: It allows you to hook in to controlling the NavMesh data and links set up before the navigation update loop is called on the native side.
Q: Can I do moving NavMesh platforms?
A: No - new API is required for consistently moving platforms carrying agents.
Q: Is OffMeshLink now obsolete?
A: No - you can still use OffMeshLink - however you'll find that NavMeshLink is more flexible and have less overhead.
Q: What happened to HeightMesh and Auto Generated OffMeshLinks?
A: They're not supported in the new NavMesh building feature. HeightMesh will be added at some point. Auto OffMeshLink generation will possibly be replaced with a solution that allows better control of placement.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 32745b071c7e5004f946a98ebaf2fc15
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More