using System; using System.Collections.Generic; using System.Linq; using Unity.AI.Navigation; using UnityEngine; using UnityEngine.AI; using UnityEngine.Tilemaps; namespace DunGen.Adapters { [AddComponentMenu("DunGen/NavMesh/Unity NavMesh Adapter (2D)")] public class UnityNavMesh2DAdapter : NavMeshAdapter { [Serializable] public sealed class NavMeshAgentLinkInfo { public int AgentTypeID; public int AreaTypeID; public bool DisableLinkWhenDoorIsClosed = true; } private static Quaternion rotation = Quaternion.Euler(-90f, 0f, 0f); public bool AddNavMeshLinksBetweenRooms = true; public List NavMeshAgentTypes = new List { new NavMeshAgentLinkInfo() }; public float NavMeshLinkDistanceFromDoorway = 1f; [SerializeField] private int agentTypeID; [SerializeField] private bool overrideTileSize; [SerializeField] private int tileSize = 256; [SerializeField] private bool overrideVoxelSize; [SerializeField] private float voxelSize; [SerializeField] private NavMeshData navMeshData; [SerializeField] private LayerMask layerMask = -1; [SerializeField] private int defaultArea; [SerializeField] private bool ignoreNavMeshAgent = true; [SerializeField] private bool ignoreNavMeshObstacle = true; [SerializeField] private int unwalkableArea = 1; private NavMeshDataInstance m_NavMeshDataInstance; private Dictionary cachedSpriteMeshes = new Dictionary(); public int AgentTypeID { get { return agentTypeID; } set { agentTypeID = value; } } public bool OverrideTileSize { get { return overrideTileSize; } set { overrideTileSize = value; } } public int TileSize { get { return tileSize; } set { tileSize = value; } } public bool OverrideVoxelSize { get { return overrideVoxelSize; } set { overrideVoxelSize = value; } } public float VoxelSize { get { return voxelSize; } set { voxelSize = value; } } public NavMeshData NavMeshData { get { return navMeshData; } set { navMeshData = value; } } public LayerMask LayerMask { get { return layerMask; } set { layerMask = value; } } public int DefaultArea { get { return defaultArea; } set { defaultArea = value; } } public bool IgnoreNavMeshAgent { get { return ignoreNavMeshAgent; } set { ignoreNavMeshAgent = value; } } public bool IgnoreNavMeshObstacle { get { return ignoreNavMeshObstacle; } set { ignoreNavMeshObstacle = value; } } public int UnwalkableArea { get { return unwalkableArea; } set { unwalkableArea = value; } } public override void Generate(Dungeon dungeon) { BakeNavMesh(dungeon); if (AddNavMeshLinksBetweenRooms) { foreach (DoorwayConnection connection in dungeon.Connections) { foreach (NavMeshAgentLinkInfo navMeshAgentType in NavMeshAgentTypes) { AddNavMeshLink(connection, navMeshAgentType); } } } if (OnProgress != null) { OnProgress(new NavMeshGenerationProgress { Description = "Done", Percentage = 1f }); } } protected void AddData() { if (!m_NavMeshDataInstance.valid && navMeshData != null) { m_NavMeshDataInstance = NavMesh.AddNavMeshData(navMeshData, base.transform.position, rotation); m_NavMeshDataInstance.owner = this; } } protected void RemoveData() { m_NavMeshDataInstance.Remove(); m_NavMeshDataInstance = default(NavMeshDataInstance); foreach (KeyValuePair cachedSpriteMesh in cachedSpriteMeshes) { UnityEngine.Object.DestroyImmediate(cachedSpriteMesh.Value); } cachedSpriteMeshes.Clear(); } protected virtual void BakeNavMesh(Dungeon dungeon) { List sources = CollectSources(); Bounds localBounds = CalculateWorldBounds(sources); NavMeshData navMeshData = NavMeshBuilder.BuildNavMeshData(GetBuildSettings(), sources, localBounds, base.transform.position, rotation); if (navMeshData != null) { navMeshData.name = base.gameObject.name; RemoveData(); this.navMeshData = navMeshData; if (base.isActiveAndEnabled) { AddData(); } } if (OnProgress != null) { OnProgress(new NavMeshGenerationProgress { Description = "Done", Percentage = 1f }); } } protected void AppendModifierVolumes(ref List sources) { List list = new List(GetComponentsInChildren()); list.RemoveAll((NavMeshModifierVolume x) => !x.isActiveAndEnabled); foreach (NavMeshModifierVolume item2 in list) { if (((int)layerMask & (1 << item2.gameObject.layer)) != 0 && item2.AffectsAgentType(agentTypeID)) { Vector3 pos = item2.transform.TransformPoint(item2.center); Vector3 lossyScale = item2.transform.lossyScale; Vector3 size = new Vector3(item2.size.x * Mathf.Abs(lossyScale.x), item2.size.y * Mathf.Abs(lossyScale.y), item2.size.z * Mathf.Abs(lossyScale.z)); NavMeshBuildSource item = default(NavMeshBuildSource); item.shape = NavMeshBuildSourceShape.ModifierBox; item.transform = Matrix4x4.TRS(pos, item2.transform.rotation, Vector3.one); item.size = size; item.area = item2.area; sources.Add(item); } } } protected virtual List CollectSources() { List sources = new List(); List list = new List(); List list2 = new List(GetComponentsInChildren()); list2.RemoveAll((NavMeshModifier x) => !x.isActiveAndEnabled); foreach (NavMeshModifier item3 in list2) { if (((int)layerMask & (1 << item3.gameObject.layer)) != 0 && item3.AffectsAgentType(agentTypeID)) { NavMeshBuildMarkup item = default(NavMeshBuildMarkup); item.root = item3.transform; item.overrideArea = item3.overrideArea; item.area = item3.area; item.ignoreFromBuild = item3.ignoreFromBuild; list.Add(item); } } SpriteRenderer[] array = UnityEngine.Object.FindObjectsOfType(); foreach (SpriteRenderer spriteRenderer in array) { Sprite sprite = spriteRenderer.sprite; Mesh mesh = GetMesh(sprite); if (mesh != null) { int area = ((((int)layerMask & (1 << spriteRenderer.gameObject.layer)) == 0) ? unwalkableArea : 0); sources.Add(new NavMeshBuildSource { transform = spriteRenderer.transform.localToWorldMatrix, size = mesh.bounds.extents * 2f, shape = NavMeshBuildSourceShape.Mesh, area = area, sourceObject = mesh, component = spriteRenderer }); } } NavMeshBuildSource navMeshBuildSource = default(NavMeshBuildSource); navMeshBuildSource.shape = NavMeshBuildSourceShape.Mesh; navMeshBuildSource.area = 0; NavMeshBuildSource item2 = navMeshBuildSource; Tilemap[] array2 = UnityEngine.Object.FindObjectsOfType(); foreach (Tilemap tilemap in array2) { for (int j = tilemap.cellBounds.xMin; j < tilemap.cellBounds.xMax; j++) { for (int k = tilemap.cellBounds.yMin; k < tilemap.cellBounds.yMax; k++) { Vector3Int position = new Vector3Int(j, k, 0); if (tilemap.HasTile(position)) { UnityEngine.Tilemaps.Tile tile = tilemap.GetTile(position); Mesh mesh2 = GetMesh(tilemap.GetSprite(position)); if (mesh2 != null) { item2.transform = Matrix4x4.TRS(tilemap.GetCellCenterWorld(position) - tilemap.layoutGrid.cellGap, tilemap.transform.rotation, tilemap.transform.lossyScale) * tilemap.orientationMatrix * tilemap.GetTransformMatrix(position); item2.sourceObject = mesh2; item2.component = tilemap; item2.area = ((tile.colliderType != 0) ? unwalkableArea : 0); sources.Add(item2); } } } } } if (ignoreNavMeshAgent) { sources.RemoveAll((NavMeshBuildSource x) => x.component != null && x.component.gameObject.GetComponent() != null); } if (ignoreNavMeshObstacle) { sources.RemoveAll((NavMeshBuildSource x) => x.component != null && x.component.gameObject.GetComponent() != null); } AppendModifierVolumes(ref sources); return sources; } protected Mesh GetMesh(Sprite sprite) { if (sprite == null) { return null; } if (!cachedSpriteMeshes.TryGetValue(sprite, out var value)) { value = new Mesh { vertices = sprite.vertices.Select((Vector2 v) => new Vector3(v.x, v.y, 0f)).ToArray(), triangles = ((IEnumerable)sprite.triangles).Select((Func)((ushort i) => i)).ToArray() }; value.RecalculateBounds(); value.RecalculateNormals(); value.RecalculateTangents(); cachedSpriteMeshes[sprite] = value; } return value; } protected void AddNavMeshLink(DoorwayConnection connection, NavMeshAgentLinkInfo agentLinkInfo) { GameObject gameObject = connection.A.gameObject; NavMeshBuildSettings settingsByID = NavMesh.GetSettingsByID(agentLinkInfo.AgentTypeID); float width = Mathf.Max(connection.A.Socket.Size.x - settingsByID.agentRadius * 2f, 0.01f); NavMeshLink link = gameObject.AddComponent(); link.agentTypeID = agentLinkInfo.AgentTypeID; link.bidirectional = true; link.area = agentLinkInfo.AreaTypeID; link.startPoint = new Vector3(0f, 0f, 0f - NavMeshLinkDistanceFromDoorway); link.endPoint = new Vector3(0f, 0f, NavMeshLinkDistanceFromDoorway); link.width = width; if (!agentLinkInfo.DisableLinkWhenDoorIsClosed) { return; } GameObject gameObject2 = ((connection.A.UsedDoorPrefabInstance != null) ? connection.A.UsedDoorPrefabInstance : ((connection.B.UsedDoorPrefabInstance != null) ? connection.B.UsedDoorPrefabInstance : null)); if (!(gameObject2 != null)) { return; } Door component = gameObject2.GetComponent(); link.enabled = component.IsOpen; if (component != null) { component.OnDoorStateChanged += delegate(Door d, bool o) { link.enabled = o; }; } } public NavMeshBuildSettings GetBuildSettings() { NavMeshBuildSettings settingsByID = NavMesh.GetSettingsByID(agentTypeID); if (settingsByID.agentTypeID == -1) { Debug.LogWarning("No build settings for agent type ID " + AgentTypeID, this); settingsByID.agentTypeID = agentTypeID; } if (OverrideTileSize) { settingsByID.overrideTileSize = true; settingsByID.tileSize = TileSize; } if (OverrideVoxelSize) { settingsByID.overrideVoxelSize = true; settingsByID.voxelSize = VoxelSize; } return settingsByID; } protected Bounds CalculateWorldBounds(List sources) { Matrix4x4 inverse = Matrix4x4.TRS(base.transform.position, rotation, Vector3.one).inverse; Bounds result = default(Bounds); foreach (NavMeshBuildSource source in sources) { switch (source.shape) { case NavMeshBuildSourceShape.Mesh: { Mesh mesh = source.sourceObject as Mesh; result.Encapsulate(GetWorldBounds(inverse * source.transform, mesh.bounds)); break; } case NavMeshBuildSourceShape.Terrain: { TerrainData terrainData = source.sourceObject as TerrainData; result.Encapsulate(GetWorldBounds(inverse * source.transform, new Bounds(0.5f * terrainData.size, terrainData.size))); break; } case NavMeshBuildSourceShape.Box: case NavMeshBuildSourceShape.Sphere: case NavMeshBuildSourceShape.Capsule: case NavMeshBuildSourceShape.ModifierBox: result.Encapsulate(GetWorldBounds(inverse * source.transform, new Bounds(Vector3.zero, source.size))); break; } } result.Expand(0.1f); return result; } private static Vector3 Abs(Vector3 v) { return new Vector3(Mathf.Abs(v.x), Mathf.Abs(v.y), Mathf.Abs(v.z)); } private static Bounds GetWorldBounds(Matrix4x4 mat, Bounds bounds) { Vector3 vector = Abs(mat.MultiplyVector(Vector3.right)); Vector3 vector2 = Abs(mat.MultiplyVector(Vector3.up)); Vector3 vector3 = Abs(mat.MultiplyVector(Vector3.forward)); Vector3 center = mat.MultiplyPoint(bounds.center); Vector3 size = vector * bounds.size.x + vector2 * bounds.size.y + vector3 * bounds.size.z; return new Bounds(center, size); } } }