//function called to find path, give start and end position of path to be found void FindPath(TileVec2 StartPos, TileVec2 EndPos) { List <TileVec2> openSet = new List <TileVec2>(); //all possible positions worth searching. HashSet <TileVec2> closedSet = new HashSet <TileVec2>(); //closed set of evaluated and discarded positions openSet.Add(startPos); //add start pos as first tile in open set //whilst we still have values in openset while (openSet.Count > 0) { TileVec2 currentTile = openSet[0]; for (int i = 0; i < openSet.Count; i++) { if (openSet[i].fCost < currentTile.fCost || openSet[i].fCost == currentTile.fCost && openSet[i].hCost < currentTile.hCost) { currentTile = openSet[i]; //current tile is equal to next best candidate in list } } openSet.Remove(currentTile); //remove tile we currently evaluating closedSet.Add(currentTile); //move it to closed set //if we at end position if (currentTile.xPos == EndPos.xPos) { if (currentTile.yPos == EndPos.yPos) { //go draw path to show user RetracePath(startPos, currentTile); return; } } //go look at our neighbours and calculate their costs and add to open list. foreach (TileVec2 neighbour in MazeGrid.GetNeighbours(currentTile)) { //unless they out bounds or already in closed list if (!neighbour.isFloor || closedSet.Contains(neighbour)) { continue; } int newMovCostToNeighbour = currentTile.gCost + GetDistance(currentTile, neighbour); if (newMovCostToNeighbour < neighbour.gCost || !openSet.Contains(neighbour)) { neighbour.gCost = newMovCostToNeighbour; neighbour.hCost = GetDistance(neighbour, EndPos); neighbour.parent = currentTile; if (!openSet.Contains(neighbour)) { openSet.Add(neighbour); } } } } }
//retrace path //keep looking at next tiles parent until we back at start tile //along the way instaniate a tile to show user calculated path void RetracePath(TileVec2 startNode, TileVec2 currentTile) { resetPathHolder(); while (currentTile != startNode) { GameObject tile = Instantiate(pathTile, new Vector3(currentTile.xPos, 0.05f, currentTile.yPos), flatTile) as GameObject; tile.transform.parent = pathHolder.transform; currentTile = currentTile.parent; } }
//function used to calculate distance between tiles //we use a value of 10 from tile to tile vertically and horizontally, then a value of 14 fo diagonal movements due to pythagoras 1^2 + 1^2 = sqrt(2) or 1.4ish //but use ints and scale to 10 and 14, as ints cheaper then floats int GetDistance(TileVec2 tileA, TileVec2 tileB) { int xDist = Mathf.Abs(tileA.xPos - tileB.xPos); int yDist = Mathf.Abs(tileA.yPos - tileB.yPos); if (xDist > yDist) { return(14 * yDist + 10 * (xDist - yDist)); } return(14 * xDist + 10 * (yDist - xDist)); }
//void OnDrawGizmos() //{ // Gizmos.DrawWireCube(transform.position, new Vector3(columns, 1, rows)); // // if (MazeGrid != null) // { // foreach (TileVec2 n in MazeGrid) // { // Gizmos.color = (n.isFloor) ? Color.white : Color.red; // Vector3 worldPos = new Vector3(n.xPos, 0, n.yPos); // Gizmos.DrawCube(worldPos, Vector3.one * 0.5f); // } // } // //} //this will be used for pathfinding void CreateGrid() { MazeGrid = new TileVec2[columns, rows]; for (int x = 0; x < columns; x++) { for (int y = 0; y < rows; y++) { //check each pos in maze bool floor = FloorTiles[x, y]; //set grid to position and see if floor tile there MazeGrid[x, y] = new TileVec2(floor, x, y); } } }
void setStartEndTiles(bool start) { //do a raycast to see where user clicked on screen and if it hit a tile store position and place start/end tile at that positiom Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { //if left click to place start tile if (start) { GameObject tile = Instantiate(startTile, hit.transform.position + aboveMaze, flatTile) as GameObject; tile.transform.parent = pathHolder.transform; tile.name = "startTile"; //set our starttile boolean to true so we know we have a start tile in the world blnStartTile = true; startPos = new TileVec2(true, (int)hit.transform.position.x, (int)hit.transform.position.z); } //if right click to place end tile else { //if start tile exists, go to place the end tile if (blnStartTile) { GameObject tile = Instantiate(endTile, hit.transform.position + aboveMaze, flatTile) as GameObject; tile.name = "endTile"; tile.transform.parent = pathHolder.transform; //set our endtile boolean to true so we know we have a end tile in the world //blnEndTile = true; TileVec2 endPos = new TileVec2(true, (int)hit.transform.position.x, (int)hit.transform.position.z); //now we have a start/end tile, go find path between them FindPath(startPos, endPos); } } } }
//check all tiles around given tile public List <TileVec2> GetNeighbours(TileVec2 Tile) { List <TileVec2> neighbours = new List <TileVec2>(); for (int x = -1; x <= 1; x++) { for (int y = -1; y <= 1; y++) { if (x == 0 && y == 0) { continue; //dont check self } int xCheck = Tile.xPos + x; int yCheck = Tile.yPos + y; //check we not out of bounds of grid if (xCheck >= 0 && xCheck < columns && yCheck >= 0 && yCheck < rows) { neighbours.Add(MazeGrid[xCheck, yCheck]); } } } return(neighbours); }