/// <summary> /// Roughly how far away another region is by nearest node /// </summary> /// <param name="otherRegion"></param> /// <returns></returns> public float Distance(PathfindingRegion otherRegion) { // JANK var xDistance = otherRegion.OriginNode.TileRef.X - OriginNode.TileRef.X; var yDistance = otherRegion.OriginNode.TileRef.Y - OriginNode.TileRef.Y; if (xDistance > 0) { xDistance -= Width; } else if (xDistance < 0) { xDistance = Math.Abs(xDistance + otherRegion.Width); } if (yDistance > 0) { yDistance -= Height; } else if (yDistance < 0) { yDistance = Math.Abs(yDistance + otherRegion.Height); } return(PathfindingHelpers.OctileDistance(xDistance, yDistance)); }
/// <summary> /// Can the given args can traverse this region? /// </summary> /// <param name="reachableArgs"></param> /// <returns></returns> public bool RegionTraversable(ReachableArgs reachableArgs) { // The assumption is that all nodes in a region have the same pathfinding traits // As such we can just use the origin node for checking. return(PathfindingHelpers.Traversable(reachableArgs.CollisionMask, reachableArgs.Access, OriginNode)); }
/// <summary> /// Check to see if the node is a jump point (only works for cardinal directions) /// </summary> private bool IsCardinalJumpPoint(Direction direction, PathfindingNode currentNode) { PathfindingNode openNeighborOne; PathfindingNode closedNeighborOne; PathfindingNode openNeighborTwo; PathfindingNode closedNeighborTwo; switch (direction) { case Direction.North: openNeighborOne = currentNode.GetNeighbor(Direction.NorthEast); closedNeighborOne = currentNode.GetNeighbor(Direction.East); openNeighborTwo = currentNode.GetNeighbor(Direction.NorthWest); closedNeighborTwo = currentNode.GetNeighbor(Direction.West); break; case Direction.East: openNeighborOne = currentNode.GetNeighbor(Direction.NorthEast); closedNeighborOne = currentNode.GetNeighbor(Direction.North); openNeighborTwo = currentNode.GetNeighbor(Direction.SouthEast); closedNeighborTwo = currentNode.GetNeighbor(Direction.South); break; case Direction.South: openNeighborOne = currentNode.GetNeighbor(Direction.SouthEast); closedNeighborOne = currentNode.GetNeighbor(Direction.East); openNeighborTwo = currentNode.GetNeighbor(Direction.SouthWest); closedNeighborTwo = currentNode.GetNeighbor(Direction.West); break; case Direction.West: openNeighborOne = currentNode.GetNeighbor(Direction.NorthWest); closedNeighborOne = currentNode.GetNeighbor(Direction.North); openNeighborTwo = currentNode.GetNeighbor(Direction.SouthWest); closedNeighborTwo = currentNode.GetNeighbor(Direction.South); break; default: throw new ArgumentOutOfRangeException(); } if ((closedNeighborOne == null || !PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, closedNeighborOne)) && (openNeighborOne != null && PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, openNeighborOne))) { return(true); } if ((closedNeighborTwo == null || !PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, closedNeighborTwo)) && (openNeighborTwo != null && PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, openNeighborTwo))) { return(true); } return(false); }
/// <summary> /// Gets all of the tiles in range that can we access /// </summary> /// If you want Dikstra then add distances. /// Doesn't use the JobQueue as it will generally be encapsulated by other jobs /// <param name="pathfindingArgs"></param> /// <param name="range"></param> /// <param name="fromStart">Whether we traverse from the starting tile or the end tile</param> /// <returns></returns> public static IEnumerable <PathfindingNode> GetNodesInRange(PathfindingArgs pathfindingArgs, bool fromStart = true) { var pathfindingSystem = EntitySystem.Get <PathfindingSystem>(); // Don't need a priority queue given not looking for shortest path var openTiles = new Queue <PathfindingNode>(); var closedTiles = new HashSet <TileRef>(); PathfindingNode startNode; if (fromStart) { startNode = pathfindingSystem.GetNode(pathfindingArgs.Start); } else { startNode = pathfindingSystem.GetNode(pathfindingArgs.End); } PathfindingNode currentNode; openTiles.Enqueue(startNode); while (openTiles.Count > 0) { currentNode = openTiles.Dequeue(); foreach (var neighbor in currentNode.GetNeighbors()) { // No distances stored so can just check closed tiles here if (closedTiles.Contains(neighbor.TileRef)) { continue; } closedTiles.Add(currentNode.TileRef); // So currently tileCost gets the octile distance between the 2 so we'll also use that for our range check var tileCost = PathfindingHelpers.GetTileCost(pathfindingArgs, startNode, neighbor); var direction = PathfindingHelpers.RelativeDirection(neighbor, currentNode); if (tileCost == null || tileCost > pathfindingArgs.Proximity || !PathfindingHelpers.DirectionTraversable(pathfindingArgs.CollisionMask, pathfindingArgs.Access, currentNode, direction)) { continue; } openTiles.Enqueue(neighbor); yield return(neighbor); } } }
public async Task Test() { var server = StartServerDummyTicker(); server.Assert(() => { var pathfindingSystem = EntitySystem.Get <PathfindingSystem>(); var mapMan = IoCManager.Resolve <IMapManager>(); // Setup var mapId = mapMan.CreateMap(new MapId(1)); var gridId = new GridId(2); mapMan.CreateGrid(mapId, gridId); var chunkTile = mapMan.GetGrid(gridId).GetTileRef(new Vector2i(0, 0)); var chunk = pathfindingSystem.GetChunk(chunkTile); Assert.That(chunk.Nodes.Length == PathfindingChunk.ChunkSize * PathfindingChunk.ChunkSize); // Neighbors var chunkNeighbors = chunk.GetNeighbors().ToList(); Assert.That(chunkNeighbors.Count == 0); var neighborChunkTile = mapMan.GetGrid(gridId).GetTileRef(new Vector2i(PathfindingChunk.ChunkSize, PathfindingChunk.ChunkSize)); var neighborChunk = pathfindingSystem.GetChunk(neighborChunkTile); chunkNeighbors = chunk.GetNeighbors().ToList(); Assert.That(chunkNeighbors.Count == 1); // Directions Assert.That(PathfindingHelpers.RelativeDirection(neighborChunk, chunk) == Direction.NorthEast); Assert.That(PathfindingHelpers.RelativeDirection(chunk.Nodes[0, 1], chunk.Nodes[0, 0]) == Direction.North); // Nodes var node = chunk.Nodes[1, 1]; var nodeNeighbors = node.GetNeighbors().ToList(); Assert.That(nodeNeighbors.Count == 8); // Bottom-left corner with no chunk neighbor node = chunk.Nodes[0, 0]; nodeNeighbors = node.GetNeighbors().ToList(); Assert.That(nodeNeighbors.Count == 3); // Given we have 1 NE neighbor then NE corner should have 4 neighbors due to the 1 extra from the neighbor chunk node = chunk.Nodes[PathfindingChunk.ChunkSize - 1, PathfindingChunk.ChunkSize - 1]; nodeNeighbors = node.GetNeighbors().ToList(); Assert.That(nodeNeighbors.Count == 4); }); await server.WaitIdleAsync(); }
protected override async Task <Queue <TileRef>?> Process() { if (_startNode == null || _endNode == null || Status == JobStatus.Finished) { return(null); } // If we couldn't get a nearby node that's good enough if (!PathfindingHelpers.TryEndNode(ref _endNode, _pathfindingArgs)) { return(null); } var frontier = new PriorityQueue <ValueTuple <float, PathfindingNode> >(new PathfindingComparer()); var costSoFar = new Dictionary <PathfindingNode, float>(); var cameFrom = new Dictionary <PathfindingNode, PathfindingNode>(); PathfindingNode?currentNode = null; frontier.Add((0.0f, _startNode)); costSoFar[_startNode] = 0.0f; var routeFound = false; var count = 0; while (frontier.Count > 0) { // Handle whether we need to pause if we've taken too long count++; if (count % 20 == 0 && count > 0) { await SuspendIfOutOfTime(); if (_startNode == null || _endNode == null) { return(null); } } // Actual pathfinding here (_, currentNode) = frontier.Take(); if (currentNode.Equals(_endNode)) { routeFound = true; break; } foreach (var nextNode in currentNode.GetNeighbors()) { // If tile is untraversable it'll be null var tileCost = PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, nextNode); if (tileCost == null) { continue; } // So if we're going NE then that means either N or E needs to be free to actually get there var direction = PathfindingHelpers.RelativeDirection(nextNode, currentNode); if (!PathfindingHelpers.DirectionTraversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, currentNode, direction)) { continue; } // f = g + h // gScore is distance to the start node // hScore is distance to the end node var gScore = costSoFar[currentNode] + tileCost.Value; if (costSoFar.TryGetValue(nextNode, out var nextValue) && gScore >= nextValue) { continue; } cameFrom[nextNode] = currentNode; costSoFar[nextNode] = gScore; // pFactor is tie-breaker where the fscore is otherwise equal. // See http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html#breaking-ties // There's other ways to do it but future consideration // The closer the fScore is to the actual distance then the better the pathfinder will be // (i.e. somewhere between 1 and infinite) // Can use hierarchical pathfinder or whatever to improve the heuristic but this is fine for now. var fScore = gScore + PathfindingHelpers.OctileDistance(_endNode, nextNode) * (1.0f + 1.0f / 1000.0f); frontier.Add((fScore, nextNode)); } } if (!routeFound) { return(null); } DebugTools.AssertNotNull(currentNode); var route = PathfindingHelpers.ReconstructPath(cameFrom, currentNode !); if (route.Count == 1) { return(null); } #if DEBUG // Need to get data into an easier format to send to the relevant clients if (DebugRoute != null && route.Count > 0) { var debugCameFrom = new Dictionary <TileRef, TileRef>(cameFrom.Count); var debugGScores = new Dictionary <TileRef, float>(costSoFar.Count); foreach (var(node, parent) in cameFrom) { debugCameFrom.Add(node.TileRef, parent.TileRef); } foreach (var(node, score) in costSoFar) { debugGScores.Add(node.TileRef, score); } var debugRoute = new SharedAiDebug.AStarRouteDebug( _pathfindingArgs.Uid, route, debugCameFrom, debugGScores, DebugTime); DebugRoute.Invoke(debugRoute); } #endif return(route); }
/// <summary> /// Check to see if the node is a jump point (only works for cardinal directions) /// </summary> private bool IsCardinalJumpPoint(Direction direction, PathfindingNode currentNode) { PathfindingNode?openNeighborOne = null; PathfindingNode?closedNeighborOne = null; PathfindingNode?openNeighborTwo = null; PathfindingNode?closedNeighborTwo = null; switch (direction) { case Direction.North: foreach (var neighbor in currentNode.GetNeighbors()) { var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode); switch (neighborDirection) { case Direction.NorthEast: openNeighborOne = neighbor; break; case Direction.East: closedNeighborOne = neighbor; break; case Direction.NorthWest: openNeighborTwo = neighbor; break; case Direction.West: closedNeighborTwo = neighbor; break; } } break; case Direction.East: foreach (var neighbor in currentNode.GetNeighbors()) { var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode); switch (neighborDirection) { case Direction.NorthEast: openNeighborOne = neighbor; break; case Direction.North: closedNeighborOne = neighbor; break; case Direction.SouthEast: openNeighborTwo = neighbor; break; case Direction.South: closedNeighborTwo = neighbor; break; } } break; case Direction.South: foreach (var neighbor in currentNode.GetNeighbors()) { var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode); switch (neighborDirection) { case Direction.SouthEast: openNeighborOne = neighbor; break; case Direction.East: closedNeighborOne = neighbor; break; case Direction.SouthWest: openNeighborTwo = neighbor; break; case Direction.West: closedNeighborTwo = neighbor; break; } } break; case Direction.West: foreach (var neighbor in currentNode.GetNeighbors()) { var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode); switch (neighborDirection) { case Direction.NorthWest: openNeighborOne = neighbor; break; case Direction.North: closedNeighborOne = neighbor; break; case Direction.SouthWest: openNeighborTwo = neighbor; break; case Direction.South: closedNeighborTwo = neighbor; break; } } break; default: throw new ArgumentOutOfRangeException(); } if ((closedNeighborOne == null || !PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, closedNeighborOne)) && openNeighborOne != null && PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, openNeighborOne)) { return(true); } if ((closedNeighborTwo == null || !PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, closedNeighborTwo)) && openNeighborTwo != null && PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, openNeighborTwo)) { return(true); } return(false); }
protected override async Task <Queue <TileRef>?> Process() { // VERY similar to A*; main difference is with the neighbor tiles you look for jump nodes instead if (_startNode == null || _endNode == null) { return(null); } // If we couldn't get a nearby node that's good enough if (!PathfindingHelpers.TryEndNode(ref _endNode, _pathfindingArgs)) { return(null); } var openTiles = new PriorityQueue <ValueTuple <float, PathfindingNode> >(new PathfindingComparer()); var gScores = new Dictionary <PathfindingNode, float>(); var cameFrom = new Dictionary <PathfindingNode, PathfindingNode>(); var closedTiles = new HashSet <PathfindingNode>(); #if DEBUG var jumpNodes = new HashSet <PathfindingNode>(); #endif PathfindingNode?currentNode = null; openTiles.Add((0, _startNode)); gScores[_startNode] = 0.0f; var routeFound = false; var count = 0; while (openTiles.Count > 0) { count++; // JPS probably getting a lot fewer nodes than A* is if (count % 5 == 0 && count > 0) { await SuspendIfOutOfTime(); } (_, currentNode) = openTiles.Take(); if (currentNode.Equals(_endNode)) { routeFound = true; break; } foreach (var node in currentNode.GetNeighbors()) { var direction = PathfindingHelpers.RelativeDirection(node, currentNode); var jumpNode = GetJumpPoint(currentNode, direction, _endNode); if (jumpNode != null && !closedTiles.Contains(jumpNode)) { closedTiles.Add(jumpNode); #if DEBUG jumpNodes.Add(jumpNode); #endif // GetJumpPoint should already check if we can traverse to the node var tileCost = PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, jumpNode); if (tileCost == null) { throw new InvalidOperationException(); } var gScore = gScores[currentNode] + tileCost.Value; if (gScores.TryGetValue(jumpNode, out var nextValue) && gScore >= nextValue) { continue; } cameFrom[jumpNode] = currentNode; gScores[jumpNode] = gScore; // pFactor is tie-breaker where the fscore is otherwise equal. // See http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html#breaking-ties // There's other ways to do it but future consideration var fScore = gScores[jumpNode] + PathfindingHelpers.OctileDistance(_endNode, jumpNode) * (1.0f + 1.0f / 1000.0f); openTiles.Add((fScore, jumpNode)); } } } if (!routeFound) { return(null); } DebugTools.AssertNotNull(currentNode); var route = PathfindingHelpers.ReconstructJumpPath(cameFrom, currentNode !); if (route.Count == 1) { return(null); } #if DEBUG // Need to get data into an easier format to send to the relevant clients if (DebugRoute != null && route.Count > 0) { var debugJumpNodes = new HashSet <TileRef>(jumpNodes.Count); foreach (var node in jumpNodes) { debugJumpNodes.Add(node.TileRef); } var debugRoute = new SharedAiDebug.JpsRouteDebug( _pathfindingArgs.Uid, route, debugJumpNodes, DebugTime); DebugRoute.Invoke(debugRoute); } #endif return(route); }
private bool IsDiagonalJumpPoint(Direction direction, PathfindingNode currentNode) { // If we're going diagonally need to check all cardinals. // I tried just casting direction ints and offsets to make it smaller but brain no worky. // From NorthEast we check (Closed / Open) S - SE, W - NW PathfindingNode?openNeighborOne = null; PathfindingNode?closedNeighborOne = null; PathfindingNode?openNeighborTwo = null; PathfindingNode?closedNeighborTwo = null; switch (direction) { case Direction.NorthEast: foreach (var neighbor in currentNode.GetNeighbors()) { var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode); switch (neighborDirection) { case Direction.SouthEast: openNeighborOne = neighbor; break; case Direction.South: closedNeighborOne = neighbor; break; case Direction.NorthWest: openNeighborTwo = neighbor; break; case Direction.West: closedNeighborTwo = neighbor; break; } } break; case Direction.SouthEast: foreach (var neighbor in currentNode.GetNeighbors()) { var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode); switch (neighborDirection) { case Direction.NorthEast: openNeighborOne = neighbor; break; case Direction.North: closedNeighborOne = neighbor; break; case Direction.SouthWest: openNeighborTwo = neighbor; break; case Direction.West: closedNeighborTwo = neighbor; break; } } break; case Direction.SouthWest: foreach (var neighbor in currentNode.GetNeighbors()) { var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode); switch (neighborDirection) { case Direction.NorthWest: openNeighborOne = neighbor; break; case Direction.North: closedNeighborOne = neighbor; break; case Direction.SouthEast: openNeighborTwo = neighbor; break; case Direction.East: closedNeighborTwo = neighbor; break; } } break; case Direction.NorthWest: foreach (var neighbor in currentNode.GetNeighbors()) { var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode); switch (neighborDirection) { case Direction.SouthWest: openNeighborOne = neighbor; break; case Direction.South: closedNeighborOne = neighbor; break; case Direction.NorthEast: openNeighborTwo = neighbor; break; case Direction.East: closedNeighborTwo = neighbor; break; } } break; default: throw new ArgumentOutOfRangeException(); } if ((closedNeighborOne == null || PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, closedNeighborOne) == null) && openNeighborOne != null && PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, openNeighborOne) != null) { return(true); } if ((closedNeighborTwo == null || PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, closedNeighborTwo) == null) && openNeighborTwo != null && PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, openNeighborTwo) != null) { return(true); } return(false); }
private PathfindingNode?GetJumpPoint(PathfindingNode currentNode, Direction direction, PathfindingNode endNode) { var count = 0; while (count < 1000) { count++; PathfindingNode?nextNode = null; foreach (var node in currentNode.GetNeighbors()) { if (PathfindingHelpers.RelativeDirection(node, currentNode) == direction) { nextNode = node; break; } } // We'll do opposite DirectionTraversable just because of how the method's setup // Nodes should be 2-way anyway. if (nextNode == null || PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, nextNode) == null) { return(null); } if (nextNode == endNode) { return(endNode); } // Horizontal and vertical are treated the same i.e. // They only check in their specific direction // (So Going North means you check NorthWest and NorthEast to see if we're a jump point) // Diagonals also check the cardinal directions at the same time at the same time // See https://harablog.wordpress.com/2011/09/07/jump-point-search/ for original description switch (direction) { case Direction.East: if (IsCardinalJumpPoint(direction, nextNode)) { return(nextNode); } break; case Direction.NorthEast: if (IsDiagonalJumpPoint(direction, nextNode)) { return(nextNode); } if (GetJumpPoint(nextNode, Direction.North, endNode) != null || GetJumpPoint(nextNode, Direction.East, endNode) != null) { return(nextNode); } break; case Direction.North: if (IsCardinalJumpPoint(direction, nextNode)) { return(nextNode); } break; case Direction.NorthWest: if (IsDiagonalJumpPoint(direction, nextNode)) { return(nextNode); } if (GetJumpPoint(nextNode, Direction.North, endNode) != null || GetJumpPoint(nextNode, Direction.West, endNode) != null) { return(nextNode); } break; case Direction.West: if (IsCardinalJumpPoint(direction, nextNode)) { return(nextNode); } break; case Direction.SouthWest: if (IsDiagonalJumpPoint(direction, nextNode)) { return(nextNode); } if (GetJumpPoint(nextNode, Direction.South, endNode) != null || GetJumpPoint(nextNode, Direction.West, endNode) != null) { return(nextNode); } break; case Direction.South: if (IsCardinalJumpPoint(direction, nextNode)) { return(nextNode); } break; case Direction.SouthEast: if (IsDiagonalJumpPoint(direction, nextNode)) { return(nextNode); } if (GetJumpPoint(nextNode, Direction.South, endNode) != null || GetJumpPoint(nextNode, Direction.East, endNode) != null) { return(nextNode); } break; default: throw new ArgumentOutOfRangeException(nameof(direction), direction, null); } currentNode = nextNode; } Logger.WarningS("pathfinding", "Recursion found in JPS pathfinder"); return(null); }
protected override async Task <Queue <TileRef> > Process() { if (_startNode == null || _endNode == null || Status == JobStatus.Finished) { return(null); } // If we couldn't get a nearby node that's good enough if (!PathfindingHelpers.TryEndNode(ref _endNode, _pathfindingArgs)) { return(null); } var openTiles = new PriorityQueue <ValueTuple <float, PathfindingNode> >(new PathfindingComparer()); var gScores = new Dictionary <PathfindingNode, float>(); var cameFrom = new Dictionary <PathfindingNode, PathfindingNode>(); var closedTiles = new HashSet <PathfindingNode>(); PathfindingNode currentNode = null; openTiles.Add((0.0f, _startNode)); gScores[_startNode] = 0.0f; var routeFound = false; var count = 0; while (openTiles.Count > 0) { count++; if (count % 20 == 0 && count > 0) { await SuspendIfOutOfTime(); } if (_startNode == null || _endNode == null) { return(null); } (_, currentNode) = openTiles.Take(); if (currentNode.Equals(_endNode)) { routeFound = true; break; } closedTiles.Add(currentNode); foreach (var(direction, nextNode) in currentNode.Neighbors) { if (closedTiles.Contains(nextNode)) { continue; } // If tile is untraversable it'll be null var tileCost = PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, nextNode); if (tileCost == null || !PathfindingHelpers.DirectionTraversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, currentNode, direction)) { continue; } var gScore = gScores[currentNode] + tileCost.Value; if (gScores.TryGetValue(nextNode, out var nextValue) && gScore >= nextValue) { continue; } cameFrom[nextNode] = currentNode; gScores[nextNode] = gScore; // pFactor is tie-breaker where the fscore is otherwise equal. // See http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html#breaking-ties // There's other ways to do it but future consideration var fScore = gScores[nextNode] + PathfindingHelpers.OctileDistance(_endNode, nextNode) * (1.0f + 1.0f / 1000.0f); openTiles.Add((fScore, nextNode)); } } if (!routeFound) { return(null); } var route = PathfindingHelpers.ReconstructPath(cameFrom, currentNode); if (route.Count == 1) { return(null); } #if DEBUG // Need to get data into an easier format to send to the relevant clients if (DebugRoute != null && route.Count > 0) { var debugCameFrom = new Dictionary <TileRef, TileRef>(cameFrom.Count); var debugGScores = new Dictionary <TileRef, float>(gScores.Count); var debugClosedTiles = new HashSet <TileRef>(closedTiles.Count); foreach (var(node, parent) in cameFrom) { debugCameFrom.Add(node.TileRef, parent.TileRef); } foreach (var(node, score) in gScores) { debugGScores.Add(node.TileRef, score); } foreach (var node in closedTiles) { debugClosedTiles.Add(node.TileRef); } var debugRoute = new SharedAiDebug.AStarRouteDebug( _pathfindingArgs.Uid, route, debugCameFrom, debugGScores, debugClosedTiles, DebugTime); DebugRoute.Invoke(debugRoute); } #endif return(route); }
private bool IsDiagonalJumpPoint(Direction direction, PathfindingNode currentNode) { // If we're going diagonally need to check all cardinals. // I just just using casts int casts and offset to make it smaller but brain no workyand it wasn't working. // From NorthEast we check (Closed / Open) S - SE, W - NW PathfindingNode openNeighborOne; PathfindingNode closedNeighborOne; PathfindingNode openNeighborTwo; PathfindingNode closedNeighborTwo; switch (direction) { case Direction.NorthEast: openNeighborOne = currentNode.GetNeighbor(Direction.SouthEast); closedNeighborOne = currentNode.GetNeighbor(Direction.South); openNeighborTwo = currentNode.GetNeighbor(Direction.NorthWest); closedNeighborTwo = currentNode.GetNeighbor(Direction.West); break; case Direction.SouthEast: openNeighborOne = currentNode.GetNeighbor(Direction.NorthEast); closedNeighborOne = currentNode.GetNeighbor(Direction.North); openNeighborTwo = currentNode.GetNeighbor(Direction.SouthWest); closedNeighborTwo = currentNode.GetNeighbor(Direction.West); break; case Direction.SouthWest: openNeighborOne = currentNode.GetNeighbor(Direction.NorthWest); closedNeighborOne = currentNode.GetNeighbor(Direction.North); openNeighborTwo = currentNode.GetNeighbor(Direction.SouthEast); closedNeighborTwo = currentNode.GetNeighbor(Direction.East); break; case Direction.NorthWest: openNeighborOne = currentNode.GetNeighbor(Direction.SouthWest); closedNeighborOne = currentNode.GetNeighbor(Direction.South); openNeighborTwo = currentNode.GetNeighbor(Direction.NorthEast); closedNeighborTwo = currentNode.GetNeighbor(Direction.East); break; default: throw new ArgumentOutOfRangeException(); } if ((closedNeighborOne == null || PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, closedNeighborOne) == null) && openNeighborOne != null && PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, openNeighborOne) != null) { return(true); } if ((closedNeighborTwo == null || PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, closedNeighborTwo) == null) && openNeighborTwo != null && PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, openNeighborTwo) != null) { return(true); } return(false); }