From 5e7b98aedf3a32c697b150179d5baf0d93ca00ed Mon Sep 17 00:00:00 2001 From: Dominykas Kiauleikis Date: Wed, 31 Jul 2019 16:40:45 +0300 Subject: [PATCH] Initial commit of scripts used in Hackweek to support NavMesh for 2D --- .../Editor/NavMeshBuilder2DEditor.cs | 18 +++ .../Editor/NavMeshBuilder2DEditor.cs.meta | 11 ++ .../Scripts/NavMeshBuilder2D.cs | 100 ++++++++++++++++ .../Scripts/NavMeshBuilder2D.cs.meta | 11 ++ .../Scripts/NavMeshSourceTag2D.cs | 109 ++++++++++++++++++ .../Scripts/NavMeshSourceTag2D.cs.meta | 12 ++ 6 files changed, 261 insertions(+) create mode 100644 Assets/NavMeshComponents/Editor/NavMeshBuilder2DEditor.cs create mode 100644 Assets/NavMeshComponents/Editor/NavMeshBuilder2DEditor.cs.meta create mode 100644 Assets/NavMeshComponents/Scripts/NavMeshBuilder2D.cs create mode 100644 Assets/NavMeshComponents/Scripts/NavMeshBuilder2D.cs.meta create mode 100644 Assets/NavMeshComponents/Scripts/NavMeshSourceTag2D.cs create mode 100644 Assets/NavMeshComponents/Scripts/NavMeshSourceTag2D.cs.meta diff --git a/Assets/NavMeshComponents/Editor/NavMeshBuilder2DEditor.cs b/Assets/NavMeshComponents/Editor/NavMeshBuilder2DEditor.cs new file mode 100644 index 00000000..85f3aaa7 --- /dev/null +++ b/Assets/NavMeshComponents/Editor/NavMeshBuilder2DEditor.cs @@ -0,0 +1,18 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; + +[CustomEditor(typeof(NavMeshBuilder2D))] +public class NavMeshBuilder2DEditor : Editor +{ + public override void OnInspectorGUI() + { + DrawDefaultInspector(); + if (GUILayout.Button("Bake")) + { + (target as NavMeshBuilder2D).RebuildNavmesh(false); + SceneView.RepaintAll(); + } + } +} diff --git a/Assets/NavMeshComponents/Editor/NavMeshBuilder2DEditor.cs.meta b/Assets/NavMeshComponents/Editor/NavMeshBuilder2DEditor.cs.meta new file mode 100644 index 00000000..1cbe2db9 --- /dev/null +++ b/Assets/NavMeshComponents/Editor/NavMeshBuilder2DEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b88fc62e059addf49b7574d4aa295f9d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/NavMeshComponents/Scripts/NavMeshBuilder2D.cs b/Assets/NavMeshComponents/Scripts/NavMeshBuilder2D.cs new file mode 100644 index 00000000..f3cda248 --- /dev/null +++ b/Assets/NavMeshComponents/Scripts/NavMeshBuilder2D.cs @@ -0,0 +1,100 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.AI; + +[ExecuteAlways] +public class NavMeshBuilder2D : MonoBehaviour +{ + public enum UpdateMethod + { + Manual, + Update, + FixedUpdate, + } + + public bool bakeOnEnable; + public UpdateMethod updateMethod; + public bool updateAsync; + + Collider2D[] collider2Ds; + + List buildSources = new List(); + NavMeshData data; + NavMeshDataInstance dataInstance; + + static readonly Quaternion Plane2DRotation = Quaternion.AngleAxis(-90f, Vector3.right); + static readonly Quaternion Plane2DRotationInverse = Quaternion.Inverse(Plane2DRotation); + + AsyncOperation buildOperation; + + private void OnEnable() + { + if (bakeOnEnable) + RebuildNavmesh(false); + } + + private void OnDisable() + { + buildOperation = null; + dataInstance.Remove(); + } + + private void Update() + { + var shouldUpdate = updateMethod == UpdateMethod.Update; +#if UNITY_EDITOR + shouldUpdate |= (updateMethod == UpdateMethod.FixedUpdate) && !Application.isPlaying; +#endif + if (shouldUpdate) + RebuildNavmesh(updateAsync); + } + + private void FixedUpdate() + { + if (updateMethod == UpdateMethod.FixedUpdate) + RebuildNavmesh(updateAsync); + } + + public void RebuildNavmesh(bool async) + { + if (async && buildOperation != null && !buildOperation.isDone) + return; + + buildOperation = null; + + var totalBounds = new Bounds(); + NavMeshSourceTag2D.Collect(ref buildSources, ref totalBounds); + var buildSettings = NavMesh.GetSettingsByID(0); + var totalBoundsReversed = new Bounds(Plane2DRotationInverse * totalBounds.center, Plane2DRotationInverse * totalBounds.size); // We need to reverse the rotation that's going to be used for baking to get proper bounds + var buildBounds = new Bounds(Vector3.zero, Vector3.one * float.MaxValue); //Using enclosing and empty bounds is bugged at the moment - use arbitrarily big one + if (!data) + { + data = NavMeshBuilder.BuildNavMeshData(buildSettings, buildSources, buildBounds, totalBounds.center, Plane2DRotation); + } + else + { + if (async) + { + buildOperation = NavMeshBuilder.UpdateNavMeshDataAsync(data, buildSettings, buildSources, buildBounds); + } + else + { + NavMeshBuilder.UpdateNavMeshData(data, buildSettings, buildSources, buildBounds); + } + } + + dataInstance.Remove(); + dataInstance = NavMesh.AddNavMeshData(data); + } + + private void OnDrawGizmosSelected() + { + if (!data) + return; + + Gizmos.color = Color.cyan; + Gizmos.matrix = Matrix4x4.Rotate(data.rotation); + Gizmos.DrawWireCube(data.sourceBounds.center, data.sourceBounds.size); + } +} diff --git a/Assets/NavMeshComponents/Scripts/NavMeshBuilder2D.cs.meta b/Assets/NavMeshComponents/Scripts/NavMeshBuilder2D.cs.meta new file mode 100644 index 00000000..e15a9664 --- /dev/null +++ b/Assets/NavMeshComponents/Scripts/NavMeshBuilder2D.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b8360b9b599435e42b3c6672303dcc87 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/NavMeshComponents/Scripts/NavMeshSourceTag2D.cs b/Assets/NavMeshComponents/Scripts/NavMeshSourceTag2D.cs new file mode 100644 index 00000000..7f278b5c --- /dev/null +++ b/Assets/NavMeshComponents/Scripts/NavMeshSourceTag2D.cs @@ -0,0 +1,109 @@ +using UnityEngine; +using UnityEngine.AI; +using System.Collections.Generic; + +[DefaultExecutionOrder(-200)] +[ExecuteAlways] +public class NavMeshSourceTag2D : MonoBehaviour +{ + public static List tags = new List(); + + public int area; + Collider2D collider2D; + + Mesh cachedMesh; + uint shapeHash; + + void OnEnable() + { + collider2D = GetComponent(); + tags.Add(this); + } + + void OnDisable() + { + tags.Remove(this); + DestroyMesh(); + } + + void UpdateCachedMesh() + { + if (collider2D == null) + { + DestroyMesh(); + return; + } + if (cachedMesh == null) + { + CreateMesh(); + return; + } + if (collider2D.GetShapeHash() != shapeHash) + { + DestroyMesh(); + CreateMesh(); + } + } + + void CreateMesh() + { + cachedMesh = collider2D.CreateMesh(false, false); + shapeHash = collider2D.GetShapeHash(); + } + + void DestroyMesh() + { + if (cachedMesh == null) + return; + + if (Application.isPlaying) + { + Destroy(cachedMesh); + } + else + { + DestroyImmediate(cachedMesh); + } + shapeHash = 0; + } + + // Collect all the navmesh build sources for enabled objects tagged by this component + public static void Collect(ref List sources, ref Bounds bounds) + { + sources.Clear(); + for (var i = 0; i < tags.Count; ++i) + { + var tag = tags[i]; + if (tag == null) continue; + + var collider2D = tag.collider2D; + if (collider2D == null) continue; + if (!collider2D.enabled) continue; + + tag.UpdateCachedMesh(); + + if (tag.cachedMesh == null) + continue; + + var colliderBounds = collider2D.bounds; + bounds.Encapsulate(colliderBounds.min); + bounds.Encapsulate(colliderBounds.max); + //bounds.Encapsulate(colliderBounds); + + var buildSource = new NavMeshBuildSource(); + buildSource.shape = NavMeshBuildSourceShape.Mesh; + buildSource.sourceObject = tag.cachedMesh; + if (collider2D.attachedRigidbody) + { + buildSource.transform = Matrix4x4.TRS(collider2D.transform.position, collider2D.transform.rotation, Vector3.one); + } + else + { + buildSource.transform = Matrix4x4.identity; + } + buildSource.area = tag.area; + sources.Add(buildSource); + + } + } +} diff --git a/Assets/NavMeshComponents/Scripts/NavMeshSourceTag2D.cs.meta b/Assets/NavMeshComponents/Scripts/NavMeshSourceTag2D.cs.meta new file mode 100644 index 00000000..7ea795da --- /dev/null +++ b/Assets/NavMeshComponents/Scripts/NavMeshSourceTag2D.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 363584b0cb85c9e4181e6d1f1ee550ca +timeCreated: 1430814538 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: