using System.Collections; using System.Collections.Generic; using UnityEngine; using HPJ.Simulation.Map; using System.Threading; using System.Threading.Tasks; using HPJ.Simulation.Pathing; using HPJ.Simulation.Enums; using UnityEditor; using System; using HPJ.Simulation; using System.IO; //using Unity.Plastic.Newtonsoft.Json; using UnityEngine.SceneManagement; using HPJ.Presentation.Obstacle; using HPJ.Presentation.Agents; using HPJ.Presentation.NavigationManagerSettings; using Newtonsoft.Json; #if UNITY_EDITOR using Unity.EditorCoroutines.Editor; #endif namespace HPJ.Presentation { /// /// The Manager used to manage the Map sets, Obstacles as Gameobjects, Navigation Agents as Gameobjects /// public class NavigationManager : MonoBehaviour { #region Maps [SerializeField] protected List _mapSets = new List(); /// /// A List of the maps /// public List MapSets { get { if (ApplicationPlaying) { return _activeSets; } return _mapSets; } } [NonSerialized] protected List _activeSets; protected bool ApplicationPlaying { get; set; } /// /// A List of all the bridges /// [SerializeField] protected List _bridges = new List(); public List Bridges { get { return _bridges; } } /// /// A List of all the bridges /// [SerializeField] protected List _seams = new List(); public List Seams { get { return _seams; } } /// /// The Navigation Manager Settings /// public ManagerSetting ManagerSettings = new ManagerSetting(); /// /// Seam settings for the Navigation Manager /// public SeamSettings MapSeamSettings = new SeamSettings(); /// /// Used to send a Navigation Job Request to the simulation /// /// public void SetDestination(NavigationJob Job) { for (int i = 0; i < MapSets.Count; i++) { MapSet Set = MapSets[i]; if (Set.JobCompatible(Job)) { Set.SetDestination(Job); return; } } if (Job.CurrentPath != null) { Job.CurrentPath.PreviousStatus = "Was not in bounds of a Map"; Job.CurrentPath.ValidPath = false; Job.CurrentStep = PathfindingCalculationStep.Complete; } } /// /// Adds a navigation order for the Navigation Agents /// /// internal void AddNavigationOrder(NavigationOrder newOrder) { newOrder.StartingMap.SetDestination(newOrder.NavJob); } /// /// Initilaizes all the map bridges /// public void SetMapBridges() { for (int i = 0; i < MapSets.Count; i++) { MapSet Map = MapSets[i]; Map.ClearMaps(); foreach (MapBridge Bridge in Bridges) { if (Bridge.Contains(Map)) { Bridge.RefreshWithNew(Map); Map.AddBridge(Bridge); } } foreach (MapSeam Seam in Seams) { if (Seam.Contains(Map)) { Seam.RefreshWithNew(Map); Map.AddSeam(Seam); } } } } /// /// Gets a desired bridge or seam for a position /// /// /// /// /// /// /// public bool GetDesiredBridgeOrSeam(IntVector3 start, MapSet startingMap, MapSet endingMap, out MapBridge ClosestBridge, out MapSeam ClosestSeam, out int BridgeScore, out int SeamScore) { ClosestBridge = null; ClosestSeam = null; BridgeScore = int.MaxValue; SeamScore = int.MaxValue; // Make sure that these maps HAVE a way out of them if ((endingMap.MapBridges.Count == 0 && endingMap.MapSeams.Count == 0) || (startingMap.MapBridges.Count == 0 && startingMap.MapSeams.Count == 0)) { Debug.Log($"No Seam or Bridge for Starting or Ending Map {startingMap.SetSettings.Name}-> S#{startingMap.MapSeams.Count} - B#{startingMap.MapBridges.Count} / {endingMap.SetSettings.Name}-> S#{endingMap.MapSeams.Count} - B#{endingMap.MapBridges.Count}"); return false; } int ClosestScore = int.MaxValue; // Check for direct bridges first for (int i = 0; i < Bridges.Count; i++) { MapBridge Bridge = Bridges[i]; if (!Bridge.ValidBridge) { continue; } if (Bridge.CanWalkInDirection(startingMap, endingMap)) { IntVector3 BridgePoint = Bridge.GetPoint(startingMap); int Distance = IntVector3.DistanceSquared(BridgePoint, start); if (Distance < ClosestScore) { ClosestBridge = Bridge; ClosestScore = Distance; } } } ClosestScore = int.MaxValue; // Check for direct bridges first for (int i = 0; i < Seams.Count; i++) { MapSeam Seam = Seams[i]; if (!Seam.ValidSeam) { continue; } if (Seam.DirectConnects(startingMap, endingMap)) { IntVector3 SeamPoint = Seam.GetPoint(startingMap, start); int Distance = IntVector3.DistanceSquared(SeamPoint, start); if (Distance < ClosestScore) { ClosestSeam = Seam; ClosestScore = Distance; } } } // Ok adjacent map does not directly lead to the end map, so lets do node based pathfinding to try and get there if (ClosestBridge == null && ClosestSeam == null) { ClosestBridge = AStarBridgeNode(startingMap, endingMap, start); ClosestSeam = AStarSeamNode(startingMap, endingMap, start); } if (ClosestBridge != null) { IntVector3 BridgePoint = ClosestBridge.GetPoint(startingMap); BridgeScore = IntVector3.DistanceSquared(BridgePoint, start); } if (ClosestSeam != null) { IntVector3 SeamPoint = ClosestSeam.GetPoint(startingMap, start); SeamScore = IntVector3.DistanceSquared(SeamPoint, start); } return ClosestBridge != null || ClosestSeam != null; } /// /// Gets a desired bridge for a position /// /// /// /// /// public MapBridge GetDesiredBridge(IntVector3 start, MapSet startingMap, MapSet endingMap) { if (endingMap.MapBridges.Count == 0 || startingMap.MapBridges.Count == 0) { return null; } MapBridge ClosestBridge = null; int ClosestScore = int.MaxValue; // Check for direct bridges first foreach (MapBridge Bridge in Bridges) { if (!Bridge.ValidBridge) { continue; } if (Bridge.CanWalkInDirection(startingMap, endingMap)) { IntVector3 BridgePoint = Bridge.GetPoint(startingMap); int Distance = IntVector3.DistanceSquared(BridgePoint, start); if (Distance < ClosestScore) { ClosestBridge = Bridge; ClosestScore = Distance; } } } // Ok adjacent map does not directly lead to the end map, so lets do node based pathfinding to try and get there if (ClosestBridge == null) { ClosestBridge = AStarBridgeNode(startingMap, endingMap, start); //ClosestScore = int.MaxValue; //List ClosedMaps = new List(); //foreach (MapBridge Bridge in startingMap.MapBridges) //{ // if (!Bridge.ValidBridge) // { // continue; // } // IntVector3 BridgePoint = Bridge.GetPoint(startingMap); // int Distance = IntVector3.DistanceSquared(BridgePoint, start); // ClosedMaps.Add(startingMap); // if (BridgeEventuallyLeadsToMap(Bridge, startingMap, endingMap, ClosedMaps)) // { // if (Distance < ClosestScore) // { // ClosestBridge = Bridge; // } // } // ClosedMaps.Clear(); //} } return ClosestBridge; } /// /// Gets a desired seam for a position /// /// /// /// /// public MapSeam GetDesiredSeam(IntVector3 start, MapSet startingMap, MapSet endingMap) { if (endingMap.MapBridges.Count == 0 || startingMap.MapBridges.Count == 0) { return null; } MapSeam ClosestSeam = null; int ClosestScore = int.MaxValue; // Check for direct bridges first foreach (MapSeam Seam in Seams) { if (!Seam.ValidSeam) { continue; } if (Seam.DirectConnects(startingMap, endingMap)) { IntVector3 BridgePoint = Seam.GetPoint(startingMap, start); int Distance = IntVector3.DistanceSquared(BridgePoint, start); if (Distance < ClosestScore) { ClosestSeam = Seam; ClosestScore = Distance; } } } // Ok adjacent map does not directly lead to the end map, so lets do node based pathfinding to try and get there if (ClosestSeam == null) { ClosestSeam = AStarSeamNode(startingMap, endingMap, start); //ClosestScore = int.MaxValue; //List ClosedMaps = new List(); //foreach (MapBridge Bridge in startingMap.MapBridges) //{ // if (!Bridge.ValidBridge) // { // continue; // } // IntVector3 BridgePoint = Bridge.GetPoint(startingMap); // int Distance = IntVector3.DistanceSquared(BridgePoint, start); // ClosedMaps.Add(startingMap); // if (BridgeEventuallyLeadsToMap(Bridge, startingMap, endingMap, ClosedMaps)) // { // if (Distance < ClosestScore) // { // ClosestBridge = Bridge; // } // } // ClosedMaps.Clear(); //} } return ClosestSeam; } #region Bridge AStar Region #region Map Bridges /// /// Used A* Node Pathfinding to find a set of bridge to a ending map /// /// /// /// /// public MapBridge AStarBridgeNode(MapSet StartMap, MapSet EndMap, IntVector3 Start) { List OpenList = new List(); List ClosedList = new List(); Dictionary SortedList = new Dictionary(); OpenList.Add(StartMap.InstanceID); AStarBridgeData StartingTileData = new AStarBridgeData(-1, StartMap.InstanceID, GetMapDistance(StartMap, EndMap)); StartingTileData.GCost = 0; StartingTileData.FCost = StartingTileData.GCost + StartingTileData.HCost; SortedList.Add(StartMap.InstanceID, StartingTileData); MapSet CurrentTile = StartMap; int Iterations = 0; bool MaxIterationsMade = false; int MaxIterations = 3000; while (OpenList.Count > 0) { // Give a safty range of 2 times the range in terms of iterations, if it cannot find a path by then, it's failed. can probably cannot be reached if (Iterations > MaxIterations) { MaxIterationsMade = true; break; } Iterations++; int CurrentNodeIndex = GetLowestFCostMapInstanceID(OpenList, EndMap); // Remove current CurrentTile = MapSets[OpenList[CurrentNodeIndex]]; OpenList.RemoveAt(CurrentNodeIndex); AStarBridgeData CurrentData = SortedList[CurrentTile.InstanceID]; CurrentTile = MapSets[CurrentData.ID]; ClosedList.Add(CurrentTile.InstanceID); if (EndMap.InstanceID == CurrentTile.InstanceID) { // Reached the end //Debug.Log("Found the End - Should be Path Complete"); break; } for (int j= 0; j < CurrentTile.MapBridges.Count; j++) { MapBridge Bridge = CurrentTile.MapBridges[j]; MapSet OtherMap = Bridge.Other(CurrentTile); //Debug.Log($"Other Map {OtherMap.SetSettings.Name} ({OtherMap.InstanceID}) -> Bridges {OtherMap.MapBridges.Count}"); //if (OtherMap.InstanceID == EndMap.InstanceID) //{ // Debug.Log("Found Last Map"); //} if (OtherMap == null) { continue; } if (!Bridge.CanWalkInDirection(CurrentTile, OtherMap)) { continue; } AStarBridgeData NeighbourData; if (!SortedList.ContainsKey(OtherMap.InstanceID)) { NeighbourData = new AStarBridgeData(CurrentData.ID, OtherMap.InstanceID, GetMapDistance(OtherMap, EndMap)); SortedList.Add(OtherMap.InstanceID, NeighbourData); if (!ClosedList.Contains(OtherMap.InstanceID)) { //Debug.Log($"Add Map {OtherMap.SetSettings.Name} for ({StartMap.SetSettings.Name} -> {EndMap.SetSettings.Name})"); OpenList.Add(OtherMap.InstanceID); } } else { NeighbourData = SortedList[OtherMap.InstanceID]; } int TentativeGCost = CurrentData.GCost + GetMapDistance(CurrentTile, OtherMap); if (TentativeGCost < NeighbourData.GCost) { NeighbourData.FromMapInstanceID = CurrentData.ID; NeighbourData.GCost = TentativeGCost; NeighbourData.FCost = NeighbourData.GCost + NeighbourData.HCost; SortedList[OtherMap.InstanceID] = NeighbourData; } } for (int j = 0; j < CurrentTile.MapSeams.Count; j++) { MapSeam Seam = CurrentTile.MapSeams[j]; MapSet OtherMap = Seam.Other(CurrentTile); //Debug.Log($"Other Map {OtherMap.SetSettings.Name} ({OtherMap.InstanceID}) -> Bridges {OtherMap.MapBridges.Count}"); //if (OtherMap.InstanceID == EndMap.InstanceID) //{ // Debug.Log("Found Last Map"); //} if (OtherMap == null) { continue; } AStarBridgeData NeighbourData; if (!SortedList.ContainsKey(OtherMap.InstanceID)) { NeighbourData = new AStarBridgeData(CurrentData.ID, OtherMap.InstanceID, GetMapDistance(OtherMap, EndMap)); SortedList.Add(OtherMap.InstanceID, NeighbourData); if (!ClosedList.Contains(OtherMap.InstanceID)) { //Debug.Log($"Add Map {OtherMap.SetSettings.Name} for ({StartMap.SetSettings.Name} -> {EndMap.SetSettings.Name})"); OpenList.Add(OtherMap.InstanceID); } } else { NeighbourData = SortedList[OtherMap.InstanceID]; } int TentativeGCost = CurrentData.GCost + GetMapDistance(CurrentTile, OtherMap); if (TentativeGCost < NeighbourData.GCost) { NeighbourData.FromMapInstanceID = CurrentData.ID; NeighbourData.GCost = TentativeGCost; NeighbourData.FCost = NeighbourData.GCost + NeighbourData.HCost; SortedList[OtherMap.InstanceID] = NeighbourData; } } } AStarBridgeData EndTileData; if (MaxIterationsMade) { Debug.Log("Max Iterations Made"); return null; } else if (!SortedList.TryGetValue(EndMap.InstanceID, out EndTileData)) { // No Valid Path Found Debug.Log($"No Valid Path Found in sorted list ({ClosedList.Count} / {OpenList.Count} / {SortedList.Count})"); return null; } else if (EndTileData.FromMapInstanceID == -1) { // No Valid Path Found Debug.Log("End does not have a from ID"); return null; } else { int NextMapIndex = GetNextMap(SortedList, EndMap, StartMap); if (NextMapIndex != -1) { MapSet NextMap = MapSets[NextMapIndex]; MapBridge ClosestMap = StartMap.GetClosestBridge(NextMap, Start); return ClosestMap; } Debug.Log("This should not hit"); } return null; } private int GetNextMap(Dictionary sortedList, MapSet EndMap, MapSet StartMap) { AStarBridgeData CurrentTile = sortedList[EndMap.InstanceID]; // The Route From Index of the starting tile should be the only one with a (-1, -1) index while (CurrentTile.FromMapInstanceID != -1) { if (CurrentTile.FromMapInstanceID == StartMap.InstanceID) { return CurrentTile.ID; } CurrentTile = sortedList[CurrentTile.FromMapInstanceID]; } return -1; } private int GetLowestFCostMapInstanceID(List openList, MapSet endMap) { int LowestCostIndex = 0; // Typical AStar pathfinding int CurrentLowestCost = GetMapDistance(MapSets[openList[0]], endMap); for (int i = 1; i < openList.Count; i++) { int testCost = GetMapDistance(MapSets[openList[i]], endMap); if (testCost < CurrentLowestCost) { CurrentLowestCost = testCost; LowestCostIndex = i; } } return LowestCostIndex; } private int GetMapDistance(MapSet Map1, MapSet Map2) { if (Map1.InstanceID == Map2.InstanceID) { return 0; } int LowestDistance = int.MaxValue; for (int i = 0; i < Map1.KeyBorderPositions.Count; i++) { IntVector3 KeyPosition = Map1.KeyBorderPositions[i]; for (int j = 0; j < Map2.KeyBorderPositions.Count; j++) { IntVector3 OtherMapKeyPosition = Map2.KeyBorderPositions[j]; int NewScore = IntVector3.Displacement(KeyPosition, OtherMapKeyPosition); if (NewScore < LowestDistance) { LowestDistance = NewScore; } } } return LowestDistance; } private struct AStarBridgeData { public int ID; public int FromMapInstanceID; public int FCost; public int GCost; public int HCost; public AStarBridgeData(int MapInstanceID, int ThisID, int EndCost) { ID = ThisID; FromMapInstanceID = MapInstanceID; FCost = int.MaxValue; GCost = int.MaxValue; HCost = EndCost; } } public bool BridgeEventuallyLeadsToMap(MapBridge Bridge, MapSet CurrentMap, MapSet EndingMap, List ClosedList) { MapSet OtherMap = Bridge.Other(CurrentMap); if (OtherMap == null) { return false; } if (OtherMap == EndingMap) { return true; } if (ClosedList.Contains(OtherMap)) { return false; } ClosedList.Add(OtherMap); for (int i = 0; i < OtherMap.MapBridges.Count; i++) { MapBridge OtherBridge = OtherMap.MapBridges[i]; MapSet NextOtherMap = OtherBridge.Other(OtherMap); if (NextOtherMap == EndingMap) { return true; } if (NextOtherMap == null) { continue; } if (ClosedList.Contains(NextOtherMap)) { continue; } if (BridgeEventuallyLeadsToMap(OtherBridge, OtherMap, EndingMap, ClosedList)) { return true; } } for (int i = 0; i < OtherMap.MapSeams.Count; i++) { MapSeam OtherSeam = OtherMap.MapSeams[i]; MapSet NextOtherMap = OtherSeam.Other(OtherMap); if (NextOtherMap == EndingMap) { return true; } if (NextOtherMap == null) { continue; } if (ClosedList.Contains(NextOtherMap)) { continue; } if (SeamEventuallyLeadsToMap(OtherSeam, OtherMap, EndingMap, ClosedList)) { return true; } } return false; } #endregion #region Map Seams /// /// Used A* Node Pathfinding to find a set of seams to a ending map /// /// /// /// /// public MapSeam AStarSeamNode(MapSet StartMap, MapSet EndMap, IntVector3 Start) { List OpenList = new List(); List ClosedList = new List(); Dictionary SortedList = new Dictionary(); OpenList.Add(StartMap.InstanceID); AStarBridgeData StartingTileData = new AStarBridgeData(-1, StartMap.InstanceID, GetMapDistance(StartMap, EndMap)); StartingTileData.GCost = 0; StartingTileData.FCost = StartingTileData.GCost + StartingTileData.HCost; SortedList.Add(StartMap.InstanceID, StartingTileData); MapSet CurrentTile = StartMap; int Iterations = 0; bool MaxIterationsMade = false; int MaxIterations = 3000; while (OpenList.Count > 0) { // Give a safty range of 2 times the range in terms of iterations, if it cannot find a path by then, it's failed. can probably cannot be reached if (Iterations > MaxIterations) { MaxIterationsMade = true; break; } Iterations++; int CurrentNodeIndex = GetLowestFCostMapInstanceID(OpenList, EndMap); // Remove current CurrentTile = MapSets[OpenList[CurrentNodeIndex]]; OpenList.RemoveAt(CurrentNodeIndex); AStarBridgeData CurrentData = SortedList[CurrentTile.InstanceID]; CurrentTile = MapSets[CurrentData.ID]; ClosedList.Add(CurrentTile.InstanceID); if (EndMap.InstanceID == CurrentTile.InstanceID) { // Reached the end //Debug.Log("Found the End - Should be Path Complete"); break; } foreach (MapBridge Bridge in CurrentTile.MapBridges) { MapSet OtherMap = Bridge.Other(CurrentTile); //Debug.Log($"Other Map {OtherMap.SetSettings.Name} ({OtherMap.InstanceID}) -> Bridges {OtherMap.MapBridges.Count}"); //if (OtherMap.InstanceID == EndMap.InstanceID) //{ // Debug.Log("Found Last Map"); //} if (OtherMap == null) { continue; } AStarBridgeData NeighbourData; if (!SortedList.ContainsKey(OtherMap.InstanceID)) { NeighbourData = new AStarBridgeData(CurrentData.ID, OtherMap.InstanceID, GetMapDistance(OtherMap, EndMap)); SortedList.Add(OtherMap.InstanceID, NeighbourData); if (!ClosedList.Contains(OtherMap.InstanceID)) { //Debug.Log($"Add Map {OtherMap.SetSettings.Name} for ({StartMap.SetSettings.Name} -> {EndMap.SetSettings.Name})"); OpenList.Add(OtherMap.InstanceID); } } else { NeighbourData = SortedList[OtherMap.InstanceID]; } int TentativeGCost = CurrentData.GCost + GetMapDistance(CurrentTile, OtherMap); if (TentativeGCost < NeighbourData.GCost) { NeighbourData.FromMapInstanceID = CurrentData.ID; NeighbourData.GCost = TentativeGCost; NeighbourData.FCost = NeighbourData.GCost + NeighbourData.HCost; SortedList[OtherMap.InstanceID] = NeighbourData; } } foreach (MapSeam Seam in CurrentTile.MapSeams) { MapSet OtherMap = Seam.Other(CurrentTile); //Debug.Log($"Other Map {OtherMap.SetSettings.Name} ({OtherMap.InstanceID}) -> Bridges {OtherMap.MapBridges.Count}"); //if (OtherMap.InstanceID == EndMap.InstanceID) //{ // Debug.Log("Found Last Map"); //} if (OtherMap == null) { continue; } AStarBridgeData NeighbourData; if (!SortedList.ContainsKey(OtherMap.InstanceID)) { NeighbourData = new AStarBridgeData(CurrentData.ID, OtherMap.InstanceID, GetMapDistance(OtherMap, EndMap)); SortedList.Add(OtherMap.InstanceID, NeighbourData); if (!ClosedList.Contains(OtherMap.InstanceID)) { //Debug.Log($"Add Map {OtherMap.SetSettings.Name} for ({StartMap.SetSettings.Name} -> {EndMap.SetSettings.Name})"); OpenList.Add(OtherMap.InstanceID); } } else { NeighbourData = SortedList[OtherMap.InstanceID]; } int TentativeGCost = CurrentData.GCost + GetMapDistance(CurrentTile, OtherMap); if (TentativeGCost < NeighbourData.GCost) { NeighbourData.FromMapInstanceID = CurrentData.ID; NeighbourData.GCost = TentativeGCost; NeighbourData.FCost = NeighbourData.GCost + NeighbourData.HCost; SortedList[OtherMap.InstanceID] = NeighbourData; } } } AStarBridgeData EndTileData; if (MaxIterationsMade) { Debug.Log("Max Iterations Made"); return null; } else if (!SortedList.TryGetValue(EndMap.InstanceID, out EndTileData)) { // No Valid Path Found Debug.Log($"No Valid Path Found in sorted list ({ClosedList.Count} / {OpenList.Count} / {SortedList.Count})"); return null; } else if (EndTileData.FromMapInstanceID == -1) { // No Valid Path Found Debug.Log("End does not have a from ID"); return null; } else { int NextMapIndex = GetNextMap(SortedList, EndMap, StartMap); if (NextMapIndex != -1) { MapSet NextMap = MapSets[NextMapIndex]; MapSeam ClosestMap = StartMap.GetClosestSeam(NextMap, Start); return ClosestMap; } Debug.Log("This should not hit"); } return null; } public bool SeamEventuallyLeadsToMap(MapSeam Seam, MapSet CurrentMap, MapSet EndingMap, List ClosedList) { MapSet OtherMap = Seam.Other(CurrentMap); if (OtherMap == null) { return false; } if (OtherMap == EndingMap) { return true; } if (ClosedList.Contains(OtherMap)) { return false; } ClosedList.Add(OtherMap); foreach (MapSeam OtherSeam in OtherMap.MapSeams) { MapSet NextOtherMap = OtherSeam.Other(OtherMap); if (NextOtherMap == EndingMap) { return true; } if (NextOtherMap == null) { continue; } if (ClosedList.Contains(NextOtherMap)) { continue; } if (SeamEventuallyLeadsToMap(OtherSeam, OtherMap, EndingMap, ClosedList)) { return true; } } foreach (MapBridge OtherBridge in OtherMap.MapBridges) { MapSet NextOtherMap = OtherBridge.Other(OtherMap); if (NextOtherMap == EndingMap) { return true; } if (NextOtherMap == null) { continue; } if (ClosedList.Contains(NextOtherMap)) { continue; } if (BridgeEventuallyLeadsToMap(OtherBridge, OtherMap, EndingMap, ClosedList)) { return true; } } return false; } #endregion #endregion /// /// Checks whether a specific bridge is valid /// /// /// public bool ValidBridge(MapBridge Bridge) { if (Bridge == null || Bridge.MidPoints == null) { return false; } if (Bridge.MidPoints.Count <= 0) { return false; } Bridge.MapB = GetMapAtTileWorldPosition(Bridge.MidPoints[Bridge.MidPoints.Count - 1]); Bridge.MapA = GetMapAtTileWorldPosition(Bridge.MidPoints[0]); if (Bridge.MapB == Bridge.MapA) { return false; } if (Bridge.MapB == null || Bridge.MapA == null) { return false; } return true; } private void ValidateBridge(MapBridge bridge) { bridge.MapB = GetMapAtTileWorldPosition(bridge.MidPoints[bridge.MidPoints.Count - 1]); bridge.MapA = GetMapAtTileWorldPosition(bridge.MidPoints[0]); } #endregion #region Threads /// /// Map Simulatin Threads /// public Thread[] Threads; /// /// Whether the map simulation threads are running /// public bool Running { get; protected set; } /// /// Extra Simulation Thrad /// public Thread ExtraSimulatinThread; /// /// Whether the map simulation helper thread is running /// public bool HelperRunning { get; protected set; } /// /// The options for parrellism in the map simulation threads /// protected ParallelOptions _parallelOptions; /// /// Log list for callback from the map simulation Threads /// public List Logs { get; protected set; } public List Warnings { get; protected set; } public List Errors { get; protected set; } /// /// Logs all the callbacks from the simulation /// /// /// public void AddDebugLog(string Log, SimulationDebugLog LogType) { if (LogType == SimulationDebugLog.Error) { lock (Errors) { Errors.Add(Log); } } else if (LogType == SimulationDebugLog.Warning) { lock (Warnings) { Warnings.Add(Log); } } else { lock (Logs) { Logs.Add(Log); } } } #endregion #region Painter /// /// Painting Settings /// public PainterSetting Painter = new PainterSetting(); /// /// Auto-Painter Settings /// public AutoPainterSetting AutoPainter = new AutoPainterSetting(); /// /// Autopaints a givent map by the auto painter settings /// public void AutoPaint() { if (AutoPainter.AutoPaintMapIndex < 0 || AutoPainter.AutoPaintMapIndex >= _mapSets.Count) { return; } #if UNITY_EDITOR if (!Application.isPlaying) { EditorCoroutineUtility.StartCoroutine(BakeTiles(), this); } else { StartCoroutine(BakeTiles()); } #endif } /// /// Whether the Auto-Painter is baking in the tiles right now or not /// public bool Baking { get; set; } /// /// Bakes all the tiles from the Auto-Painter /// /// protected virtual IEnumerator BakeTiles() { MapSet Map = _mapSets[AutoPainter.AutoPaintMapIndex]; Debug.Log($"Baking Map {Map.SetSettings.Name} (Index:{AutoPainter.AutoPaintMapIndex})"); ClearMap(AutoPainter.AutoPaintMapIndex); RaycastHit ForRayHit; RaycastHit AgainstRayHit; Baking = true; float RaycastDistance = AutoPainter.AutoPainterElavationIncrease + AutoPainter.AutoPainterElavationIDecrease + Map.SetSettings.MapSettings.Offset.y / 100f; while (Baking) { for (int x = 0; x < Map.SetSettings.MapSettings.MapWidth; x++) { for (int y = 0; y < Map.SetSettings.MapSettings.MapHeight; y++) { // Raycast Vector3 TileWorldPosition = GetTileWorldIndexCenter(x, y, Map); // Water Check if (Physics.Raycast(TileWorldPosition + Vector3.up * AutoPainter.AutoPainterElavationIncrease, Vector3.down * RaycastDistance, out ForRayHit, 800, AutoPainter.AutoPainterCheckForLayers)) { if (Physics.Raycast(TileWorldPosition + Vector3.up * AutoPainter.AutoPainterElavationIncrease, Vector3.down * RaycastDistance, out AgainstRayHit, 800, AutoPainter.AutoPainterCheckAgainstLayers)) { // If the for is closer then count it if (ForRayHit.distance < AgainstRayHit.distance) { Map.ChangeDefaultTileType(x, y, AutoPainter.AutoPainterType); } else { // Distance Check float HeightDifference = AgainstRayHit.point.y - Map.SetSettings.MapSettings.Offset.y / 100f; if (HeightDifference > AutoPainter.AboveMapTypeChangeHeight) { Map.ChangeDefaultTileType(x, y, AutoPainter.AutoPainterAboveHeightType); } else if (HeightDifference < -AutoPainter.BelowMapTypeChangeHeight) { Map.ChangeDefaultTileType(x, y, AutoPainter.AutoPainterBelowHeightType); } } } else { Map.ChangeDefaultTileType(x, y, AutoPainter.AutoPainterType); } } else { if (Physics.Raycast(TileWorldPosition + Vector3.up * AutoPainter.AutoPainterElavationIncrease, Vector3.down * RaycastDistance, out AgainstRayHit, 800, AutoPainter.AutoPainterCheckAgainstLayers)) { // Distance Check float HeightDifference = AgainstRayHit.point.y - Map.SetSettings.MapSettings.Offset.y / 100f; if (HeightDifference > AutoPainter.AboveMapTypeChangeHeight) { Map.ChangeDefaultTileType(x, y, AutoPainter.AutoPainterAboveHeightType); } else if (HeightDifference < -AutoPainter.BelowMapTypeChangeHeight) { Map.ChangeDefaultTileType(x, y, AutoPainter.AutoPainterBelowHeightType); } } } if (!Baking) { break; } } if (!Baking) { break; } yield return null; } Baking = false; } SaveMaps(); Debug.Log($"Bake Complete"); } /// /// Creates a seam from the seam settings. Editor Only /// public void CreateSeam() { if (Application.isPlaying) { return; } if (MapSeamSettings.MapAInsertSettings.MapIndex < 0 || MapSeamSettings.MapAInsertSettings.MapIndex >= MapSets.Count) { return; } if (MapSeamSettings.MapBInsertSettings.MapIndex < 0 || MapSeamSettings.MapBInsertSettings.MapIndex >= MapSets.Count) { return; } MapSet MapA = MapSets[MapSeamSettings.MapAInsertSettings.MapIndex]; MapSet MapB = MapSets[MapSeamSettings.MapBInsertSettings.MapIndex]; MapSeam NewSeam = new MapSeam(MapA, MapB, MapSeamSettings.MapAInsertSettings.ConnectionSide, MapSeamSettings.MapBInsertSettings.ConnectionSide); } /// /// Clears all maps of all the painted tiles /// public void ClearMap() { if (Painter.CleanMapIndex < 0 || Painter.CleanMapIndex >= _mapSets.Count) { return; } MapSet Map = _mapSets[AutoPainter.AutoPaintMapIndex]; Map.CleanMapTiles(); SaveMaps(); } /// /// Clears a map of all the painted tiles /// public void ClearMap(int MapIndex) { if (MapIndex < 0 || MapIndex >= _mapSets.Count) { return; } MapSet Map = _mapSets[MapIndex]; Map.CleanMapTiles(); SaveMaps(); } #endregion #region Unity public static NavigationManager Instance = null; public bool Initialized { get; set; } protected void Awake() { ApplicationPlaying = Application.IsPlaying(this); if (Instance == null) { Instance = this; } else { Debug.Log($"Duplicate Navigation Manager in Scene. Destroying Navigation Manager {name}#{GetInstanceID()}"); Destroy(gameObject); } if (RandomValidPosition == null) { RandomValidPosition = new System.Random(33399927); } _activeSets = new List(); LoadMaps(_activeSets); if (_activeSets.Count <= 0) { enabled = false; return; } Logs = new List(); Warnings = new List(); Errors = new List(); SetMapBridges(); Running = true; SimulationAgents = new Dictionary(); SimulationAgentList = new List(); Obstacles = new List(); Agents = new List(); _parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = ManagerSettings.PathfindingParrellism }; Threads = new Thread[ManagerSettings.MapRedundancy + 1]; bool CreateAgentMap = ManagerSettings.SimulationSettings.UseExtraSimulation && ManagerSettings.SimulationSettings.UseAgentAvoidance; foreach (MapSet Set in _activeSets) { Set.Initialize(ManagerSettings.PathfindingParrellism, ManagerSettings.MapRedundancy, CreateAgentMap, AddDebugLog); } foreach(MapBridge Bridge in Bridges) { Bridge.Verify(); //Debug.Log($"{Bridge.MapA.SetSettings.Name} to {Bridge.MapB.SetSettings.Name} is Valid = {Bridge.ValidBridge}"); } foreach (MapSeam Seam in Seams) { Seam.Verify(); //Debug.Log($"{Bridge.MapA.SetSettings.Name} to {Bridge.MapB.SetSettings.Name} is Valid = {Bridge.ValidBridge}"); } if (ManagerSettings.SimulationSettings.UseExtraSimulation) { ExtraSimulatinThread = new Thread(SimulationHelperUpdate); ExtraSimulatinThread.Start(); } for (int i = 0; i < ManagerSettings.MapRedundancy + 1; i++) { if (i == 0) { Threads[i] = new Thread(UpdateThread0); } else if (i == 1) { Threads[i] = new Thread(UpdateThread1); } else if (i == 2) { Threads[i] = new Thread(UpdateThread2); } else if (i == 3) { Threads[i] = new Thread(UpdateThread3); } else if (i == 4) { Threads[i] = new Thread(UpdateThread4); } else if (i == 5) { Threads[i] = new Thread(UpdateThread5); } else if (i == 6) { Threads[i] = new Thread(UpdateThread6); } else if (i == 7) { Threads[i] = new Thread(UpdateThread7); } else if (i == 8) { Threads[i] = new Thread(UpdateThread8); } else if (i == 9) { Threads[i] = new Thread(UpdateThread9); } Threads[i].Start(); } Initialized = true; Debug.Log($"Navigation Manager Initialized"); } /// /// List of all the obstacles that are currently active /// public List Obstacles { get; protected set; } /// /// List of all the agents that are currently active /// public List Agents { get; protected set; } private void Update() { for (int i = 0; i < Obstacles.Count; i++) { Obstacles[i].UpdateObstacle(); } for (int i = 0; i < Agents.Count; i++) { Agents[i].AutoUpdateAgent(); } } public void FixedUpdate() { lock (Logs) { if (Logs.Count > 0) { for (int i = 0; i < Logs.Count; i++) { string log = Logs[i]; if (!string.IsNullOrEmpty(log)) { Debug.Log(log); } } Logs.Clear(); } } lock (Warnings) { if (Warnings.Count > 0) { for (int i = 0; i < Warnings.Count; i++) { string log = Warnings[i]; if (!string.IsNullOrEmpty(log)) { Debug.LogWarning(log); } } Warnings.Clear(); } } lock (Errors) { if (Errors.Count > 0) { for (int i = 0; i < Errors.Count; i++) { string log = Errors[i]; if (!string.IsNullOrEmpty(log)) { Debug.LogError(log); } } Errors.Clear(); } } } private void OnDestroy() { foreach(MapSet Map in MapSets) { Map.OnDestroy(); } if (Running) { Running = false; foreach(Thread thread in Threads) { thread?.Abort(); } } if (HelperRunning) { HelperRunning = false; ExtraSimulatinThread?.Abort(); } Instance = null; } /// /// Navigation Manager Gizmos Settings /// public GizmosSetting GizmosSettings; /// /// Current Bridge that is being attempted /// public MapBridge AttemptedBridge { get; set; } private void OnDrawGizmosSelected() { #if UNITY_EDITOR if (!Application.isEditor) { return; } if (!Application.IsPlaying(this)) { for (int i = 0; i < MapSets.Count; i++) { if (MapSets[i].SetSettings.MapSettings.MapInfo.MakeSureSizeIsRight()) { //Debug.Log($"Had to Correct Map {MapSets[i].SetSettings.Name}"); } MapSets[i].InstanceID = i; } } if (!GizmosSettings.DisplayGizmos) { return; } int Index = -1; foreach(MapSet Map in MapSets) { Index++; if (GizmosSettings.DisplayMapGizmos_ForMapIndecies.Count > 0 && !GizmosSettings.DisplayMapGizmos_ForMapIndecies.Contains(Index)) { continue; } Vector3 MapOffset = new Vector3(Map.SetSettings.MapSettings.Offset.x, Map.SetSettings.MapSettings.Offset.y, Map.SetSettings.MapSettings.Offset.z); Vector3 MapSize = new Vector3(); MapSize.x = Map.SetSettings.MapSettings.TileSize * Map.SetSettings.MapSettings.MapWidth / 100f; MapSize.y = 0.1f; MapSize.z = Map.SetSettings.MapSettings.TileSize * Map.SetSettings.MapSettings.MapHeight / 100f; Vector3 MapCenter = new Vector3(); MapCenter.x = MapSize.x / 2 + Map.SetSettings.MapSettings.Offset.x / 100f; MapCenter.y = Map.SetSettings.MapSettings.Offset.y / 100f; MapCenter.z = MapSize.z / 2 + Map.SetSettings.MapSettings.Offset.z / 100f; if (GizmosSettings.DisplayMapBase) { if (GizmosSettings.DisplayMapEdge) { Color color = Color.black; color.a = GizmosSettings.DisplayMapAlpha; Gizmos.color = color; Gizmos.DrawCube(MapCenter - Vector3.down * 0.4f, new Vector3(MapSize.x + 5, MapSize.y, MapSize.z + 5)); } Color basecolor = Color.white; basecolor.a = GizmosSettings.DisplayMapAlpha; Gizmos.color = basecolor; Gizmos.DrawCube(MapCenter, MapSize); } if (GizmosSettings.DisplayTiles) { Gizmos.color = Color.black; MapOffset.y += 0.1f; for (int x = 0; x <= Map.SetSettings.MapSettings.MapWidth; x++) { Vector3 Start = MapOffset + new Vector3(Map.SetSettings.MapSettings.TileSize, 0, 0) * x; Start /= 100f; Gizmos.DrawLine(Start, Start + Vector3.forward * MapSize.z); } for (int y = 0; y <= Map.SetSettings.MapSettings.MapHeight; y++) { Vector3 Start = MapOffset + new Vector3(0, 0, Map.SetSettings.MapSettings.TileSize) * y; Start /= 100f; Gizmos.DrawLine(Start, Start + Vector3.right * MapSize.x); } } if (GizmosSettings.DisplayPaintedTilesMapIndex == -1 || GizmosSettings.DisplayPaintedTilesMapIndex == Index) { if (GizmosSettings.DisplayPaintedTiles) { if (Application.IsPlaying(this)) { if (Map.Initialized) { for (int x = 0; x < Map.SetSettings.MapSettings.MapWidth; x++) { for (int y = 0; y < Map.SetSettings.MapSettings.MapHeight; y++) { try { TileTypes GetType = Map.BaseMap.GetTileType(x, y); if (!GizmosSettings.OnlyPaintTheseTypes.Contains(GetType)) { continue; } if (GetType == TileTypes.Standard || GetType == TileTypes.Free) { Gizmos.color = Color.green; } else if (GetType == TileTypes.Blocked || GetType == TileTypes.Void) { Gizmos.color = Color.black; } else if (GetType == TileTypes.Obstacle) { Gizmos.color = Color.red; } else if (GetType == TileTypes.Water) { Gizmos.color = Color.blue; } Vector3 TilePosition = GetTileWorldIndexCenter(x, y, Map); TilePosition.y += 0.01f; Vector3 Size = Vector3.one * Map.SetSettings.MapSettings.TileSize / 100f; Size.y = 0.01f; Gizmos.DrawCube(TilePosition, Size); } catch (Exception ex) { Debug.LogException(ex); } } } } } else { Map.SetSettings.MapSettings.MapInfo.MakeSureSizeIsRight(); if (Map.SetSettings.MapSettings.MapWidth != Map.SetSettings.MapSettings.MapInfo.SavedTiles.GetLength(0) || Map.SetSettings.MapSettings.MapHeight != Map.SetSettings.MapSettings.MapInfo.SavedTiles.GetLength(1)) { continue; } for (int x = 0; x < Map.SetSettings.MapSettings.MapWidth; x++) { for (int y = 0; y < Map.SetSettings.MapSettings.MapHeight; y++) { try { TileTypes GetType = (TileTypes)Map.SetSettings.MapSettings.MapInfo.SavedTiles[x, y]; if (!GizmosSettings.OnlyPaintTheseTypes.Contains(GetType)) { continue; } if (GetType == TileTypes.Standard || GetType == TileTypes.Free) { Gizmos.color = Color.green; } else if (GetType == TileTypes.Blocked || GetType == TileTypes.Void) { Gizmos.color = Color.black; } else if (GetType == TileTypes.Obstacle) { Gizmos.color = Color.red; } else if (GetType == TileTypes.Water) { Gizmos.color = Color.blue; } Vector3 TilePosition = GetTileWorldIndexCenter(x, y, Map); TilePosition.y += 0.2f; Vector3 Size = Vector3.one * Map.SetSettings.MapSettings.TileSize / 100f; Size.y = 0.5f; Gizmos.DrawCube(TilePosition, Size); } catch (Exception ex) { Debug.LogException(ex); } } } } } if (GizmosSettings.DisplayAgentOwnedTiles) { if (Application.IsPlaying(this)) { if (Map.Initialized) { Gizmos.color = Color.cyan; if (Map.BaseMap.AgentOwnershipMap != null) { for (int x = 0; x < Map.SetSettings.MapSettings.MapWidth; x++) { for (int y = 0; y < Map.SetSettings.MapSettings.MapHeight; y++) { try { if (Map.BaseMap.AgentOwnershipMap.Tiles[x,y] != 0) { Vector3 TilePosition = GetTileWorldIndexCenter(x, y, Map); TilePosition.y += 0.01f; Vector3 Size = Vector3.one * Map.SetSettings.MapSettings.TileSize / 100f; Size.y = 0.01f; Gizmos.DrawCube(TilePosition, Size); } } catch (Exception ex) { Debug.LogException(ex); } } } } } } } } } if (GizmosSettings.DisplayBridges) { Gizmos.color = Color.blue; for (int i = 0; i < Bridges.Count; i++) { if (ValidBridge(Bridges[i])) { Gizmos.color = Color.blue; } else { Gizmos.color = Color.red; } for (int j = 1; j < Bridges[i].MidPoints.Count; j++) { // Figure out positions Vector3 MidPoint = new Vector3(Bridges[i].MidPoints[j].x, Bridges[i].MidPoints[j].y, Bridges[i].MidPoints[j].z) / 100f; Vector3 PreviousMidPoint = new Vector3(Bridges[i].MidPoints[j - 1].x, Bridges[i].MidPoints[j - 1].y, Bridges[i].MidPoints[j - 1].z) / 100f; // Draw Map Position Gizmos.DrawLine(MidPoint, PreviousMidPoint); } } if(GizmosSettings.DisplayBridgeDirections) { for (int i = 0; i < Bridges.Count; i++) { if (!ValidBridge(Bridges[i])) { continue; } if (Bridges[i].WalkableDirections == BridgeDirections.A_To_B) { Vector3 StartPoint = new Vector3(Bridges[i].MidPoints[0].x, Bridges[i].MidPoints[0].y, Bridges[i].MidPoints[0].z) / 100f; Vector3 EndPoint = new Vector3(Bridges[i].MidPoints[Bridges[i].MidPoints.Count - 1].x, Bridges[i].MidPoints[Bridges[i].MidPoints.Count - 1].y, Bridges[i].MidPoints[Bridges[i].MidPoints.Count - 1].z) / 100f; Gizmos.color = Color.red; Gizmos.DrawSphere(StartPoint, 0.5f); Gizmos.color = Color.blue; Gizmos.DrawSphere(EndPoint, 0.5f); } else if (Bridges[i].WalkableDirections == BridgeDirections.B_To_A) { Vector3 StartPoint = new Vector3(Bridges[i].MidPoints[0].x, Bridges[i].MidPoints[0].y, Bridges[i].MidPoints[0].z) / 100f; Vector3 EndPoint = new Vector3(Bridges[i].MidPoints[Bridges[i].MidPoints.Count - 1].x, Bridges[i].MidPoints[Bridges[i].MidPoints.Count - 1].y, Bridges[i].MidPoints[Bridges[i].MidPoints.Count - 1].z) / 100f; Gizmos.color = Color.blue; Gizmos.DrawSphere(StartPoint, 0.5f); Gizmos.color = Color.red; Gizmos.DrawSphere(EndPoint, 0.5f); } } } if (AttemptedBridge != null) { if (ValidBridge(AttemptedBridge)) { Gizmos.color = Color.green; } else { Gizmos.color = Color.yellow; } for (int j = 1; j < AttemptedBridge.MidPoints.Count; j++) { // Figure out positions Vector3 MidPoint = new Vector3(AttemptedBridge.MidPoints[j].x, AttemptedBridge.MidPoints[j].y, AttemptedBridge.MidPoints[j].z) / 100f; Vector3 PreviousMidPoint = new Vector3(AttemptedBridge.MidPoints[j - 1].x, AttemptedBridge.MidPoints[j - 1].y, AttemptedBridge.MidPoints[j - 1].z) / 100f; // Draw Map Position Gizmos.DrawLine(MidPoint, PreviousMidPoint); } } } else { if (Application.IsPlaying(this)) { return; } foreach (MapBridge Bridge in Bridges) { ValidateBridge(Bridge); } } if (MapSeamSettings.DisplaySeams) { //Gizmos.color = Color.green; foreach(MapSeam Seam in Seams) { Vector3 MapASize = new Vector3(); MapASize.y = 0.1f; Vector3 MapAPosition = new Vector3(); MapAPosition.y = Seam.MapA.SetSettings.MapSettings.Offset.y / 100f; if (Seam.MapA_ConectionSide == SeamConnectionSide.South || Seam.MapA_ConectionSide == SeamConnectionSide.North) { MapASize.z = Seam.MapA.SetSettings.MapSettings.TileSize / 100f; MapASize.x = Seam.MapA.SetSettings.MapSettings.TileSize * Seam.MapA.SetSettings.MapSettings.MapWidth / 100f; MapAPosition.x = MapASize.x / 2 + Seam.MapA.SetSettings.MapSettings.Offset.x / 100f; MapAPosition.z = MapASize.z / 2 + Seam.MapA.SetSettings.MapSettings.Offset.z / 100f; if (Seam.MapA_ConectionSide == SeamConnectionSide.North) { MapAPosition.z += Seam.MapA.SetSettings.MapSettings.TileSize * (Seam.MapB.SetSettings.MapSettings.MapHeight - 1) / 100f; } } else { MapASize.x = Seam.MapA.SetSettings.MapSettings.TileSize / 100f; MapASize.z = Seam.MapA.SetSettings.MapSettings.TileSize * Seam.MapA.SetSettings.MapSettings.MapHeight / 100f; MapAPosition.x = MapASize.x / 2 + Seam.MapA.SetSettings.MapSettings.Offset.x / 100f; MapAPosition.z = MapASize.z / 2 + Seam.MapA.SetSettings.MapSettings.Offset.z / 100f; if (Seam.MapA_ConectionSide == SeamConnectionSide.East) { MapAPosition.x += Seam.MapA.SetSettings.MapSettings.TileSize * (Seam.MapA.SetSettings.MapSettings.MapWidth - 1) / 100f; } } Vector3 MapBSize = new Vector3(); MapBSize.y = 0.1f; Vector3 MapBPosition = new Vector3(); if (Seam.MapB_ConectionSide == SeamConnectionSide.South || Seam.MapB_ConectionSide == SeamConnectionSide.North) { MapBSize.z = Seam.MapB.SetSettings.MapSettings.TileSize / 100f; MapBSize.x = Seam.MapB.SetSettings.MapSettings.TileSize * Seam.MapB.SetSettings.MapSettings.MapWidth / 100f; MapBPosition.x = MapBSize.x / 2 + Seam.MapB.SetSettings.MapSettings.Offset.x / 100f; MapBPosition.z = MapBSize.z / 2 + Seam.MapB.SetSettings.MapSettings.Offset.z / 100f; if (Seam.MapB_ConectionSide == SeamConnectionSide.North) { MapBPosition.z += Seam.MapB.SetSettings.MapSettings.TileSize * (Seam.MapB.SetSettings.MapSettings.MapHeight - 1) / 100f; } } else { MapBSize.x = Seam.MapB.SetSettings.MapSettings.TileSize / 100f; MapBSize.z = Seam.MapB.SetSettings.MapSettings.TileSize * Seam.MapB.SetSettings.MapSettings.MapHeight / 100f; MapBPosition.x = MapASize.x / 2 + Seam.MapB.SetSettings.MapSettings.Offset.x / 100f; MapBPosition.z = MapASize.z / 2 + Seam.MapB.SetSettings.MapSettings.Offset.z / 100f; if (Seam.MapB_ConectionSide == SeamConnectionSide.East) { MapBPosition.x += Seam.MapB.SetSettings.MapSettings.TileSize * (Seam.MapA.SetSettings.MapSettings.MapWidth - 1) / 100f; } } Gizmos.color = Color.blue; Gizmos.DrawCube(MapAPosition, MapASize); Gizmos.color = Color.yellow; Gizmos.DrawCube(MapBPosition, MapBSize); Gizmos.color = Color.blue; Gizmos.DrawLine(MapAPosition, MapBPosition); } } if (Application.IsPlaying(this)) { return; } if (GizmosSettings.DisplayPaintBrush) { Event e = Event.current; Vector3 mousePos = e.mousePosition; float ppp = EditorGUIUtility.pixelsPerPoint; if (SceneView.currentDrawingSceneView == null || SceneView.currentDrawingSceneView.camera == null) { return; } mousePos.y = SceneView.currentDrawingSceneView.camera.pixelHeight - mousePos.y * ppp; mousePos.x *= ppp; //Debug.Log($"Try To Paint"); Ray ray = SceneView.currentDrawingSceneView.camera.ScreenPointToRay(mousePos); MapSet ClosestMapSet = null; float ClosestScore = float.MaxValue; Vector3 ClosestPosition = new Vector3(); int NewIndex = 0; foreach (MapSet Set in MapSets) { if (GizmosSettings.DisplayMapGizmos_ForMapIndecies.Count > 0 && !GizmosSettings.DisplayMapGizmos_ForMapIndecies.Contains(NewIndex)) { continue; } NewIndex++; Plane MapPlane = new Plane(Vector3.up, new Vector3(Set.SetSettings.MapSettings.Offset.x, Set.SetSettings.MapSettings.Offset.y, Set.SetSettings.MapSettings.Offset.z) / 100f); if (MapPlane.Raycast(ray, out float Enter)) { Vector3 HitPosition = ray.GetPoint(Enter); if (ValidPoint(HitPosition, Set)) { //Debug.Log($"Scored a Hit at Position {HitPosition} on map {Set.SetSettings.Name} - Score {Enter}"); if (Enter < ClosestScore) { ClosestMapSet = Set; ClosestScore = Enter; ClosestPosition = HitPosition; } } } } if (ClosestMapSet != null) { //Debug.Log("Show Paint Brush"); Vector3 Size = (ClosestMapSet.SetSettings.MapSettings.TileSize + ClosestMapSet.SetSettings.MapSettings.TileSize * Painter.CurrentPainterRadius * 2) * Vector3.one; Size /= 100f; Size.y = 1; Size.x -= 0.2f * ClosestMapSet.SetSettings.MapSettings.TileSize / 100f; Size.z -= 0.2f * ClosestMapSet.SetSettings.MapSettings.TileSize / 100f; Vector3 PainterPosition = ClosestPosition + Vector3.up; PainterPosition = GetTileCenterWorldPosition(PainterPosition, ClosestMapSet); Color BrushColor = Color.white; if (Painter.CurrentPainterType == TileTypes.Standard || Painter.CurrentPainterType == TileTypes.Free) { BrushColor = Color.green; } else if (Painter.CurrentPainterType == TileTypes.Blocked || Painter.CurrentPainterType == TileTypes.Void) { BrushColor = Color.black; } else if (Painter.CurrentPainterType == TileTypes.Obstacle) { BrushColor = Color.red; } else if (Painter.CurrentPainterType == TileTypes.Water) { BrushColor = Color.blue; } BrushColor.a = 0.5f; Gizmos.color = BrushColor; if (GizmosSettings.DebugPaintBrushPosition) { IntVector3 WorldTileIndex = GetTileWorldPosition(PainterPosition); IntVector2 TileIndex = ClosestMapSet.GetMapTileIndex(WorldTileIndex); Debug.Log($"Brush Position Data: Tile Center({PainterPosition.x}, {PainterPosition.y}, {PainterPosition.z}), Tile Index({TileIndex.x}, {TileIndex.y})"); } if (Painter.PainterType == PainterTypes.Square) { Gizmos.DrawCube(PainterPosition, Size); //if (SquarePaintBrush == null) //{ // GameObject PrimativeMeshObject = GameObject.CreatePrimitive(PrimitiveType.Cube); // SquarePaintBrush = PrimativeMeshObject.GetComponent(); // _squareFilter = PrimativeMeshObject.GetComponent(); // Collider collider = PrimativeMeshObject.GetComponent(); // collider.enabled = false; // PrimativeMeshObject.layer = 5; // SquarePaintBrush.transform.SetParent(transform); // SquarePaintBrush.gameObject.name = "Square Brush"; // DestroyImmediate(collider); // SquarePaintBrush.transform.position = Vector3.up * 1000000; //} //SquarePaintBrush.sharedMaterial.color = BrushColor; //SquarePaintBrush.transform.position = PainterPosition; //SquarePaintBrush.transform.localScale = Size; //Gizmos.DrawMesh(_squareFilter.sharedMesh, SquarePaintBrush.subMeshStartIndex, PainterPosition, Quaternion.identity, Size); } else { if (Painter.CirclePaintBrush == null) { GameObject PrimativeMeshObject = GameObject.CreatePrimitive(PrimitiveType.Cylinder); Painter.CirclePaintBrush = PrimativeMeshObject.GetComponent(); Painter.CircleFilter = PrimativeMeshObject.GetComponent(); Collider collider = PrimativeMeshObject.GetComponent(); PrimativeMeshObject.layer = 5; //CirclePaintBrush.transform.SetParent(transform); Painter.CirclePaintBrush.gameObject.name = "Circle Brush"; DestroyImmediate(collider); Painter.CirclePaintBrush.transform.position = Vector3.up * 1000000; } //CirclePaintBrush.sharedMaterial.color = BrushColor; //CirclePaintBrush.transform.position = PainterPosition; //CirclePaintBrush.transform.localScale = Size; Gizmos.DrawMesh(Painter.CircleFilter.sharedMesh, Painter.CirclePaintBrush.subMeshStartIndex, PainterPosition, Quaternion.identity, Size); } } } #endif } /// /// Saves all the maps for the current scene /// public void SaveMaps() { if (Application.isPlaying) { return; } JsonSerializerSettings setting = new JsonSerializerSettings { Formatting = Formatting.None, ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; string BasePath = Application.dataPath + ManagerSettings.MapSaveDataFolder; if (string.IsNullOrEmpty(ManagerSettings.MapSaveDataFolder) || ManagerSettings.MapSaveDataFolder == "None" || !Directory.Exists(BasePath)) { if (!Directory.Exists(BasePath) && ManagerSettings.MapSaveDataFolder != "None" && !string.IsNullOrEmpty(ManagerSettings.MapSaveDataFolder)) { Debug.Log($"Save -> Base Path not Valid: FULL: '{BasePath}' -Partial: '{ManagerSettings.MapSaveDataFolder}'"); } BasePath = Application.streamingAssetsPath + "/Saved Maps/"; if (!Directory.Exists(Application.streamingAssetsPath)) { Directory.CreateDirectory(Application.streamingAssetsPath); } if (!Directory.Exists(Application.streamingAssetsPath + "/Saved Maps/")) { Directory.CreateDirectory(Application.streamingAssetsPath + "/Saved Maps/"); } } foreach (MapSet MapSet in _mapSets) { MapSet.SetSettings.MapSettings.MapInfo.MakeSureSizeIsRight(); string path = Path.Combine(BasePath, $"Scene_({SceneManager.GetActiveScene().name})-Map_({MapSet.SetSettings.Name}).json"); string json = JsonConvert.SerializeObject(MapSet, setting); if (File.Exists(path)) { File.Delete(path); } File.WriteAllText(path, json); } } /// /// Load all the maps for the current scene /// public void LoadMaps() { if (Application.isPlaying) { return; } //Debug.Log("Load Maps"); JsonSerializerSettings setting = new JsonSerializerSettings { Formatting = Formatting.None, ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; string BasePath = Application.dataPath + ManagerSettings.MapSaveDataFolder; if (string.IsNullOrEmpty(ManagerSettings.MapSaveDataFolder) || ManagerSettings.MapSaveDataFolder == "None" || !Directory.Exists(BasePath)) { if (!Directory.Exists(BasePath) && ManagerSettings.MapSaveDataFolder != "None" && !string.IsNullOrEmpty(ManagerSettings.MapSaveDataFolder)) { Debug.Log($"Load -> Base Path not Valid: FULL: '{BasePath}' -Partial: '{ManagerSettings.MapSaveDataFolder}'"); } BasePath = Application.streamingAssetsPath + "/Saved Maps/"; } List NewMapSet = new List(); foreach (MapSet MapSet in _mapSets) { string path = Path.Combine(BasePath, $"Scene_({SceneManager.GetActiveScene().name})-Map_({MapSet.SetSettings.Name}).json"); if (File.Exists(path)) { string fileContent = File.ReadAllText(path); MapSet SavedMap = JsonConvert.DeserializeObject(fileContent); NewMapSet.Add(SavedMap); } else { NewMapSet.Add(MapSet); } } _mapSets.Clear(); _mapSets = NewMapSet; } /// /// Load all the maps for the current scene and puts them in the list /// /// protected void LoadMaps(List NewList) { JsonSerializerSettings setting = new JsonSerializerSettings { Formatting = Formatting.None, ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; string BasePath = Application.dataPath + ManagerSettings.MapSaveDataFolder; if (string.IsNullOrEmpty(ManagerSettings.MapSaveDataFolder) || ManagerSettings.MapSaveDataFolder == "None" || !Directory.Exists(BasePath)) { if (!Directory.Exists(BasePath) && ManagerSettings.MapSaveDataFolder != "None" && !string.IsNullOrEmpty(ManagerSettings.MapSaveDataFolder)) { Debug.Log($"Load -> Base Path not Valid: FULL: '{BasePath}' -Partial: '{ManagerSettings.MapSaveDataFolder}'"); } BasePath = Application.streamingAssetsPath + "/Saved Maps/"; } foreach (MapSet MapSet in _mapSets) { string path = Path.Combine(BasePath, $"Scene_({SceneManager.GetActiveScene().name})-Map_({MapSet.SetSettings.Name}).json"); if (File.Exists(path)) { string fileContent = File.ReadAllText(path); MapSet SavedMap = JsonConvert.DeserializeObject(fileContent); NewList.Add(SavedMap); } else { NewList.Add(MapSet); } } } #endregion #region Updates public void UpdateThread0() { try { while (Running) { Thread.Sleep(1); Parallel.ForEach(_activeSets, _parallelOptions, Update0); } } catch (Exception e) { Debug.Log($"Error Caught {e}"); } } public void Update0(MapSet Map) { if (Map.Initialized) { Map.RedundantMaps[0].Update(); } } public void UpdateThread1() { try { while (Running) { Thread.Sleep(1); Parallel.ForEach(_activeSets, _parallelOptions, Update1); } } catch (Exception e) { Debug.Log($"Error Caught {e}"); } } public void Update1(MapSet Map) { if (Map.Initialized) { Map.RedundantMaps[1].Update(); } } public void UpdateThread2() { try { while (Running) { Thread.Sleep(1); Parallel.ForEach(_activeSets, _parallelOptions, Update2); } } catch (Exception e) { Debug.Log($"Error Caught {e}"); } } public void Update2(MapSet Map) { if (Map.Initialized) { Map.RedundantMaps[2].Update(); } } public void UpdateThread3() { try { while (Running) { Thread.Sleep(1); Parallel.ForEach(_activeSets, _parallelOptions, Update3); } } catch (Exception e) { Debug.Log($"Error Caught {e}"); } } public void Update3(MapSet Map) { if (Map.Initialized) { Map.RedundantMaps[3].Update(); } } public void UpdateThread4() { try { while (Running) { Thread.Sleep(1); Parallel.ForEach(_activeSets, _parallelOptions, Update4); } } catch (Exception e) { Debug.Log($"Error Caught {e}"); } } public void Update4(MapSet Map) { if (Map.Initialized) { Map.RedundantMaps[4].Update(); } } public void UpdateThread5() { try { while (Running) { Thread.Sleep(1); Parallel.ForEach(_activeSets, _parallelOptions, Update5); } } catch (Exception e) { Debug.Log($"Error Caught {e}"); } } public void Update5(MapSet Map) { if (Map.Initialized) { Map.RedundantMaps[5].Update(); } } public void UpdateThread6() { try { while (Running) { Thread.Sleep(1); Parallel.ForEach(_activeSets, _parallelOptions, Update6); } } catch (Exception e) { Debug.Log($"Error Caught {e}"); } } public void Update6(MapSet Map) { if (Map.Initialized) { Map.RedundantMaps[6].Update(); } } public void UpdateThread7() { try { while (Running) { Thread.Sleep(1); Parallel.ForEach(_activeSets, _parallelOptions, Update7); } } catch (Exception e) { Debug.Log($"Error Caught {e}"); } } public void Update7(MapSet Map) { if (Map.Initialized) { Map.RedundantMaps[7].Update(); } } public void UpdateThread8() { try { while (Running) { Thread.Sleep(1); Parallel.ForEach(_activeSets, _parallelOptions, Update8); } } catch (Exception e) { Debug.Log($"Error Caught {e}"); } } public void Update8(MapSet Map) { if (Map.Initialized) { Map.RedundantMaps[8].Update(); } } public void UpdateThread9() { try { while (Running) { Thread.Sleep(1); Parallel.ForEach(_activeSets, _parallelOptions, Update9); } } catch (Exception e) { Debug.Log($"Error Caught {e}"); } } public void Update9(MapSet Map) { if (Map.Initialized) { Map.RedundantMaps[9].Update(); } } #endregion #region Extra Simulation /// /// Dictionary of all the simulation Agent /// public Dictionary SimulationAgents { get; protected set; } /// /// List of all the simulation agents /// public List SimulationAgentList { get; protected set; } /// /// Stopwatch to keep track of the helper update /// protected System.Diagnostics.Stopwatch HelperStopwatch; /// /// The Helper threads target period in ms /// public long HelperTargetPeriod { get; set; } /// /// The Helper threads target period in sec /// public float HelperTargetDeltaTime { get; set; } /// /// The Helper threads Delta Time of the last frame /// public float HelperDeltaTime { get; protected set; } /// /// This helper is used to run the simulation agents and the agent avoidence /// public void SimulationHelperUpdate() { HelperRunning = true; HelperStopwatch = new System.Diagnostics.Stopwatch(); HelperTargetPeriod = (int)MathF.Ceiling(1000f / ManagerSettings.SimulationSettings.DesiredHelperTickrate); HelperTargetDeltaTime = HelperTargetPeriod / 1000f; while (HelperRunning) { try { HelperStopwatch.Start(); // Update HelperUpdate(); // Wait Section HelperStopwatch.Stop(); HelperDeltaTime = HelperStopwatch.ElapsedMilliseconds / 1000f; if (HelperStopwatch.ElapsedMilliseconds < HelperTargetPeriod) { HelperDeltaTime = HelperTargetDeltaTime; int SleepPeriod = (int)(HelperTargetPeriod - HelperStopwatch.ElapsedMilliseconds); Thread.Sleep(SleepPeriod); } else { HelperDeltaTime = HelperStopwatch.ElapsedMilliseconds / 1000f; } HelperStopwatch.Reset(); } catch (Exception e) { lock (Errors) { Errors.Add($"{e.Message} \n --------------------------------- \n {e.StackTrace}"); } } } } /// /// The main update section of the simulation helper update. Removed this section for clarity and ease of use /// protected virtual void HelperUpdate() { lock (SimulationAgents) { for (int i = 0; i < SimulationAgentList.Count; i++) { SimulationAgentList[i].Update(); } } for (int i = 0; i < MapSets.Count; i++) { MapSets[i].ClearClaims(); } } #endregion #region Helpers /// /// Gets the tile world index center for a specific map /// /// /// /// /// private Vector3 GetTileWorldIndexCenter(int x, int y, MapSet map) { Vector3 NewPosition = new Vector3(); NewPosition.x = x * map.SetSettings.MapSettings.TileSize + map.SetSettings.MapSettings.TileSize / 2f + map.SetSettings.MapSettings.Offset.x; NewPosition.y = map.SetSettings.MapSettings.Offset.y; NewPosition.z = y * map.SetSettings.MapSettings.TileSize + map.SetSettings.MapSettings.TileSize / 2f + map.SetSettings.MapSettings.Offset.z; NewPosition /= 100f; return NewPosition; } /// /// Checks to see if any point of a circle is on a specific map /// /// /// /// /// internal bool CircleOnMap(MapSet set, Vector3 newPosition, float radius) { IntVector3 WorldTileIndex = GetTileWorldPosition(newPosition); IntVector2 TileIndex = set.GetMapTileIndex(WorldTileIndex); int IntRadius = Mathf.FloorToInt(radius * 100 / set.SetSettings.MapSettings.TileSize); IntVector2 RadiusVector = new IntVector2(IntRadius, IntRadius); IntVector2 BottomLeftTileIndex = set.GetMapTileIndex(set.BottomLeftPosition) - RadiusVector; IntVector2 TopRightTileIndex = set.GetMapTileIndex(set.TopRightPosition) + RadiusVector; if (TileIndex.x >= BottomLeftTileIndex.x && TileIndex.x <= TopRightTileIndex.x && TileIndex.y >= BottomLeftTileIndex.y && TileIndex.y <= TopRightTileIndex.y) { return true; } return false; } /// /// Checks to see if any point of a rectangle is on a specific map /// /// /// /// /// internal bool RectOnMap(MapSet set, PositionSquareRect newPositionRect, float HeightCheck) { if (newPositionRect.Corners[0].y * 100 < set.SetSettings.MapSettings.Offset.y || (newPositionRect.Corners[0].y - HeightCheck) * 100 > set.SetSettings.MapSettings.Offset.y) { return false; } foreach(Vector3 Corner in newPositionRect.Corners) { if (set.PointInMap(GetTileWorldPosition(Corner))) { return true; } } return false; } /// /// Get the best map at a world tile index. Can be NULL /// /// /// public MapSet GetMapAtTileWorldPosition(IntVector3 intVector3) { List PotentialMaps = new List(); foreach(MapSet set in MapSets) { if (set.PointInMap(intVector3)) { PotentialMaps.Add(set); } } if (PotentialMaps.Count == 0) { return null; } MapSet ClosestMap = PotentialMaps[0]; int ClosestMapScore = Mathf.Abs(ClosestMap.SetSettings.MapSettings.Offset.y - intVector3.y); foreach (MapSet set in PotentialMaps) { int NewScore = Mathf.Abs(set.SetSettings.MapSettings.Offset.y - intVector3.y); if (NewScore < ClosestMapScore) { ClosestMap = set; ClosestMapScore = NewScore; } } return ClosestMap; } /// /// Get the best map at a world position. Can be NULL /// /// /// public MapSet GetMapAtWorldPosition(Vector3 position) { IntVector3 intWorldPosition = GetTileWorldPosition(position); List PotentialMaps = new List(); foreach (MapSet set in MapSets) { if (set.PointInMap(intWorldPosition)) { PotentialMaps.Add(set); } } if (PotentialMaps.Count == 0) { return null; } MapSet ClosestMap = PotentialMaps[0]; int ClosestMapScore = Mathf.Abs(ClosestMap.SetSettings.MapSettings.Offset.y - intWorldPosition.y); foreach (MapSet set in PotentialMaps) { int NewScore = Mathf.Abs(set.SetSettings.MapSettings.Offset.y - intWorldPosition.y); if (NewScore < ClosestMapScore) { ClosestMap = set; ClosestMapScore = NewScore; } } return ClosestMap; } /// /// Adds the Obstacle to the Manager /// /// public void AddObstacle(NavigationObstacle navigationObstacle) { if (!Obstacles.Contains(navigationObstacle)) { Obstacles.Add(navigationObstacle); } } /// /// Removes the Obstacle to the Manager /// /// public void RemoveObstacle(NavigationObstacle navigationObstacle) { if (Obstacles.Contains(navigationObstacle)) { Obstacles.Remove(navigationObstacle); } } /// /// Adds the Agent to the Manager /// /// public void AddAgent(NavigationAgent agent) { if (!Agents.Contains(agent)) { Agents.Add(agent); } } /// /// Adds the simulation Agent to the Manager /// /// public void AddSimAgent(SimNavigationAgent agent) { if (!Agents.Contains(agent)) { Agents.Add(agent); lock(SimulationAgents) { SimulationAgents.Add(agent.SimulationAgent.SimID, agent.SimulationAgent); lock (SimulationAgentList) { SimulationAgentList.Add(agent.SimulationAgent); } } } } /// /// Removes the Agent to the Manager /// /// public void RemoveAgent(NavigationAgent agent) { if (Agents.Contains(agent)) { Agents.Remove(agent); } } /// /// Removes the simulation Agent to the Manager /// /// public void RemoveSimAgent(SimNavigationAgent agent) { if (!Running) { return; } if (Agents.Contains(agent)) { Agents.Remove(agent); lock (SimulationAgents) { SimulationAgents.Remove(agent.SimulationAgent.SimID); lock (SimulationAgentList) { SimulationAgentList.Remove(agent.SimulationAgent); } } } } /// /// Checks whether a point is valid on a map /// /// /// /// public bool ValidPoint(Vector3 hitPosition, MapSet set) { if (set == null) { return false; } return set.PointInMap(new IntVector3(Mathf.FloorToInt(hitPosition.x * 100f), (int)(hitPosition.y * 100f), Mathf.FloorToInt(hitPosition.z * 100f))); } /// /// Checks whether a point is valid on a any map /// /// /// /// public bool ValidPoint(Vector3 hitPosition, out MapSet HitMap) { HitMap = null; foreach(MapSet Map in MapSets) { if (Map.PointInMap(new IntVector3(Mathf.FloorToInt(hitPosition.x * 100f), (int)(hitPosition.y * 100f), Mathf.FloorToInt(hitPosition.z * 100f)))) { HitMap = Map; } } if (HitMap == null) { return false; } return true; } /// /// Checks whether a point is valid on a map /// /// /// /// public bool ValidPoint(IntVector3 hitWorldTilePosition, MapSet set) { return set.PointInMap(hitWorldTilePosition); } /// /// Get the world tile index from a position /// /// /// public IntVector3 GetTileWorldPosition(Vector3 hitPosition) { return new IntVector3(Mathf.FloorToInt(hitPosition.x * 100), Mathf.FloorToInt(hitPosition.y * 100), Mathf.FloorToInt(hitPosition.z * 100)); } /// /// Get the position world tile index from a world tile index /// /// /// public Vector3 GetTileWorldPosition(IntVector3 tilePosition) { return new Vector3(tilePosition.x / 100f, tilePosition.y / 100f, tilePosition.z / 100f); } /// /// Gets the tile index center position from a position /// /// /// /// public Vector3 GetTileCenterWorldPosition(Vector3 hitRoughPosition, MapSet Map) { IntVector3 GetTilePosition = GetTileWorldPosition(hitRoughPosition); IntVector2 GetTileIndex = Map.GetMapTileIndex(GetTilePosition); return GetTileCenterWorldPosition(GetTileIndex, Map); } /// /// Gets the tile index center position from a map tile index /// /// /// /// public Vector3 GetTileCenterWorldPosition(IntVector2 mapTileIndex, MapSet Map) { return new Vector3(Map.SetSettings.MapSettings.Offset.x + mapTileIndex.x * Map.SetSettings.MapSettings.TileSize + Map.SetSettings.MapSettings.TileSize / 2f, Map.SetSettings.MapSettings.Offset.y, Map.SetSettings.MapSettings.Offset.z + mapTileIndex.y * Map.SetSettings.MapSettings.TileSize + Map.SetSettings.MapSettings.TileSize / 2f) / 100f; } /// /// Gets the tile index center position from a world tile index /// /// /// /// public Vector3 GetTileCenterWorldPosition(IntVector3 worldTileIndex, MapSet Map) { IntVector3 FlattenedWorldTileIndex = Map.GetWorldTileIndex(Map.GetMapTileIndex(worldTileIndex), false); // Set it to the corner position return new Vector3(FlattenedWorldTileIndex.x + Map.SetSettings.MapSettings.TileSize / 2f, (int)FlattenedWorldTileIndex.y, FlattenedWorldTileIndex.z + Map.SetSettings.MapSettings.TileSize / 2f) / 100f; } /// /// Gets the world tile index center position from a world tile index /// /// /// /// public Vector3 GetTileCenterWorldPosition(IntVector3 worldTileIndex) { return new Vector3(Mathf.FloorToInt(worldTileIndex.x), (int)worldTileIndex.y, Mathf.FloorToInt(worldTileIndex.z)) / 100f; } /// /// Gets the tile index from a world tile index /// /// /// /// public IntVector2 GetTileIndexForMap(IntVector3 TileWorldIndex, MapSet Map) { if (Map == null) { return new IntVector2(); } return Map.GetMapTileIndex(TileWorldIndex); } /// /// Gets the tile index from a position /// /// /// /// public IntVector2 GetTileIndexForMap(Vector3 WorldPosition, MapSet Map) { IntVector3 WorldTileIndex = GetTileWorldPosition(WorldPosition); return Map.GetMapTileIndex(WorldTileIndex); } /// /// Gets the tile type from a position /// /// /// /// public TileTypes GetTileTypeForMap(Vector3 WorldPosition, MapSet Map) { IntVector3 WorldTileIndex = GetTileWorldPosition(WorldPosition); return Map.GetTileType(Map.GetMapTileIndex(WorldTileIndex)); } /// /// Gets the tile type from a tile index /// /// /// /// public TileTypes GetTileTypeForMap(IntVector2 MapIndex, MapSet Map) { if (Map == null) { return TileTypes.OutOfBounds; } return Map.GetTileType(MapIndex); } /// /// Gets the tile type from a tile index /// /// /// /// /// public TileTypes GetTileTypeForMap(int x, int y, MapSet Map) { return Map.GetTileType(x, y); } /// /// Get the tile bounds of a map /// /// /// /// /// public TileBounds GetTileBoundsForMap(int x, int y, MapSet Map) { Vector3 TilePosition = GetTileWorldPosition(Map.GetWorldTileIndex(x, y)); return new TileBounds(TilePosition, Map.SetSettings.MapSettings.TileSize / 100f); } /// /// Get the tile bounds of a map /// /// /// /// public TileBounds GetTileBoundsForMap(IntVector2 TileIndex, MapSet Map) { Vector3 TilePosition = GetTileWorldPosition(Map.GetWorldTileIndex(TileIndex)); return new TileBounds(TilePosition, Map.SetSettings.MapSettings.TileSize / 100f); } /// /// Get a random valid position in the scene /// /// /// /// public Vector3 GetRandomValidPosition(List ValidTypes, bool PlaceAtCenter = false) { if (MapSets.Count > 0) { MapSet RandomMap = MapSets[RandomValidPosition.Next(0, MapSets.Count)]; return GetRandomValidPosition(RandomMap, ValidTypes, PlaceAtCenter); } return new Vector3(); } /// /// Get a random valid position oon this map /// /// /// /// /// public Vector3 GetRandomValidPosition(List ValidTypes, MapSet Map, bool PlaceAtCenter = false) { return GetRandomValidPosition(Map, ValidTypes, PlaceAtCenter); } /// /// Get a random position in range of a position /// /// /// /// /// /// public Vector3 GetRandomValidPositionAtPosition(List ValidTypes, IntVector2 Position, int Range = 30, bool PlaceAtCenter = false) { if (MapSets.Count > 0) { MapSet RandomMap = MapSets[RandomValidPosition.Next(0, MapSets.Count - 1)]; return GetRandomValidPositionAtPosition(RandomMap, ValidTypes, Position, Range, PlaceAtCenter); } return new Vector3(); } protected System.Random RandomValidPosition = new System.Random(33399927); /// /// get a random valid position for a map /// /// /// /// /// public Vector3 GetRandomValidPosition(MapSet Map, List ValidTypes, bool PlaceAtCenter) { if (Map == null) { throw new Exception($"Map Is Null"); } if (Map.SetSettings == null) { throw new Exception($"1. Map Settings Is Null"); } if (Map.SetSettings.MapSettings == null) { throw new Exception($"2. Map Settings Is Null"); } IntVector2 RandomIndex = new IntVector2(RandomValidPosition.Next(0, Map.SetSettings.MapSettings.MapWidth - 1), RandomValidPosition.Next(0, Map.SetSettings.MapSettings.MapHeight - 1)); IntVector3 WorldIndex; if(!ValidTypes.Contains(Map.GetTileType(RandomIndex))) { int CurrentRadius = 0; IntVector2 NewIndex; while (!ValidTypes.Contains(Map.GetTileType(RandomIndex)) && CurrentRadius < 103) { CurrentRadius++; NewIndex = RandomIndex; NewIndex.x += CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.x -= CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.y += CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.y -= CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.x += CurrentRadius; NewIndex.y += CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.x -= CurrentRadius; NewIndex.y += CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.x -= CurrentRadius; NewIndex.y -= CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.x += CurrentRadius; NewIndex.y -= CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } } } WorldIndex = Map.GetWorldTileIndex(RandomIndex, false); if (PlaceAtCenter) { Vector3 RandomPosition = new Vector3(WorldIndex.x + Map.SetSettings.MapSettings.TileSize / 2f, WorldIndex.y, WorldIndex.z + Map.SetSettings.MapSettings.TileSize / 2f) / 100f; //Debug.Log($"Random Index ({RandomIndex.x}, {RandomIndex.y}) -> World Index ({WorldIndex.x}, {WorldIndex.y}, {WorldIndex.z}) -> World Position ({RandomPosition.x}, {RandomPosition.y}, {RandomPosition.z})"); return RandomPosition; } return new Vector3(WorldIndex.x, WorldIndex.y, WorldIndex.z) / 100f; } /// /// Get a random valid position at a position within a range /// /// /// /// /// /// /// public Vector3 GetRandomValidPositionAtPosition(MapSet Map, List ValidTypes, IntVector2 Position, int Range = 30, bool PlaceAtCenter = false) { IntVector2 LowBound = Position - new IntVector2(Range, Range); if (LowBound.x < 0) { LowBound.x = 0; } if (LowBound.y < 0) { LowBound.y = 0; } IntVector2 HighBound = Position + new IntVector2(Range, Range); if (HighBound.x >= Map.SetSettings.MapSettings.MapWidth) { HighBound.x = Map.SetSettings.MapSettings.MapWidth - 1; } if (HighBound.y >= Map.SetSettings.MapSettings.MapHeight) { HighBound.y = Map.SetSettings.MapSettings.MapHeight - 1; } IntVector2 RandomIndex = new IntVector2(RandomValidPosition.Next(LowBound.x, HighBound.x), RandomValidPosition.Next(LowBound.y, HighBound.y)); IntVector3 WorldIndex; if (!ValidTypes.Contains(Map.GetTileType(RandomIndex))) { int CurrentRadius = 0; IntVector2 NewIndex; while (!ValidTypes.Contains(Map.GetTileType(RandomIndex)) && CurrentRadius < 103) { CurrentRadius++; NewIndex = RandomIndex; NewIndex.x += CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.x -= CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.y += CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.y -= CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.x += CurrentRadius; NewIndex.y += CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.x -= CurrentRadius; NewIndex.y += CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.x -= CurrentRadius; NewIndex.y -= CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } NewIndex = RandomIndex; NewIndex.x += CurrentRadius; NewIndex.y -= CurrentRadius; if (ValidTypes.Contains(Map.GetTileType(NewIndex))) { RandomIndex = NewIndex; break; } } } WorldIndex = Map.GetWorldTileIndex(RandomIndex, false); if (PlaceAtCenter) { Vector3 RandomPosition = new Vector3(WorldIndex.x + Map.SetSettings.MapSettings.TileSize / 2f, WorldIndex.y, WorldIndex.z + Map.SetSettings.MapSettings.TileSize / 2f) / 100f; //Debug.Log($"Random Index ({RandomIndex.x}, {RandomIndex.y}) -> World Index ({WorldIndex.x}, {WorldIndex.y}, {WorldIndex.z}) -> World Position ({RandomPosition.x}, {RandomPosition.y}, {RandomPosition.z})"); return RandomPosition; } return new Vector3(WorldIndex.x, WorldIndex.y, WorldIndex.z) / 100f; } public Vector3 GetRandomPosition() { if (MapSets.Count > 0) { MapSet RandomMap = MapSets[RandomValidPosition.Next(0, MapSets.Count)]; return GetRandomPosition(RandomMap); } return new Vector3(); } /// /// Get a random position on a map /// /// /// public Vector3 GetRandomPosition(MapSet Map) { IntVector2 RandomIndex = new IntVector2(RandomValidPosition.Next(0, Map.SetSettings.MapSettings.MapWidth - 1), RandomValidPosition.Next(0, Map.SetSettings.MapSettings.MapHeight - 1)); IntVector3 WorldIndex = Map.GetWorldTileIndex(RandomIndex); return new Vector3(WorldIndex.x, WorldIndex.y, WorldIndex.z); } /// /// Gets the best map from a position /// /// /// public MapSet GetCurrentMap(Vector3 WorldPosition) { if (MapSets.Count <= 0) { return null; } IntVector3 WorltIileIndex = GetTileWorldPosition(WorldPosition); MapSet ClosestMap = null; int ClosestScore = int.MaxValue; foreach (MapSet map in MapSets) { if (map.PointInMap(WorltIileIndex)) { int NewScore = WorltIileIndex.y - map.SetSettings.MapSettings.Offset.y; if (NewScore >= 0) { if (NewScore < ClosestScore) { ClosestScore = NewScore; ClosestMap = map; } } } } return ClosestMap; } #endregion } [Serializable] public struct TileBounds { public Vector3 Center; public Vector3 BottomLeft; public Vector3 TopLeft; public Vector3 BottomRight; public Vector3 TopRight; public TileBounds(Vector3 TilePosition, float TileSize) { Center = TilePosition; Center.x += TileSize / 2; Center.z += TileSize / 2; TopRight = TilePosition; TopRight.x += TileSize; TopRight.z += TileSize; BottomLeft = TilePosition; TopLeft = TilePosition; TopLeft.z += TileSize; BottomRight = TilePosition; BottomRight.x += TileSize; TopRight = TilePosition; TopRight.x += TileSize; TopRight.z += TileSize; } public bool InBounds(Vector3 Point) { if (Point.x > TopRight.x || Point.x < BottomLeft.x || Point.z > TopRight.z || Point.z < BottomLeft.z) { return false; } return true; } public bool IntersectsBounds(Vector3 PointA, Vector3 PointB) { if (InBounds(PointA) || InBounds(PointB)) { return true; } if (LineLineCollision(PointA, PointB, BottomRight, BottomLeft) || LineLineCollision(PointA, PointB, BottomRight, TopRight) || LineLineCollision(PointA, PointB, BottomLeft, TopLeft) || LineLineCollision(PointA, PointB, TopRight, TopLeft)) { return true; } return false; } public static bool LineLineCollision(Vector3 Position1Start, Vector3 Position1End, Vector3 Position2Start, Vector3 Position2End) { // Directions float uA = ((Position2End.x - Position2Start.x) * (Position1Start.z - Position2Start.z) - (Position2End.z - Position2Start.z) * (Position1Start.x - Position2Start.x)) / ((Position2End.z - Position2Start.z) * (Position1End.x - Position1Start.x) - (Position2End.x - Position2Start.x) * (Position1End.z - Position1Start.z)); float uB = ((Position1End.x - Position1Start.x) * (Position1Start.z - Position2Start.z) - (Position1End.z - Position1Start.z) * (Position1Start.x - Position2Start.x)) / ((Position2End.z - Position2Start.z) * (Position1End.x - Position1Start.x) - (Position2End.x - Position2Start.x) * (Position1End.z - Position1Start.z)); if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) { return true; } return false; } } } namespace HPJ.Presentation.NavigationManagerSettings { /// /// The Manager settings of the Navigation Manager /// [Serializable] public class ManagerSetting { /// /// The amount of redundant maps generated - More Maps = Faster Pathfinding at cost of memory /// [SerializeField] [Range(0, 8)] [Tooltip("The amount of redundant maps generated - More Maps = Faster Pathfinding at cost of memory")] public int MapRedundancy = 4; /// /// Setting used for parrellism for each maps pathfinding /// [SerializeField] [Range(2, 16)] [Tooltip("Setting used for parrellism for each maps pathfinding")] public int PathfindingParrellism = 4; /// /// The size of the grid of maps you want to create /// [Tooltip("The size of the grid of maps you want to create")] public Vector2Int MapAutoCreationGridSize = new Vector2Int(1, 1); /// /// The number of tiles in the x and z of the generated grid maps /// [Tooltip("The number of tiles in the x and z of the generated grid maps")] public Vector2Int MapAutoGridTileWidthAndHeight = new Vector2Int(1, 1); /// /// The physical size of the autogenerated tiles on the generated maps /// [Tooltip("The physical size of the autogenerated tiles on the generated maps")] public int MapAutoGridTileSize = 100; /// /// The physical offset of the bottom left map /// [Tooltip("The physical offset of the bottom left map")] public IntVector3 MapAutoGridOffset = new IntVector3(); /// /// Whether to create seams for each connected side of the autogrid /// [Tooltip("Whether to create seams for each connected side of the autogrid")] public bool MapAutoGridAutoSeam = true; /// /// The filepath for the save data for this scene /// [Tooltip("The filepath for the save data")] public string MapSaveDataFolder = "None"; /// /// Extra Simulation Settings /// public ExtraSimulationSettings SimulationSettings = new ExtraSimulationSettings(); } /// /// The Painter settings, used to paint tiles on the Maps /// [Serializable] public class PainterSetting { /// /// The Paint Brush Size /// [Range(0, 500)] public int CurrentPainterRadius = 1; /// /// The Tile Types we want to paint /// public TileTypes CurrentPainterType = TileTypes.Blocked; /// /// The Paint Brush Type /// public PainterTypes PainterType = PainterTypes.Square; /// /// Which Map do you want to clean (Change to Default Tile) /// [Tooltip("Which Map do you want to clean (Change to Default Tile)")] public int CleanMapIndex = 0; [NonSerialized] public MeshRenderer SquarePaintBrush = null; [NonSerialized] public MeshRenderer CirclePaintBrush = null; [NonSerialized] public MeshFilter SquareFilter = null; [NonSerialized] public MeshFilter CircleFilter = null; } /// /// The Auto-Painter settings, used to paint tiles on the Maps automatically /// [Serializable] public class AutoPainterSetting { /// /// What Tile Type the Auto painter turns the Tile Into /// [Tooltip("What Tile Type the Auto painter turns the Tile Into")] public TileTypes AutoPainterType = TileTypes.Water; /// /// What Layers the Raycast must HIT to turn that tile /// [Tooltip("What Layers the Raycast must HIT to turn that tile")] public LayerMask AutoPainterCheckForLayers; /// /// What Layers the Raycast must AVOID HITTING to turn that tile /// [Tooltip("What Layers the Raycast must AVOID HITTING to turn that tile")] public LayerMask AutoPainterCheckAgainstLayers; /// /// "How much the Raycast will increase its starting position from the height of the Map (Offset Y)" /// [Tooltip("How much the Raycast will increase its starting position from the height of the Map (Offset Y)")] public float AutoPainterElavationIncrease = 20; /// /// How much the Raycast will go below the height of the Map (Offset Y) /// [Tooltip("How much the Raycast will go below the height of the Map (Offset Y)")] public float AutoPainterElavationIDecrease = 100; /// /// Which Map do you want to Auto Paint /// [Tooltip("Which Map do you want to Auto Paint")] public int AutoPaintMapIndex = 0; /// /// How high above the map, where the tiles change type /// [Tooltip("How high above the map, where the tiles change type")] public float AboveMapTypeChangeHeight = 10; /// /// What Tile Type the Auto painter turns the Tile Into on height check above /// [Tooltip("What Tile Type the Auto painter turns the Tile Into on height check above")] public TileTypes AutoPainterAboveHeightType = TileTypes.Blocked; /// /// How far below the map, where the tiles change type /// [Tooltip("How far below the map, where the tiles change type")] public float BelowMapTypeChangeHeight = 10; /// /// What Tile Type the Auto painter turns the Tile Into on height check below /// [Tooltip("What Tile Type the Auto painter turns the Tile Into on height check below")] public TileTypes AutoPainterBelowHeightType = TileTypes.Blocked; } /// /// The all seam settings /// [Serializable] public class SeamSettings { /// /// Whether to display seams /// public bool DisplaySeams = false; /// /// Map 1 or A Seam Insert Settings /// public MapSeamInsertSettings MapAInsertSettings = new MapSeamInsertSettings(); /// /// Map 2 or B Seam Insert Settings /// public MapSeamInsertSettings MapBInsertSettings = new MapSeamInsertSettings(); } /// /// The map seam settings /// [Serializable] public class MapSeamInsertSettings { /// /// Map Index /// public int MapIndex = -1; /// /// Map Index /// public SeamConnectionSide ConnectionSide = SeamConnectionSide.East; } /// /// Extra Simulation Settings /// [Serializable] public class ExtraSimulationSettings { /// /// Whether to use another thread for extra simulation. Used by Simulation Agents and Agent Avoidance /// public bool UseExtraSimulation = false; /// /// Whether to use agent avoidence for the simulation agents /// public bool UseAgentAvoidance = false; /// /// The Desired tickrate of the helper update /// public int DesiredHelperTickrate = 60; } /// /// The Gizmos settings of the Navigation Manager /// [Serializable] public class GizmosSetting { /// /// Whether to show the Gizmos /// [Header("GUI")] public bool DisplayGizmos = true; /// /// Whether to diplay the bridges /// public bool DisplayBridges = false; /// /// Shows the direction the bridge allows. Nothing for both ways, otherwise direction is from Red to Blue /// public bool DisplayBridgeDirections = false; /// /// The Bridge we want in inset a midpoint on /// public int InsertBridgePointIndex = -1; /// /// Whether to display tile grid /// public bool DisplayTiles = false; /// /// Whether to display transform handle for the map /// public bool DisplayMapHandles = true; /// /// Whether to display map nale label for the map /// public bool DisplayMapLabels = true; /// /// Whether to display the base of the Maps /// public bool DisplayMapBase = true; /// /// Whether to display the Map Edge /// public bool DisplayMapEdge = true; /// /// The Alpha of the Map Base and Edge /// [Range(0, 1)] public float DisplayMapAlpha = 1; /// /// Whether to display the Map arrows on the Maps /// public bool DisplayMapArrows = true; /// /// Adjusts the Map Arrow Size /// public float DisplayArrowSize = 12; /// /// Whether to display the paint brush or not /// public bool DisplayPaintBrush = false; /// /// Whether to debug the paint brush position /// public bool DebugPaintBrushPosition = false; /// /// Whether to display the painted tiles. Painter and Auto-Painter /// public bool DisplayPaintedTiles = false; /// /// The Tiles we want to see /// public List OnlyPaintTheseTypes = new List(); /// /// The Map index we want to show the painted tiles on. -1 for all /// [Tooltip("Which Map do you want the tiles rendered, (-1 for All)")] public int DisplayPaintedTilesMapIndex = -1; /// /// limits the amount of gizmos on screen by the desired map indecies. a empty list means every map /// public List DisplayMapGizmos_ForMapIndecies = new List(); /// /// Whetehr to display the tiles owned by agents /// public bool DisplayAgentOwnedTiles = false; } }