/// <summary> /// Constructor /// </summary> /// <param name="nodePos">Node position</param> /// <param name="targetPos">Target position</param> /// <param name="parentNode">Parent node</param> public PathNode(Vector2 nodePos, Vector2 targetPos, PathNode parentNode) { this.nodePos = nodePos; this.parentNode = parentNode; if (this.parentNode != null) this.valueG = this.parentNode.valueG + (nodePos - this.parentNode.nodePos).Length(); else this.valueG = 0.0f; this.valueH = Math.Abs(targetPos.X - nodePos.X) + Math.Abs(targetPos.Y - nodePos.Y); this.valueF = this.valueG + this.valueH; }
/// <summary> /// Update information on a node and add it to open list if necessary /// </summary> /// <param name="pathNode">The node to update</param> /// <param name="checkedNode">The currently selected node</param> void UpdateNode(PathNode pathNode, PathNode checkedNode, PathNode[] additionalNodes) { if (this.CheckNodePassability(pathNode, additionalNodes)) { PathNode openNode = this.OnOpenList(pathNode); if (openNode != null) { pathNode = openNode; float newValueG = checkedNode.valueG + (pathNode.nodePos - checkedNode.nodePos).Length(); if (newValueG < pathNode.valueG) { pathNode.valueG = newValueG; pathNode.valueF = pathNode.valueG + pathNode.valueH; pathNode.parentNode = checkedNode; } } else if (!(this.OnClosedList(pathNode))) this.openList.Add(pathNode); } }
/// <summary> /// Check if a path node is already on open list /// </summary> /// <param name="pathNode">Path node to check</param> /// <returns>Path node if found in list or null otherwise</returns> PathNode OnOpenList(PathNode pathNode) { foreach(PathNode currentNode in this.openList) { if (currentNode.nodePos == pathNode.nodePos) return currentNode; } return null; }
/// <summary> /// Check if a path node is already on closed list /// </summary> /// <param name="pathNode">Path node to check</param> /// <returns>True if on closed list</returns> bool OnClosedList(PathNode pathNode) { foreach (PathNode currentNode in this.closedList) { if (currentNode.nodePos == pathNode.nodePos) return true; } return false; }
/// <summary> /// Find shortest path to target position /// </summary> void FindPath() { PathNode currentNode = null; this.currentlySearching = true; while (!(this.resetPathFinder) && this.openList.Count > 0 && !(currentNode != null && currentNode.nodePos == this.targetPos)) { // Find node with lowest F value currentNode = this.openList[0]; foreach (PathNode openNode in this.openList) { if (openNode.valueF <= currentNode.valueF) currentNode = openNode; } this.closedList.Add(currentNode); this.openList.Remove(currentNode); // Inspect surrounding nodes Vector2 nextPos = currentNode.nodePos + new Vector2(-1.0f, -1.0f); PathNode pathNode = new PathNode(nextPos, this.targetPos, currentNode); PathNode[] additionalNodes = new PathNode[] { new PathNode(currentNode.nodePos + new Vector2(-1.0f, 0.0f), this.targetPos, currentNode), new PathNode(currentNode.nodePos + new Vector2(0.0f, -1.0f), this.targetPos, currentNode) }; this.UpdateNode(pathNode, currentNode, additionalNodes); nextPos = currentNode.nodePos + new Vector2(0.0f, -1.0f); pathNode = new PathNode(nextPos, this.targetPos, currentNode); this.UpdateNode(pathNode, currentNode, null); nextPos = currentNode.nodePos + new Vector2(1.0f, -1.0f); pathNode = new PathNode(nextPos, this.targetPos, currentNode); additionalNodes = new PathNode[] { new PathNode(currentNode.nodePos + new Vector2(1.0f, 0.0f), this.targetPos, currentNode), new PathNode(currentNode.nodePos + new Vector2(0.0f, -1.0f), this.targetPos, currentNode) }; this.UpdateNode(pathNode, currentNode, additionalNodes); nextPos = currentNode.nodePos + new Vector2(1.0f, 0.0f); pathNode = new PathNode(nextPos, this.targetPos, currentNode); this.UpdateNode(pathNode, currentNode, null); nextPos = currentNode.nodePos + new Vector2(1.0f, 1.0f); pathNode = new PathNode(nextPos, this.targetPos, currentNode); additionalNodes = new PathNode[] { new PathNode(currentNode.nodePos + new Vector2(1.0f, 0.0f), this.targetPos, currentNode), new PathNode(currentNode.nodePos + new Vector2(0.0f, 1.0f), this.targetPos, currentNode) }; this.UpdateNode(pathNode, currentNode, additionalNodes); nextPos = currentNode.nodePos + new Vector2(0.0f, 1.0f); pathNode = new PathNode(nextPos, this.targetPos, currentNode); this.UpdateNode(pathNode, currentNode, null); nextPos = currentNode.nodePos + new Vector2(-1.0f, 1.0f); pathNode = new PathNode(nextPos, this.targetPos, currentNode); additionalNodes = new PathNode[] { new PathNode(currentNode.nodePos + new Vector2(-1.0f, 0.0f), this.targetPos, currentNode), new PathNode(currentNode.nodePos + new Vector2(0.0f, 1.0f), this.targetPos, currentNode) }; this.UpdateNode(pathNode, currentNode, additionalNodes); nextPos = currentNode.nodePos + new Vector2(-1.0f, 0.0f); pathNode = new PathNode(nextPos, this.targetPos, currentNode); this.UpdateNode(pathNode, currentNode, null); } // Reached target pos, so trace back path lock (this.nextNodexLock) { this.nextNodes = new List<Vector2>(); if (currentNode != null && currentNode.nodePos == this.targetPos) { this.nextNodes.Add(currentNode.nodePos); while (currentNode.parentNode != null) { currentNode = currentNode.parentNode; this.nextNodes.Insert(0, currentNode.nodePos); } } } this.currentlySearching = false; }
/// <summary> /// Check if a node is passable /// </summary> /// <param name="pathNode">Main node to check</param> /// <param name="additionalNodes">Additional nodes to check</param> /// <returns>True if node passable</returns> bool CheckNodePassability(PathNode pathNode, PathNode[] additionalNodes) { bool nodePassable = this.PointInBounds(pathNode.nodePos) && !(this.levelGrid[(int)(pathNode.nodePos.X), (int)(pathNode.nodePos.Y)]); if (nodePassable && additionalNodes != null) { for (int i = 0; i < additionalNodes.Length && nodePassable; i++) nodePassable = this.PointInBounds(additionalNodes[i].nodePos) && !(this.levelGrid[(int)(additionalNodes[i].nodePos.X), (int)(additionalNodes[i].nodePos.Y)]); } return nodePassable; }