적 움직임, 부서지는 블럭 구현
This commit is contained in:
parent
5edef59289
commit
8de1300ffb
8
Assets/Test.meta
Normal file
8
Assets/Test.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8f509c661d925d54b8ca86330a6c49c6
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Test/Block.meta
Normal file
8
Assets/Test/Block.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: faee6310b0465cd4fbacf8261626b10d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
168
Assets/Test/Block/Block (1).prefab
Normal file
168
Assets/Test/Block/Block (1).prefab
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &8470849048189565944
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 3834917339563485677}
|
||||||
|
- component: {fileID: 479445809600363259}
|
||||||
|
- component: {fileID: 7594686203576484264}
|
||||||
|
- component: {fileID: -2736921013582079797}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: Block (1)
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &3834917339563485677
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8470849048189565944}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0.5, y: 0.5, z: 0}
|
||||||
|
m_LocalScale: {x: 0.2, y: 0.2, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!212 &479445809600363259
|
||||||
|
SpriteRenderer:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8470849048189565944}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 0
|
||||||
|
m_ReceiveShadows: 0
|
||||||
|
m_DynamicOccludee: 1
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 1
|
||||||
|
m_LightProbeUsage: 1
|
||||||
|
m_ReflectionProbeUsage: 1
|
||||||
|
m_RayTracingMode: 0
|
||||||
|
m_RayTraceProcedural: 0
|
||||||
|
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||||
|
m_RayTracingAccelStructBuildFlags: 1
|
||||||
|
m_SmallMeshCulling: 1
|
||||||
|
m_ForceMeshLod: -1
|
||||||
|
m_MeshLodSelectionBias: 0
|
||||||
|
m_RenderingLayerMask: 1
|
||||||
|
m_RendererPriority: 0
|
||||||
|
m_Materials:
|
||||||
|
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_StaticBatchInfo:
|
||||||
|
firstSubMesh: 0
|
||||||
|
subMeshCount: 0
|
||||||
|
m_StaticBatchRoot: {fileID: 0}
|
||||||
|
m_ProbeAnchor: {fileID: 0}
|
||||||
|
m_LightProbeVolumeOverride: {fileID: 0}
|
||||||
|
m_ScaleInLightmap: 1
|
||||||
|
m_ReceiveGI: 1
|
||||||
|
m_PreserveUVs: 0
|
||||||
|
m_IgnoreNormalsForChartDetection: 0
|
||||||
|
m_ImportantGI: 0
|
||||||
|
m_StitchLightmapSeams: 1
|
||||||
|
m_SelectedEditorRenderState: 0
|
||||||
|
m_MinimumChartSize: 4
|
||||||
|
m_AutoUVMaxDistance: 0.5
|
||||||
|
m_AutoUVMaxAngle: 89
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_GlobalIlluminationMeshLod: 0
|
||||||
|
m_SortingLayerID: 0
|
||||||
|
m_SortingLayer: 0
|
||||||
|
m_SortingOrder: 1
|
||||||
|
m_MaskInteraction: 0
|
||||||
|
m_Sprite: {fileID: 21300096, guid: 1c9ed9d1200c29d4cbfd2071028c1cd5, type: 3}
|
||||||
|
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
m_FlipX: 0
|
||||||
|
m_FlipY: 0
|
||||||
|
m_DrawMode: 0
|
||||||
|
m_Size: {x: 1, y: 1}
|
||||||
|
m_AdaptiveModeThreshold: 0.5
|
||||||
|
m_SpriteTileMode: 0
|
||||||
|
m_WasSpriteAssigned: 1
|
||||||
|
m_SpriteSortPoint: 0
|
||||||
|
--- !u!50 &7594686203576484264
|
||||||
|
Rigidbody2D:
|
||||||
|
serializedVersion: 5
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8470849048189565944}
|
||||||
|
m_BodyType: 0
|
||||||
|
m_Simulated: 1
|
||||||
|
m_UseFullKinematicContacts: 0
|
||||||
|
m_UseAutoMass: 0
|
||||||
|
m_Mass: 1
|
||||||
|
m_LinearDamping: 0
|
||||||
|
m_AngularDamping: 0.05
|
||||||
|
m_GravityScale: 1
|
||||||
|
m_Material: {fileID: 0}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_Interpolate: 0
|
||||||
|
m_SleepingMode: 1
|
||||||
|
m_CollisionDetection: 0
|
||||||
|
m_Constraints: 0
|
||||||
|
--- !u!61 &-2736921013582079797
|
||||||
|
BoxCollider2D:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8470849048189565944}
|
||||||
|
m_Enabled: 1
|
||||||
|
serializedVersion: 3
|
||||||
|
m_Density: 1
|
||||||
|
m_Material: {fileID: 0}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_LayerOverridePriority: 0
|
||||||
|
m_ForceSendLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_ForceReceiveLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_ContactCaptureLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_CallbackLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_IsTrigger: 1
|
||||||
|
m_UsedByEffector: 0
|
||||||
|
m_CompositeOperation: 0
|
||||||
|
m_CompositeOrder: 0
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
m_SpriteTilingProperty:
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
pivot: {x: 0.5, y: 0.5}
|
||||||
|
oldSize: {x: 1, y: 1}
|
||||||
|
newSize: {x: 1, y: 1}
|
||||||
|
adaptiveTilingThreshold: 0.5
|
||||||
|
drawMode: 0
|
||||||
|
adaptiveTiling: 0
|
||||||
|
m_AutoTiling: 0
|
||||||
|
m_Size: {x: 1, y: 1}
|
||||||
|
m_EdgeRadius: 0
|
||||||
7
Assets/Test/Block/Block (1).prefab.meta
Normal file
7
Assets/Test/Block/Block (1).prefab.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 07676f3c42557424c82b5586340089b9
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
185
Assets/Test/Block/Block.prefab
Normal file
185
Assets/Test/Block/Block.prefab
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &264733977300749662
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 6507705106265860797}
|
||||||
|
- component: {fileID: 9116079573936010913}
|
||||||
|
- component: {fileID: 2323667017725586191}
|
||||||
|
- component: {fileID: 8967009380059366119}
|
||||||
|
- component: {fileID: 7290293607820690937}
|
||||||
|
m_Layer: 6
|
||||||
|
m_Name: Block
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &6507705106265860797
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 264733977300749662}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0.5, y: 0.5, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!212 &9116079573936010913
|
||||||
|
SpriteRenderer:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 264733977300749662}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 0
|
||||||
|
m_ReceiveShadows: 0
|
||||||
|
m_DynamicOccludee: 1
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 1
|
||||||
|
m_LightProbeUsage: 1
|
||||||
|
m_ReflectionProbeUsage: 1
|
||||||
|
m_RayTracingMode: 0
|
||||||
|
m_RayTraceProcedural: 0
|
||||||
|
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||||
|
m_RayTracingAccelStructBuildFlags: 1
|
||||||
|
m_SmallMeshCulling: 1
|
||||||
|
m_ForceMeshLod: -1
|
||||||
|
m_MeshLodSelectionBias: 0
|
||||||
|
m_RenderingLayerMask: 1
|
||||||
|
m_RendererPriority: 0
|
||||||
|
m_Materials:
|
||||||
|
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_StaticBatchInfo:
|
||||||
|
firstSubMesh: 0
|
||||||
|
subMeshCount: 0
|
||||||
|
m_StaticBatchRoot: {fileID: 0}
|
||||||
|
m_ProbeAnchor: {fileID: 0}
|
||||||
|
m_LightProbeVolumeOverride: {fileID: 0}
|
||||||
|
m_ScaleInLightmap: 1
|
||||||
|
m_ReceiveGI: 1
|
||||||
|
m_PreserveUVs: 0
|
||||||
|
m_IgnoreNormalsForChartDetection: 0
|
||||||
|
m_ImportantGI: 0
|
||||||
|
m_StitchLightmapSeams: 1
|
||||||
|
m_SelectedEditorRenderState: 0
|
||||||
|
m_MinimumChartSize: 4
|
||||||
|
m_AutoUVMaxDistance: 0.5
|
||||||
|
m_AutoUVMaxAngle: 89
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_GlobalIlluminationMeshLod: 0
|
||||||
|
m_SortingLayerID: 0
|
||||||
|
m_SortingLayer: 0
|
||||||
|
m_SortingOrder: 0
|
||||||
|
m_MaskInteraction: 0
|
||||||
|
m_Sprite: {fileID: 21300064, guid: 1c9ed9d1200c29d4cbfd2071028c1cd5, type: 3}
|
||||||
|
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
m_FlipX: 0
|
||||||
|
m_FlipY: 0
|
||||||
|
m_DrawMode: 0
|
||||||
|
m_Size: {x: 1, y: 1}
|
||||||
|
m_AdaptiveModeThreshold: 0.5
|
||||||
|
m_SpriteTileMode: 0
|
||||||
|
m_WasSpriteAssigned: 1
|
||||||
|
m_SpriteSortPoint: 0
|
||||||
|
--- !u!50 &2323667017725586191
|
||||||
|
Rigidbody2D:
|
||||||
|
serializedVersion: 5
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 264733977300749662}
|
||||||
|
m_BodyType: 1
|
||||||
|
m_Simulated: 1
|
||||||
|
m_UseFullKinematicContacts: 0
|
||||||
|
m_UseAutoMass: 0
|
||||||
|
m_Mass: 1
|
||||||
|
m_LinearDamping: 0
|
||||||
|
m_AngularDamping: 0.05
|
||||||
|
m_GravityScale: 0
|
||||||
|
m_Material: {fileID: 0}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_Interpolate: 0
|
||||||
|
m_SleepingMode: 1
|
||||||
|
m_CollisionDetection: 0
|
||||||
|
m_Constraints: 0
|
||||||
|
--- !u!114 &8967009380059366119
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 264733977300749662}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: e4c868965068b26438573e4d06679291, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::DestructibleBlock
|
||||||
|
debrisPrefabs:
|
||||||
|
- {fileID: 8470849048189565944, guid: 07676f3c42557424c82b5586340089b9, type: 3}
|
||||||
|
debrisCount: 3
|
||||||
|
explosionForce: 1
|
||||||
|
--- !u!61 &7290293607820690937
|
||||||
|
BoxCollider2D:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 264733977300749662}
|
||||||
|
m_Enabled: 1
|
||||||
|
serializedVersion: 3
|
||||||
|
m_Density: 1
|
||||||
|
m_Material: {fileID: 0}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_LayerOverridePriority: 0
|
||||||
|
m_ForceSendLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_ForceReceiveLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_ContactCaptureLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_CallbackLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_IsTrigger: 1
|
||||||
|
m_UsedByEffector: 0
|
||||||
|
m_CompositeOperation: 0
|
||||||
|
m_CompositeOrder: 0
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
m_SpriteTilingProperty:
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
pivot: {x: 0.5, y: 0.5}
|
||||||
|
oldSize: {x: 1, y: 1}
|
||||||
|
newSize: {x: 1, y: 1}
|
||||||
|
adaptiveTilingThreshold: 0.5
|
||||||
|
drawMode: 0
|
||||||
|
adaptiveTiling: 0
|
||||||
|
m_AutoTiling: 0
|
||||||
|
m_Size: {x: 1, y: 1}
|
||||||
|
m_EdgeRadius: 0
|
||||||
7
Assets/Test/Block/Block.prefab.meta
Normal file
7
Assets/Test/Block/Block.prefab.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9f6bbfc37f93a0e4e8266ff424f3624f
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
48
Assets/Test/Block/BlockMapGenerator.cs
Normal file
48
Assets/Test/Block/BlockMapGenerator.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class BlockMapGenerator : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("생성 설정")]
|
||||||
|
[SerializeField]
|
||||||
|
private GameObject blockPrefab; // 위에서 만든 DestructibleBlock 프리팹
|
||||||
|
[SerializeField]
|
||||||
|
private int width = 20; // 맵 가로 크기
|
||||||
|
[SerializeField]
|
||||||
|
private int height = 10; // 맵 세로 크기
|
||||||
|
[SerializeField]
|
||||||
|
private float spacing = 1.0f; // 블록 간격 (스프라이트 크기에 맞춰 조절)
|
||||||
|
|
||||||
|
[Header("랜덤 확률 (0 ~ 1)")]
|
||||||
|
[Range(0, 1)]
|
||||||
|
[SerializeField]
|
||||||
|
private float fillPercent = 0.5f; // 50% 확률로 블록 생성
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
GenerateMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateMap()
|
||||||
|
{
|
||||||
|
// 맵의 중앙을 (0,0)에 맞추기 위한 시작 위치 계산
|
||||||
|
Vector2 startPos = new Vector2(-width / 2f * spacing, -height / 2f * spacing);
|
||||||
|
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
// 플레이어 시작 위치(중앙)는 비워두기
|
||||||
|
if (x == width / 2 && y == height / 2)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Random.value < fillPercent)
|
||||||
|
{
|
||||||
|
Vector2 spawnPos = startPos + new Vector2(x * spacing, y * spacing);
|
||||||
|
Instantiate(blockPrefab, spawnPos, Quaternion.identity, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Test/Block/BlockMapGenerator.cs.meta
Normal file
2
Assets/Test/Block/BlockMapGenerator.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f24b056cab559e342b0e354bd8bcb0a2
|
||||||
52
Assets/Test/Block/DestructibleBlock.cs
Normal file
52
Assets/Test/Block/DestructibleBlock.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class DestructibleBlock : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("파편 효과 설정")]
|
||||||
|
[SerializeField]
|
||||||
|
private GameObject[] debrisPrefabs;
|
||||||
|
[SerializeField]
|
||||||
|
private int debrisCount = 5;
|
||||||
|
[SerializeField]
|
||||||
|
private float explosionForce = 5f;
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
if (!gameObject.scene.isLoaded)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnDebris();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SpawnDebris()
|
||||||
|
{
|
||||||
|
if (debrisPrefabs == null || debrisPrefabs.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < debrisCount; i++)
|
||||||
|
{
|
||||||
|
// 랜덤한 파편 선택
|
||||||
|
GameObject prefab = debrisPrefabs[Random.Range(0, debrisPrefabs.Length)];
|
||||||
|
|
||||||
|
// 블록 위치 근처에서 생성
|
||||||
|
Vector2 randomPos = (Vector2)transform.position + Random.insideUnitCircle * 0.5f;
|
||||||
|
GameObject debris = Instantiate(prefab, randomPos, Quaternion.identity);
|
||||||
|
|
||||||
|
// 펑! 하고 튕겨나가는 힘 적용
|
||||||
|
Rigidbody2D rb = debris.GetComponent<Rigidbody2D>();
|
||||||
|
if (rb != null)
|
||||||
|
{
|
||||||
|
Vector2 dir = (randomPos - (Vector2)transform.position).normalized;
|
||||||
|
dir += Random.insideUnitCircle * 0.5f; // 방향 랜덤성 추가
|
||||||
|
rb.AddForce(dir.normalized * explosionForce, ForceMode2D.Impulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 파편은 2초 뒤에 스스로 사라짐
|
||||||
|
Destroy(debris, 2f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Test/Block/DestructibleBlock.cs.meta
Normal file
2
Assets/Test/Block/DestructibleBlock.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e4c868965068b26438573e4d06679291
|
||||||
16
Assets/Test/Block/NewMonoBehaviourScript.cs
Normal file
16
Assets/Test/Block/NewMonoBehaviourScript.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class NewMonoBehaviourScript : MonoBehaviour
|
||||||
|
{
|
||||||
|
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is called once per frame
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Test/Block/NewMonoBehaviourScript.cs.meta
Normal file
2
Assets/Test/Block/NewMonoBehaviourScript.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 241e239a7ad339446be26b523a6b9981
|
||||||
52
Assets/Test/Block/TileClickBreaker.cs
Normal file
52
Assets/Test/Block/TileClickBreaker.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Tilemaps;
|
||||||
|
using UnityEngine.InputSystem;
|
||||||
|
|
||||||
|
public class TileClickBreaker : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField]
|
||||||
|
private Tilemap tilemap;
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (Mouse.current == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Mouse.current.leftButton.wasPressedThisFrame)
|
||||||
|
{
|
||||||
|
Vector2 mousePos = Mouse.current.position.ReadValue();
|
||||||
|
|
||||||
|
// 2D는 카메라 좌표 변환 방식이 다릅니다
|
||||||
|
Vector2 worldPos = Camera.main.ScreenToWorldPoint(mousePos);
|
||||||
|
|
||||||
|
// 2D 전용 레이캐스트 사용
|
||||||
|
RaycastHit2D hit = Physics2D.Raycast(worldPos, Vector2.zero);
|
||||||
|
|
||||||
|
if (hit.collider != null)
|
||||||
|
{
|
||||||
|
Destroy(hit.transform.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BreakTileAtMousePosition()
|
||||||
|
{
|
||||||
|
// 마우스가 연결되어 있는지 확인
|
||||||
|
if (Mouse.current == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 새로운 방식의 마우스 위치 가져오기
|
||||||
|
Vector3 mousePos = Mouse.current.position.ReadValue();
|
||||||
|
Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(mousePos);
|
||||||
|
Vector3Int cellPosition = tilemap.WorldToCell(mouseWorldPos);
|
||||||
|
|
||||||
|
if (tilemap.HasTile(cellPosition))
|
||||||
|
{
|
||||||
|
tilemap.SetTile(cellPosition, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Test/Block/TileClickBreaker.cs.meta
Normal file
2
Assets/Test/Block/TileClickBreaker.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b79fc067910801e479bd1d49686c8981
|
||||||
8
Assets/Test/Monster.meta
Normal file
8
Assets/Test/Monster.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1f477f6b90a7bde409fd49711c61b0d6
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
71
Assets/Test/Monster/FlyEnemyMovement.cs
Normal file
71
Assets/Test/Monster/FlyEnemyMovement.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.AI;
|
||||||
|
|
||||||
|
public class FlyEnemyMovement : Entity
|
||||||
|
{
|
||||||
|
private GameObject playerObject;
|
||||||
|
private NavMeshAgent agent;
|
||||||
|
[SerializeField]
|
||||||
|
private float detectionRange = 5.0f; // 감지 범위 변수
|
||||||
|
[SerializeField]
|
||||||
|
private float EnemyTime;
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
agent = GetComponent<NavMeshAgent>();
|
||||||
|
|
||||||
|
// NavMeshPlus 2D 설정
|
||||||
|
agent.updateRotation = false;
|
||||||
|
agent.updateUpAxis = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
playerObject = GameObject.FindWithTag("Player");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (playerObject != null)
|
||||||
|
{
|
||||||
|
// 1. 현재 위치와 플레이어 위치 사이의 거리를 계산
|
||||||
|
float distance = Vector3.Distance(transform.position, playerObject.transform.position);
|
||||||
|
|
||||||
|
// 2. 거리가 감지 범위 확인
|
||||||
|
if (distance <= detectionRange)
|
||||||
|
{
|
||||||
|
SetAgentPosition(); // 추격 시작
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// 범위 밖이라면 이동을 멈춤
|
||||||
|
if (!agent.isStopped)
|
||||||
|
{
|
||||||
|
agent.ResetPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetAgentPosition()
|
||||||
|
{
|
||||||
|
if (!agent.isActiveAndEnabled || !agent.isOnNavMesh)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 targetPos = playerObject.transform.position;
|
||||||
|
agent.SetDestination(new Vector3(targetPos.x, targetPos.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTriggerEnter2D(Collider2D other)
|
||||||
|
{
|
||||||
|
if (other.CompareTag("Player"))
|
||||||
|
{
|
||||||
|
Die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void OnDrawGizmosSelected()
|
||||||
|
{
|
||||||
|
Gizmos.color = Color.red;
|
||||||
|
Gizmos.DrawWireSphere(transform.position, detectionRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Test/Monster/FlyEnemyMovement.cs.meta
Normal file
2
Assets/Test/Monster/FlyEnemyMovement.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: acacf576fd681b94087260c82b6f530c
|
||||||
77
Assets/Test/Monster/GroundEnemyMovement.cs
Normal file
77
Assets/Test/Monster/GroundEnemyMovement.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
public class GroundEnemyMovement : Entity
|
||||||
|
{
|
||||||
|
[SerializeField]
|
||||||
|
private Rigidbody2D rigidBody;
|
||||||
|
[SerializeField]
|
||||||
|
private SpriteRenderer spriteRenderer;
|
||||||
|
[SerializeField]
|
||||||
|
private float Speed = 3f;
|
||||||
|
[SerializeField]
|
||||||
|
private float startDirection = 1.5f;
|
||||||
|
|
||||||
|
private float currentDirection;
|
||||||
|
private float halfWidth;
|
||||||
|
private Vector2 movemenet;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
halfWidth = spriteRenderer.bounds.extents.x;
|
||||||
|
currentDirection = startDirection > 0 ? 1f : -1f;
|
||||||
|
spriteRenderer.flipX = currentDirection == 1 ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedUpdate()
|
||||||
|
{
|
||||||
|
movemenet.x = Speed * currentDirection;
|
||||||
|
movemenet.y = rigidBody.linearVelocity.y;
|
||||||
|
rigidBody.linearVelocity = movemenet;
|
||||||
|
|
||||||
|
SetDirection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetDirection()
|
||||||
|
{
|
||||||
|
Vector2 frontVec = transform.position;
|
||||||
|
Vector2 wallDir = Vector2.right;
|
||||||
|
|
||||||
|
if (currentDirection > 0)
|
||||||
|
{
|
||||||
|
frontVec += Vector2.right * halfWidth;
|
||||||
|
wallDir = Vector2.right;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
frontVec += Vector2.left * halfWidth;
|
||||||
|
wallDir = Vector2.left;
|
||||||
|
}
|
||||||
|
|
||||||
|
RaycastHit2D cliffHit = Physics2D.Raycast(frontVec, Vector2.down, 1f, LayerMask.GetMask("Ground"));
|
||||||
|
Debug.DrawRay(frontVec, Vector2.down * 1f, Color.green);
|
||||||
|
|
||||||
|
float wallDist = halfWidth + 0.2f;
|
||||||
|
RaycastHit2D wallHit = Physics2D.Raycast(transform.position, wallDir, wallDist, LayerMask.GetMask("Ground"));
|
||||||
|
Debug.DrawRay(transform.position, wallDir * wallDist, Color.blue);
|
||||||
|
|
||||||
|
if (cliffHit.collider == null || wallHit.collider != null)
|
||||||
|
{
|
||||||
|
Turn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Turn()
|
||||||
|
{
|
||||||
|
currentDirection *= -1;
|
||||||
|
spriteRenderer.flipX = !spriteRenderer.flipX;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCollisionEnter2D(Collision2D collision)
|
||||||
|
{
|
||||||
|
// 부딪힌 물체의 태그가 "Player"라면
|
||||||
|
if (collision.gameObject.CompareTag("Player"))
|
||||||
|
{
|
||||||
|
Die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Test/Monster/GroundEnemyMovement.cs.meta
Normal file
2
Assets/Test/Monster/GroundEnemyMovement.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7e036d2115ad53f48ba9c059c6cea1bb
|
||||||
8
Assets/Test/NavMeshComponents.meta
Normal file
8
Assets/Test/NavMeshComponents.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1d279d47cd674a34e86965d94da726be
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Test/NavMeshComponents/Editor.meta
Normal file
8
Assets/Test/NavMeshComponents/Editor.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d5b48dfc36243e144ab116b1e7eb573b
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace NavMeshPlus.Extensions.Editors
|
||||||
|
{
|
||||||
|
[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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 86cc6cf350c6f62469395948494f0945
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace NavMeshPlus.Extensions.Editors
|
||||||
|
{
|
||||||
|
[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 UNITY_6000_0_OR_NEWER
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
foreach (CollectSources2d navSurface in targets)
|
||||||
|
{
|
||||||
|
if (!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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3fc821cbc11a48745bc9edea6bfda007
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace NavMeshPlus.Extensions.Editors
|
||||||
|
{
|
||||||
|
[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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3616009fd1ab770409e0321881372357
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
using NavMeshPlus.Components.Editors;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
//***********************************************************************************
|
||||||
|
// Contributed by author jl-randazzo github.com/jl-randazzo
|
||||||
|
//***********************************************************************************
|
||||||
|
namespace NavMeshPlus.Extensions.Editors
|
||||||
|
{
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1d43a96ce80684449a8b4eee132e5d47
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
using NavMeshPlus.Components.Editors;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
//***********************************************************************************
|
||||||
|
// Contributed by author jl-randazzo github.com/jl-randazzo
|
||||||
|
//***********************************************************************************
|
||||||
|
namespace NavMeshPlus.Extensions.Editors
|
||||||
|
{
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
334
Assets/Test/NavMeshComponents/Editor/NavMeshAssetManager.cs
Normal file
334
Assets/Test/NavMeshComponents/Editor/NavMeshAssetManager.cs
Normal file
|
|
@ -0,0 +1,334 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using UnityEngine.AI;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace NavMeshPlus.Components.Editors
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0d969266144f4fb47be21604dd1e7900
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,276 @@
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.AI;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.AI;
|
||||||
|
|
||||||
|
namespace NavMeshPlus.Components.Editors
|
||||||
|
{
|
||||||
|
public static class NavMeshComponentsGUIUtility
|
||||||
|
{
|
||||||
|
public static void AreaPopup(Rect rect, string labelName, SerializedProperty areaProperty)
|
||||||
|
{
|
||||||
|
var areaIndex = -1;
|
||||||
|
var areaNames = NavMesh.GetAreaNames();
|
||||||
|
for (var i = 0; i < areaNames.Length; i++)
|
||||||
|
{
|
||||||
|
var areaValue = NavMesh.GetAreaFromName(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 = NavMesh.GetAreaFromName(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6d05c56cb29ad5240bc671605f95db0c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
279
Assets/Test/NavMeshComponents/Editor/NavMeshLinkEditor.cs
Normal file
279
Assets/Test/NavMeshComponents/Editor/NavMeshLinkEditor.cs
Normal file
|
|
@ -0,0 +1,279 @@
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NavMeshPlus.Components.Editors
|
||||||
|
{
|
||||||
|
[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;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.InSelectionHierarchy | GizmoType.Active | GizmoType.Pickable)]
|
||||||
|
|
||||||
|
static void RenderBoxGizmo(NavMeshLink navLink, GizmoType gizmoType)
|
||||||
|
{
|
||||||
|
if (!EditorApplication.isPlaying && navLink.isActiveAndEnabled)
|
||||||
|
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 (!EditorApplication.isPlaying && navLink.isActiveAndEnabled)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
||||||
|
public static void CreateNavMeshLink(MenuCommand menuCommand)
|
||||||
|
{
|
||||||
|
var parent = menuCommand.context as GameObject;
|
||||||
|
GameObject go = NavMeshComponentsGUIUtility.CreateAndSelectGameObject("NavMesh Link", parent);
|
||||||
|
go.AddComponent<NavMeshLink>();
|
||||||
|
var view = SceneView.lastActiveSceneView;
|
||||||
|
if (view != null)
|
||||||
|
view.MoveToView(go.transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 03832abc07e3394479eec5708b22e984
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace NavMeshPlus.Components.Editors
|
||||||
|
{
|
||||||
|
[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");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b8fce3c13f011874d92d75ca24a90702
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine.Tilemaps;
|
||||||
|
|
||||||
|
//***********************************************************************************
|
||||||
|
// Contributed by author jl-randazzo github.com/jl-randazzo
|
||||||
|
//***********************************************************************************
|
||||||
|
namespace NavMeshPlus.Components.Editors
|
||||||
|
{
|
||||||
|
[CanEditMultipleObjects]
|
||||||
|
[CustomEditor(typeof(NavMeshModifierTilemap))]
|
||||||
|
class NavMeshModifierTilemapEditor : Editor
|
||||||
|
{
|
||||||
|
SerializedProperty m_AffectedAgents;
|
||||||
|
SerializedProperty m_TileModifiers;
|
||||||
|
|
||||||
|
void OnEnable()
|
||||||
|
{
|
||||||
|
m_AffectedAgents = serializedObject.FindProperty("m_AffectedAgents");
|
||||||
|
m_TileModifiers = serializedObject.FindProperty("m_TileModifiers");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
NavMeshModifierTilemap modifierTilemap = target as NavMeshModifierTilemap;
|
||||||
|
|
||||||
|
serializedObject.Update();
|
||||||
|
|
||||||
|
NavMeshComponentsGUIUtility.AgentMaskPopup("Affected Agents", m_AffectedAgents);
|
||||||
|
|
||||||
|
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 static Dictionary<Object, Texture2D> Previews;
|
||||||
|
|
||||||
|
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();
|
||||||
|
Texture textureToDraw;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tileBase?.GetTileData(Vector3Int.zero, null, ref tileData);
|
||||||
|
textureToDraw = tileData.sprite?.texture;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
textureToDraw = GetPreview(tileBase);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
textureToDraw = EditorGUIUtility.IconContent("console.erroricon.sml").image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textureToDraw)
|
||||||
|
{
|
||||||
|
EditorGUI.DrawPreviewTexture(previewRect, textureToDraw, 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--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Texture2D GetPreview(Object objectToPreview)
|
||||||
|
{
|
||||||
|
int maxResolution = 128;
|
||||||
|
Previews ??= new Dictionary<Object, Texture2D>();
|
||||||
|
if (!Previews.TryGetValue(objectToPreview, out var preview) || preview == null)
|
||||||
|
{
|
||||||
|
var path = AssetDatabase.GetAssetPath(objectToPreview);
|
||||||
|
if (objectToPreview)
|
||||||
|
{
|
||||||
|
var editor = CreateEditor(objectToPreview);
|
||||||
|
preview = editor.RenderStaticPreview(path, null, maxResolution, maxResolution);
|
||||||
|
preview.Apply();
|
||||||
|
DestroyImmediate(editor);
|
||||||
|
Previews[objectToPreview] = preview;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return preview;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1fa3ed80a7c8401995efba10b64226e9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
using UnityEditor.IMGUI.Controls;
|
||||||
|
using UnityEditorInternal;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace NavMeshPlus.Components.Editors
|
||||||
|
{
|
||||||
|
[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"); }
|
||||||
|
|
||||||
|
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.InSelectionHierarchy | 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)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5999826ec8e37f74a80d7f6ee2700a3a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
428
Assets/Test/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs
Normal file
428
Assets/Test/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs
Normal file
|
|
@ -0,0 +1,428 @@
|
||||||
|
#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 System.Reflection;
|
||||||
|
|
||||||
|
namespace NavMeshPlus.Components.Editors
|
||||||
|
{
|
||||||
|
[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;
|
||||||
|
SerializedProperty m_MinRegionArea;
|
||||||
|
SerializedProperty m_HideEditorLogs;
|
||||||
|
|
||||||
|
#if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF
|
||||||
|
SerializedProperty m_NavMeshData;
|
||||||
|
#endif
|
||||||
|
class Styles
|
||||||
|
{
|
||||||
|
public readonly GUIContent m_LayerMask = new GUIContent("Include Layers");
|
||||||
|
public readonly GUIContent m_MinRegionArea = new GUIContent("Minimum Region Area");
|
||||||
|
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");
|
||||||
|
m_MinRegionArea = serializedObject.FindProperty("m_MinRegionArea");
|
||||||
|
m_HideEditorLogs = serializedObject.FindProperty("m_HideEditorLogs");
|
||||||
|
|
||||||
|
#if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF
|
||||||
|
m_NavMeshData = serializedObject.FindProperty("m_NavMeshData");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !UNITY_2022_2_OR_NEWER
|
||||||
|
NavMeshVisualizationSettings.showNavigation++;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !UNITY_2022_2_OR_NEWER
|
||||||
|
void OnDisable()
|
||||||
|
{
|
||||||
|
NavMeshVisualizationSettings.showNavigation--;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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--;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.PropertyField(m_MinRegionArea, s_Styles.m_MinRegionArea);
|
||||||
|
|
||||||
|
// Height mesh
|
||||||
|
using (new EditorGUI.DisabledScope(true))
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(m_BuildHeightMesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.PropertyField(m_HideEditorLogs);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_2022_2_OR_NEWER
|
||||||
|
[DrawGizmo(GizmoType.InSelectionHierarchy | GizmoType.Active | GizmoType.Pickable)]
|
||||||
|
static void RenderGizmoSelected(NavMeshSurface navSurface, GizmoType gizmoType)
|
||||||
|
{
|
||||||
|
//navSurface.navMeshDataInstance.FlagAsInSelectionHierarchy();
|
||||||
|
var method = navSurface.navMeshDataInstance.GetType().GetMethod("FlagAsInSelectionHierarchy", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
method.Invoke(navSurface.navMeshDataInstance, null);
|
||||||
|
RenderBoxGizmo(navSurface, gizmoType, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.Pickable)]
|
||||||
|
static void RenderGizmoNotSelected(NavMeshSurface navSurface, GizmoType gizmoType)
|
||||||
|
{
|
||||||
|
RenderBoxGizmo(navSurface, gizmoType, false);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
[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);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3a7b2ae8284a9b14abd6d688cc877cef
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
36
Assets/Test/NavMeshComponents/Editor/RootSources2dEditor.cs
Normal file
36
Assets/Test/NavMeshComponents/Editor/RootSources2dEditor.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
using UnityEditor;
|
||||||
|
using NavMeshPlus.Components;
|
||||||
|
|
||||||
|
namespace NavMeshPlus.Extensions.Editors
|
||||||
|
{
|
||||||
|
[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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7e3d768af6ee1774cb39815bd8e5b221
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Test/NavMeshComponents/Scripts.meta
Normal file
8
Assets/Test/NavMeshComponents/Scripts.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 641c4284edd67fe44923489f38de3466
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
36
Assets/Test/NavMeshComponents/Scripts/AgentOverride2d.cs
Normal file
36
Assets/Test/NavMeshComponents/Scripts/AgentOverride2d.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
15
Assets/Test/NavMeshComponents/Scripts/AgentRotate2d.cs
Normal file
15
Assets/Test/NavMeshComponents/Scripts/AgentRotate2d.cs
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Test/NavMeshComponents/Scripts/AgentRotate2d.cs.meta
Normal file
11
Assets/Test/NavMeshComponents/Scripts/AgentRotate2d.cs.meta
Normal 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:
|
||||||
16
Assets/Test/NavMeshComponents/Scripts/AgentRotateSmooth2d.cs
Normal file
16
Assets/Test/NavMeshComponents/Scripts/AgentRotateSmooth2d.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
90
Assets/Test/NavMeshComponents/Scripts/CollectSources2d.cs
Normal file
90
Assets/Test/NavMeshComponents/Scripts/CollectSources2d.cs
Normal 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 = Object.FindAnyObjectByType<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
132
Assets/Test/NavMeshComponents/Scripts/CollectSourcesCache2d.cs
Normal file
132
Assets/Test/NavMeshComponents/Scripts/CollectSourcesCache2d.cs
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var res = _lookup.ContainsKey(gameObject);
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
return UpdateSource(gameObject);
|
||||||
|
}
|
||||||
|
NavMeshBuilder2d.CollectSources(gameObject, _sources, _state);
|
||||||
|
|
||||||
|
IsDirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, int? area = null)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
if (area.HasValue)
|
||||||
|
{
|
||||||
|
source.area = area.Value;
|
||||||
|
}
|
||||||
|
_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
using NavMeshPlus.Components;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.AI;
|
||||||
|
using UnityEngine.Tilemaps;
|
||||||
|
|
||||||
|
namespace NavMeshPlus.Extensions
|
||||||
|
{
|
||||||
|
[ExecuteAlways]
|
||||||
|
[AddComponentMenu("Navigation/Navigation CacheTilemapSources2d", 30)]
|
||||||
|
public class CollectTilemapSourcesCache2d : NavMeshExtension
|
||||||
|
{
|
||||||
|
[SerializeField] private Tilemap _tilemap;
|
||||||
|
[SerializeField] private NavMeshModifier _modifier;
|
||||||
|
[SerializeField] private NavMeshModifierTilemap _modifierTilemap;
|
||||||
|
|
||||||
|
private List<NavMeshBuildSource> _sources;
|
||||||
|
private Dictionary<Vector3Int, int> _lookup;
|
||||||
|
private Dictionary<TileBase, NavMeshModifierTilemap.TileModifier> _modifierMap;
|
||||||
|
|
||||||
|
protected override void Awake()
|
||||||
|
{
|
||||||
|
_modifier ??= _tilemap.GetComponent<NavMeshModifier>();
|
||||||
|
_modifierTilemap ??= _tilemap.GetComponent<NavMeshModifierTilemap>();
|
||||||
|
_modifierMap = _modifierTilemap.GetModifierMap();
|
||||||
|
Order = -1000;
|
||||||
|
base.Awake();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR || UNITY_2022_2_OR_NEWER
|
||||||
|
private void OnTilemapTileChanged(Tilemap tilemap, Tilemap.SyncTile[] syncTiles)
|
||||||
|
{
|
||||||
|
if (tilemap == _tilemap)
|
||||||
|
{
|
||||||
|
foreach (Tilemap.SyncTile syncTile in syncTiles)
|
||||||
|
{
|
||||||
|
Vector3Int position = syncTile.position;
|
||||||
|
if (syncTile.tile != null && _modifierMap.TryGetValue(syncTile.tile, out NavMeshModifierTilemap.TileModifier tileModifier))
|
||||||
|
{
|
||||||
|
int i = _lookup[position];
|
||||||
|
NavMeshBuildSource source = _sources[i];
|
||||||
|
source.area = tileModifier.area;
|
||||||
|
_sources[i] = source;
|
||||||
|
}
|
||||||
|
else if (_modifier.overrideArea)
|
||||||
|
{
|
||||||
|
int i = _lookup[position];
|
||||||
|
NavMeshBuildSource source = _sources[i];
|
||||||
|
source.area = _modifier.area;
|
||||||
|
_sources[i] = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
public AsyncOperation UpdateNavMesh(NavMeshData data)
|
||||||
|
{
|
||||||
|
return NavMeshBuilder.UpdateNavMeshDataAsync(data, NavMeshSurfaceOwner.GetBuildSettings(), _sources, data.sourceBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncOperation UpdateNavMesh()
|
||||||
|
{
|
||||||
|
return UpdateNavMesh(NavMeshSurfaceOwner.navMeshData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostCollectSources(NavMeshSurface surface, List<NavMeshBuildSource> sources, NavMeshBuilderState navNeshState)
|
||||||
|
{
|
||||||
|
_sources = sources;
|
||||||
|
if (_lookup == null)
|
||||||
|
{
|
||||||
|
_lookup = new Dictionary<Vector3Int, int>();
|
||||||
|
for (int i = 0; i < _sources.Count; i++)
|
||||||
|
{
|
||||||
|
NavMeshBuildSource source = _sources[i];
|
||||||
|
Vector3Int position = _tilemap.WorldToCell(source.transform.GetPosition());
|
||||||
|
_lookup[position] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if UNITY_EDITOR || UNITY_2022_2_OR_NEWER
|
||||||
|
Tilemap.tilemapTileChanged -= OnTilemapTileChanged;
|
||||||
|
Tilemap.tilemapTileChanged += OnTilemapTileChanged;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDestroy()
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR || UNITY_2022_2_OR_NEWER
|
||||||
|
Tilemap.tilemapTileChanged -= OnTilemapTileChanged;
|
||||||
|
#endif
|
||||||
|
base.OnDestroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4db9c2c5aca397f41a57740bd678fd51
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
//***********************************************************************************
|
||||||
|
// Contributed by author jl-randazzo github.com/jl-randazzo
|
||||||
|
//***********************************************************************************
|
||||||
|
namespace NavMeshPlus.Extensions
|
||||||
|
{
|
||||||
|
[System.Serializable]
|
||||||
|
public class NavMeshAgentAttribute : PropertyAttribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7f3fe2b336bf34749a146f6bf7d462d0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
399
Assets/Test/NavMeshComponents/Scripts/NavMeshBuilder2d.cs
Normal file
399
Assets/Test/NavMeshComponents/Scripts/NavMeshBuilder2d.cs
Normal file
|
|
@ -0,0 +1,399 @@
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// 수정: usedByComposite 대신 compositeOperation 확인
|
||||||
|
if (collider.compositeOperation != Collider2D.CompositeOperation.None)
|
||||||
|
{
|
||||||
|
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>();
|
||||||
|
|
||||||
|
if (modifierTilemap && !modifierTilemap.AffectsAgentType(builder.agentID))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
109
Assets/Test/NavMeshComponents/Scripts/NavMeshBuilderState.cs
Normal file
109
Assets/Test/NavMeshComponents/Scripts/NavMeshBuilderState.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
58
Assets/Test/NavMeshComponents/Scripts/NavMeshExtension.cs
Normal file
58
Assets/Test/NavMeshComponents/Scripts/NavMeshExtension.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
182
Assets/Test/NavMeshComponents/Scripts/NavMeshLink.cs
Normal file
182
Assets/Test/NavMeshComponents/Scripts/NavMeshLink.cs
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
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();
|
||||||
|
// 수정: .valid 대신 NavMesh.IsLinkValid() 사용
|
||||||
|
if (m_AutoUpdatePosition && NavMesh.IsLinkValid(m_LinkInstance))
|
||||||
|
AddTracking(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDisable()
|
||||||
|
{
|
||||||
|
RemoveTracking(this);
|
||||||
|
// 수정: .Remove() 대신 NavMesh.RemoveLink() 사용
|
||||||
|
NavMesh.RemoveLink(m_LinkInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateLink()
|
||||||
|
{
|
||||||
|
// 수정: .Remove() 대신 NavMesh.RemoveLink() 사용
|
||||||
|
NavMesh.RemoveLink(m_LinkInstance);
|
||||||
|
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
|
||||||
|
// 수정: .valid 대신 NavMesh.IsLinkValid() 사용
|
||||||
|
if (NavMesh.IsLinkValid(m_LinkInstance))
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 수정: .valid 및 .owner를 최신 API로 변경
|
||||||
|
if (NavMesh.IsLinkValid(m_LinkInstance))
|
||||||
|
NavMesh.SetLinkOwner(m_LinkInstance, 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);
|
||||||
|
|
||||||
|
// 수정: .valid 대신 NavMesh.IsLinkValid() 사용
|
||||||
|
if (!NavMesh.IsLinkValid(m_LinkInstance))
|
||||||
|
return;
|
||||||
|
|
||||||
|
UpdateLink();
|
||||||
|
|
||||||
|
if (!m_AutoUpdatePosition)
|
||||||
|
{
|
||||||
|
RemoveTracking(this);
|
||||||
|
}
|
||||||
|
else if (!s_Tracked.Contains(this))
|
||||||
|
{
|
||||||
|
AddTracking(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Test/NavMeshComponents/Scripts/NavMeshLink.cs.meta
Normal file
11
Assets/Test/NavMeshComponents/Scripts/NavMeshLink.cs.meta
Normal 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:
|
||||||
56
Assets/Test/NavMeshComponents/Scripts/NavMeshModifier.cs
Normal file
56
Assets/Test/NavMeshComponents/Scripts/NavMeshModifier.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
551
Assets/Test/NavMeshComponents/Scripts/NavMeshSurface.cs
Normal file
551
Assets/Test/NavMeshComponents/Scripts/NavMeshSurface.cs
Normal file
|
|
@ -0,0 +1,551 @@
|
||||||
|
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]
|
||||||
|
float m_MinRegionArea = 0;
|
||||||
|
public float minRegionArea { get { return m_MinRegionArea; } set { m_MinRegionArea = 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;
|
||||||
|
|
||||||
|
public NavMeshDataInstance navMeshDataInstance => m_NavMeshDataInstance;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildSettings.minRegionArea = minRegionArea;
|
||||||
|
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.NavMeshEditorHelpers.CollectSourcesInStage(
|
||||||
|
null, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, gameObject.scene, sources);
|
||||||
|
}
|
||||||
|
else if (m_CollectObjects == CollectObjects.Children)
|
||||||
|
{
|
||||||
|
UnityEditor.AI.NavMeshEditorHelpers.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.NavMeshEditorHelpers.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
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Test/NavMeshComponents/Scripts/NavMeshSurface.cs.meta
Normal file
11
Assets/Test/NavMeshComponents/Scripts/NavMeshSurface.cs.meta
Normal 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:
|
||||||
32
Assets/Test/NavMeshComponents/Scripts/RootSources2d.cs
Normal file
32
Assets/Test/NavMeshComponents/Scripts/RootSources2d.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Test/NavMeshComponents/Scripts/RootSources2d.cs.meta
Normal file
11
Assets/Test/NavMeshComponents/Scripts/RootSources2d.cs.meta
Normal 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:
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
57
Assets/Test/NavMeshComponents/Scripts/RotateAgentSmoothly.cs
Normal file
57
Assets/Test/NavMeshComponents/Scripts/RotateAgentSmoothly.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
Loading…
Reference in New Issue
Block a user