}//end getAStarPath //=====================================================================================================================// //Given a start and an end supergrid, we create compound flowfield between both grids and any adjacent tiles (if they are diagonal) public void getNextFlowfield(int curr_i, int curr_j, int nex_i, int nex_j) { //GetComponent<unit_behavior>().ff_table.deleteFF(gameObject.GetInstanceID()); //unlink the previous flowfield from this object's id int current_i = curr_i; int current_j = curr_j; int next_i = nex_i; int next_j = nex_j; int row_length = 0; int col_length = 0; int xOff = 0; //offset for calculating global int zOff = 0; //grid indices int destRow = 0; //calculate local grid int destCol = 0; //indices int type = 0; //tells us which direction we are generating our flowfield in //this way we can figure out what cells are blocked when plotting a route generate_obstacle_grid obs = new generate_obstacle_grid(); //row_length, col_length, xOff,zOff, and destrow and col depend //on which direction the next cell is in //[SE] if (current_i == next_i && current_j == next_j) { row_length = grid_ratio; col_length = grid_ratio; xOff = grid_ratio * (current_i); zOff = grid_ratio * (current_j); destCol = subGrid.worldToCell(destination).Item1 % grid_ratio; destRow = subGrid.worldToCell(destination).Item2 % grid_ratio; if (destCol < 0) //deal with negatives { destCol = grid_ratio + destCol; } if (destRow < 0) { destRow = grid_ratio + destRow; } type = 0; } //[S][E] else if (current_i < next_i && current_j == next_j) { row_length = 2 * grid_ratio; //for dynamic sizing, use 10 * Math.abs(current_i - next_i) col_length = grid_ratio; xOff = grid_ratio * (current_i); zOff = grid_ratio * (current_j); destCol = grid_ratio / 2; //these are fixed destinations, we need to dynamically destRow = (2 * grid_ratio) - 1; //pick destinations based on which cells are blocked or not type = 0; } //[ ][E] //[S][ ] 2. else if (current_i < next_i && current_j < next_j) { row_length = 2 * grid_ratio; col_length = 2 * grid_ratio; xOff = grid_ratio * (current_i); zOff = grid_ratio * (current_j); destCol = (2 * grid_ratio) - 1; destRow = (2 * grid_ratio) - 1; type = 0; } //[E] //[S] 3. else if (current_i == next_i && current_j < next_j) { row_length = grid_ratio; col_length = 2 * grid_ratio; xOff = grid_ratio * (current_i); zOff = grid_ratio * (current_j); destCol = (2 * grid_ratio) - 1; destRow = grid_ratio / 2; type = 0; } //[E][ ] //[ ][S] 4. else if (current_i > next_i && current_j < next_j) { row_length = 2 * grid_ratio; col_length = 2 * grid_ratio; xOff = grid_ratio * (current_i - 1); zOff = grid_ratio * (current_j); destCol = (2 * grid_ratio) - 1; destRow = 0; type = 1; } //[E][S] 5. else if (current_i > next_i && current_j == next_j) { row_length = 2 * grid_ratio; col_length = grid_ratio; xOff = grid_ratio * (current_i - 1); zOff = grid_ratio * (current_j); destCol = grid_ratio / 2; destRow = 0; type = 1; } //[ ][S] //[E][ ] 6. else if (current_i > next_i && current_j > next_j) { row_length = 2 * grid_ratio; col_length = 2 * grid_ratio; xOff = grid_ratio * (current_i - 1); zOff = grid_ratio * (current_j - 1); destCol = 0; destRow = 0; type = 2; } //[S] //[E] 7. else if (current_i == next_i && current_j > next_j) { row_length = grid_ratio; col_length = 2 * grid_ratio; xOff = grid_ratio * (current_i); zOff = grid_ratio * (current_j - 1); destCol = 0; destRow = grid_ratio / 2; type = 3; } //[S][ ] //[ ][E] else if (current_i < next_i && current_j > next_j) { row_length = 2 * grid_ratio; col_length = 2 * grid_ratio; xOff = grid_ratio * (current_i); zOff = grid_ratio * (current_j - 1); destCol = 0; destRow = (2 * grid_ratio) - 1; type = 3; } //find the obstacles int[,] obstacle_grid = obs.Generate_grid(subGrid.cellSize, row_length, col_length, xOff, zOff); if (Path.Count == 0) //if we are at the final supergrid location { destRow = subGrid.worldToCell(destination).Item1 % grid_ratio; //subgrid cells per supergrid destCol = subGrid.worldToCell(destination).Item2 % grid_ratio; //needs to be done for negative indices if (destRow < 0) { destRow = grid_ratio + destRow; } if (destCol < 0) { destCol = grid_ratio + destCol; } //allows us to flowfield in all 4 quadrants switch (type) { case 0: break; case 1: destRow = destRow + grid_ratio; break; case 2: destRow = destRow + grid_ratio; destCol = destCol + grid_ratio; break; case 3: destCol = destCol + grid_ratio; break; } } //get destination (find nearest unblocked cell) Pair <int, int> p = obs.find_nearest_unblocked(obstacle_grid, destRow, destCol, row_length, col_length); destRow = p.first; destCol = p.second; //make sure that the destination isn't entirely blocked //if it isn't, then we say that the cell is blocked switch (type) { case 0: if (destRow < grid_ratio && destCol < grid_ratio) { if (next_i != current_i || next_j != current_j) //make sure we are not in the destination cell { aStarBlocked.Add(new Pair <int, int>(next_i, next_j)); // buffer++; //expand our search grid getAstarPath(ref Path); //recalculate path return; } } break; case 1: //Debug.Log("Destrow is: " + destRow); //Debug.Log("Destcol is: " + destCol); if (destCol < grid_ratio && destRow >= grid_ratio) { aStarBlocked.Add(new Pair <int, int>(next_i, next_j)); // buffer++; //expand our search grid getAstarPath(ref Path); //recalculate path return; } break; /* * case 2: * if (destRow >= grid_ratio && destCol >= grid_ratio) * { * aStarBlocked.Add(new Pair<int, int>(next_i, next_j)); // * buffer++; //expand our search grid * getAstarPath(ref Path); //recalculate path * return; * } * break; */ case 3: if (destRow < grid_ratio && destCol >= grid_ratio) { aStarBlocked.Add(new Pair <int, int>(next_i, next_j)); // buffer++; //expand our search grid getAstarPath(ref Path); //recalculate path return; } break; default: break; } //GENERATE FLOWFIELD WITH GIVEN PARAMETERS Djikstra dijk = new Djikstra(); //Debug.Log("Type = " + type); //Debug.Log("row_length: " + row_length + " col_length: " + col_length); //Debug.Log("destCol: " + destCol + " destRow: " + destRow); int[,] dijkstra = dijk.generate_djikstra_grid(row_length, col_length, obstacle_grid, new Pair <int, int>(destCol, destRow)); //these values contextualize our Dijkstra grid for future use djgrid.grid = dijkstra; djgrid.row_length = row_length; djgrid.col_length = col_length; djgrid.type = type; djgrid.xOff = xOff; djgrid.zOff = zOff; //if there is no path, mark this astar cell as blocked if (djgrid.getValuefromObject(gameObject) == -1) { //Debug.Log("next_i: " + next_i + " next_j: " + next_j); aStarBlocked.Add(new Pair <int, int>(next_i, next_j)); // buffer++; //expand our search grid getAstarPath(ref Path); //recalculate path return; } else if (djgrid.getValuefromObject(gameObject) == 0) //if we have reached an objective, but this function is still active for some reason, reset our pathfinding { setDestination(destination); return; } Generate_Flowfield field_obj = new Generate_Flowfield(); Vector3[,] field = field_obj.generate_flowfield(row_length, col_length, dijkstra, xOff, zOff, subGrid.cellSize); //these values also contextualize our flowfield for future use Flowfield ff = new Flowfield(); ff.flowfield = field; ff.row_length = row_length; ff.col_length = col_length; ff.type = type; ff.xOff = xOff; ff.zOff = zOff; GetComponent <unit_behavior>().ff_table.addFF(gameObject.GetInstanceID(), ff); //add flowfield to dictionary and link by ID }//end function
//======================================================================================================// //generates stack for our flowfields void getAstarPath(ref Stack <Pair <int, int> > path) { //Debug.Log("Starting cells: " + superGrid.worldToCell(transform.position)); //extract position data from tuples int i_start = superGrid.worldToCell(transform.position).Item1; int j_start = superGrid.worldToCell(transform.position).Item2; int i_end = superGrid.worldToCell(destination).Item1; int j_end = superGrid.worldToCell(destination).Item2; var astar = new Astar_search(); //we need to perform a coordinate transform on our starting and destination //points and then re-transform them to get global coordinates //WHAT TO IMPLEMENT:: //ORIGINAL GRID POSITIONS /* // Suppose we originally draw a path from [1,1] to [3,2] on the current grid *[0,3][1,3][2,3][3,3] *[0,2][1,2][2,2][3,2] *[0,1][1,1][2,1][3,1] *[0,0][1,0][2,0][3,0] */ //The first step is to figure out how to index the array. For this example [1,1] should be [0,0] //and [3,2] should be [2,1]. That way we can calculate an Astar path with normalized indices (start point //equal to [0,0]) //However, suppose we go from [3,0] to [1,3] //In this case [1,0] will become [0,0] and [1,3] will become [0,3] and [3,0] will become [2,0] //WHAT THIS FUNCTION DOES //let's re-use our example of [1,1] to [3,2] again //if we create an array with width equal to (horizontal displacement x 2) + 1, and height equal to //(vertical displacement x 2) + 1, then we will have an array where our start position is at the center /* X = [1,1] and Y = [3,2] * [0][0][0][0][Y] * [0][0][X][0][0] * [0][0][0][0][0] */ //notice how 75% of this grid is space that will not be searched, //this is pretty inefficient. Instead of assuming a width of //displacement x2 - 1, we will do a width of displacement + 1 + a buffer (more about the buffer later) //our new grid looks like this: /* X = [1,1] and Y = [3,2] * [0][0][Y] * [X][0][0] * * height = (2 - 1) + 1 = 2 * width = (3 - 1) + 1 = 3 * origin is at bottom-left, destination is at top-right */ //this works well for an example where we go north-east, north, or east. However different displacements //require different grids /* X = [3,0] and Y = [1,3] * [Y][0][0] * [0][0][0] * [0][0][0] * [0][0][X] */ //now the start point is in the bottom-right, and our end position is in the top-left //these positions are calculated based on which quadrant we start and end in, as well as the buffer size of the A* //this coordinate transform is what allows us to perform an A* in all directions in both positive and negative //world space int ROW = (Mathf.Abs(i_end - i_start)) + (2 * buffer) + 1; //figure out the size of the array to create int COL = (Mathf.Abs(j_end - j_start)) + (2 * buffer) + 1; int i_local_end = 0; int j_local_end = 0; int i_local_start = 0; int j_local_start = 0; int i_offset = 0; int j_offset = 0; //CALCULATE START AND END INDICES RELATIVE TO INTEGER ARRAY //[ ][E] //[S][ ] if (i_end >= i_start && j_end >= j_start) { i_local_start = buffer; j_local_start = buffer; i_local_end = ROW - 1 - buffer; j_local_end = COL - 1 - buffer; } //[S][ ] //[ ][E] else if (i_end > i_start && j_end < j_start) { i_local_start = buffer; j_local_start = COL - 1 - buffer; j_offset = COL - 1 - (2 * buffer); i_local_end = ROW - 1 - buffer; j_local_end = buffer; } //[ ][S] //[E][ ] else if (i_end <= i_start && j_end <= j_start) { i_local_start = ROW - 1 - buffer; j_local_start = COL - 1 - buffer; i_offset = ROW - 1 - (2 * buffer); j_offset = COL - 1 - (2 * buffer); i_local_end = buffer; j_local_end = buffer; } //[E][ ] //[ ][S] else if (i_end < i_start && j_end > j_start) { i_local_start = ROW - 1 - buffer; j_local_start = buffer; i_offset = ROW - 1 - (2 * buffer); i_local_end = buffer; j_local_end = COL - 1 - buffer; } //Debug.Log("Ending cells: " + superGrid.worldToCell(destination)); //Debug.Log("Buffer size: " + buffer); //Debug.Log("i_local_start: " + i_local_start + " j_local_start: " + j_local_start); //Debug.Log("i_local_end: " + i_local_end + " j_local_end: " + j_local_end); //This part updates our list of blocked cells before we calculate a new path //this is similar to a D* search var temp_grid = new int[ROW, COL]; for (int i = 0; i < ROW; i++) //create an array of unblocked cells (1 open, 0 blocked) { for (int j = 0; j < COL; j++) { temp_grid[i, j] = 1; } } foreach (Pair <int, int> blockedCell in aStarBlocked) //loop through the list of blocked cells and transform to local coordinates, then apply { //apply the offset int i_index = ((blockedCell.first) - i_start + i_offset + buffer); int j_index = ((blockedCell.second) - j_start + j_offset + buffer); //Debug.Log("blocked_i_index: " + i_index + " blocked_j_index: " + j_index); if ((i_index < ROW && j_index < COL) && (i_index >= 0 && j_index >= 0)) { temp_grid[i_index, j_index] = 0; } //if the destination is blocked, get the next best thing if ((i_index == i_local_end) && (j_index == j_local_end)) { //convert our blocked grid to a new format so we can find the closest unblocked neighbor int[,] new_grid = new int[ROW, COL]; for (int i = 0; i < ROW; i++) //create an array of unblocked cells (Int32.MaxValue = BLOCKED) { for (int j = 0; j < COL; j++) { if (temp_grid[i, j] == 0) { new_grid[i, j] = Int32.MaxValue; //our "find_nearest_unblocked" function only sees Int32.MaxValue as a blocked space } } } //get a new pair of destination cells generate_obstacle_grid obstacle_helper = new generate_obstacle_grid(); //create this to use its' utility function Pair <int, int> newDestPair = obstacle_helper.find_nearest_unblocked(new_grid, i_local_end, j_local_end, ROW, COL); i_local_end = newDestPair.first; j_local_end = newDestPair.second; //Debug.Log("Rowsize = " + ROW + " Colsize = " + COL); //Debug.Log("new_dest_i: " + i_local_end + " new_dest_j: " + j_local_end); } } //finally calculate the new A star search astar.aStarSearch(temp_grid, new Pair <int, int>(i_local_start, j_local_start), new Pair <int, int>(i_local_end, j_local_end), ROW, COL, ref path); //print A* stuff Stack <Pair <int, int> > temp_path = new Stack <Pair <int, int> >(); //restore global coordinates //create temporary Stack that holds our restored path values while (!(path.Count == 0)) //while stack not empty { Pair <int, int> p = path.Pop(); //get item off top of stack //re-apply the offset p.first = ((p.first) + i_start - i_offset - buffer); p.second = ((p.second) + j_start - j_offset - buffer); temp_path.Push(p); } //restore Stack while (!(temp_path.Count == 0)) //while stack not empty { path.Push(temp_path.Pop()); } }//end getAStarPath