/** Async method for moving the graph */ IEnumerator UpdateGraphCoroutine () { // Find the direction // that we want to move the graph in. // Calcuculate this in graph space (where a distance of one is the size of one node) Vector3 dir = PointToGraphSpace(target.position) - PointToGraphSpace(graph.center); // Snap to a whole number of nodes dir.x = Mathf.Round(dir.x); dir.z = Mathf.Round(dir.z); dir.y = 0; // Nothing do to if ( dir == Vector3.zero ) yield break; // Number of nodes to offset in each direction Int2 offset = new Int2(-Mathf.RoundToInt(dir.x), -Mathf.RoundToInt(dir.z)); // Move the center (this is in world units, so we need to convert it back from graph space) graph.center += graph.matrix.MultiplyVector (dir); graph.GenerateMatrix (); // Create a temporary buffer // required for the calculations if ( tmp == null || tmp.Length != graph.nodes.Length ) { tmp = new GridNode[graph.nodes.Length]; } // Cache some variables for easier access int width = graph.width; int depth = graph.depth; GridNode[] nodes = graph.nodes; // Check if we have moved // less than a whole graph // width in any direction if ( Mathf.Abs(offset.x) <= width && Mathf.Abs(offset.y) <= depth ) { // Offset each node by the #offset variable // nodes which would end up outside the graph // will wrap around to the other side of it for ( int z=0; z < depth; z++ ) { int pz = z*width; int tz = ((z+offset.y + depth)%depth)*width; for ( int x=0; x < width; x++ ) { tmp[tz + ((x+offset.x + width) % width)] = nodes[pz + x]; } } yield return null; // Copy the nodes back to the graph // and set the correct indices for ( int z=0; z < depth; z++ ) { int pz = z*width; for ( int x=0; x < width; x++ ) { GridNode node = tmp[pz + x]; node.NodeInGridIndex = pz + x; nodes[pz + x] = node; } } IntRect r = new IntRect ( 0, 0, offset.x, offset.y ); int minz = r.ymax; int maxz = depth; // If offset.x < 0, adjust the rect if ( r.xmin > r.xmax ) { int tmp2 = r.xmax; r.xmax = width + r.xmin; r.xmin = width + tmp2; } // If offset.y < 0, adjust the rect if ( r.ymin > r.ymax ) { int tmp2 = r.ymax; r.ymax = depth + r.ymin; r.ymin = depth + tmp2; minz = 0; maxz = r.ymin; } // Make sure erosion is taken into account // Otherwise we would end up with ugly artifacts r = r.Expand ( graph.erodeIterations + 1 ); // Makes sure the rect stays inside the grid r = IntRect.Intersection ( r, new IntRect ( 0, 0, width, depth ) ); yield return null; // Update all nodes along one edge of the graph // With the same width as the rect for ( int z = r.ymin; z < r.ymax; z++ ) { for ( int x = 0; x < width; x++ ) { graph.UpdateNodePositionCollision ( nodes[z*width + x], x, z, false ); } } yield return null; // Update all nodes along the other edge of the graph // With the same width as the rect for ( int z = minz; z < maxz; z++ ) { for ( int x = r.xmin; x < r.xmax; x++ ) { graph.UpdateNodePositionCollision ( nodes[z*width + x], x, z, false ); } } yield return null; // Calculate all connections for the nodes // that might have changed for ( int z = r.ymin; z < r.ymax; z++ ) { for ( int x = 0; x < width; x++ ) { graph.CalculateConnections (nodes, x, z, nodes[z*width+x]); } } yield return null; // Calculate all connections for the nodes // that might have changed for ( int z = minz; z < maxz; z++ ) { for ( int x = r.xmin; x < r.xmax; x++ ) { graph.CalculateConnections (nodes, x, z, nodes[z*width+x]); } } yield return null; // Calculate all connections for the nodes along the boundary // of the graph, these always need to be updated /** \todo Optimize to not traverse all nodes in the graph, only those at the edges */ for ( int z = 0; z < depth; z++ ) { for ( int x = 0; x < width; x++ ) { if ( x == 0 || z == 0 || x >= width-1 || z >= depth-1 ) graph.CalculateConnections (nodes, x, z, nodes[z*width+x]); } } } else { // Just update all nodes for ( int z = 0; z < depth; z++ ) { for ( int x = 0; x < width; x++ ) { graph.UpdateNodePositionCollision ( nodes[z*width + x], x, z, false ); } } // Recalculate the connections of all nodes for ( int z = 0; z < depth; z++ ) { for ( int x = 0; x < width; x++ ) { graph.CalculateConnections (nodes, x, z, nodes[z*width+x]); } } } if ( floodFill ) { yield return null; // Make sure the areas for the graph // have been recalculated // not doing this can cause pathfinding to fail AstarPath.active.QueueWorkItemFloodFill (); } }
public void UpdateArea (GraphUpdateObject guo) { Bounds b = guo.bounds; b.center -= forcedBounds.min; //Figure out which tiles are affected var r = new IntRect (Mathf.FloorToInt (b.min.x / (tileSizeX*cellSize)), Mathf.FloorToInt (b.min.z / (tileSizeZ*cellSize)), Mathf.FloorToInt (b.max.x / (tileSizeX*cellSize)), Mathf.FloorToInt (b.max.z / (tileSizeZ*cellSize))); //Clamp to bounds r = IntRect.Intersection (r, new IntRect (0,0,tileXCount-1,tileZCount-1)); if (!guo.updatePhysics) { for ( int z=r.ymin;z<=r.ymax;z++) { for ( int x=r.xmin;x<=r.xmax;x++) { NavmeshTile tile = tiles[z*tileXCount + x]; tile.flag = true; } } for ( int z=r.ymin;z<=r.ymax;z++) { for ( int x=r.xmin;x<=r.xmax;x++) { NavmeshTile tile = tiles[z*tileXCount + x]; if ( tile.flag ) { tile.flag = false; NavMeshGraph.UpdateArea (guo, tile); } } } return; } if (!dynamic) { throw new System.Exception ("Recast graph must be marked as dynamic to enable graph updates with updatePhysics = true"); } Voxelize vox = globalVox; if (vox == null) { throw new System.InvalidOperationException ("No Voxelizer object. UpdateAreaInit should have been called before this function."); } AstarProfiler.StartProfile ("Init"); /** \bug No bounds checking */ //r.DebugDraw (Matrix4x4.TRS (forcedBounds.min, Quaternion.identity, new Vector3 (tileSize*cellSize, 1, tileSize*cellSize)), Color.red); //Debug.Break (); AstarProfiler.StartProfile ("RemoveConnections"); for (int x=r.xmin;x<=r.xmax;x++) { for (int z=r.ymin;z<=r.ymax;z++) { RemoveConnectionsFromTile (tiles[x + z*tileXCount]); } } AstarProfiler.EndProfile ("RemoveConnections"); AstarProfiler.StartProfile ("Build Tiles"); for (int x=r.xmin;x<=r.xmax;x++) { for (int z=r.ymin;z<=r.ymax;z++) { BuildTileMesh (vox, x,z); } } AstarProfiler.EndProfile ("Build Tiles"); AstarProfiler.StartProfile ("ConnectTiles"); uint graphIndex = (uint)AstarPath.active.astarData.GetGraphIndex (this); for (int x=r.xmin;x<=r.xmax;x++) { for (int z=r.ymin;z<=r.ymax;z++) { NavmeshTile tile = tiles[x + z*tileXCount]; GraphNode[] nodes = tile.nodes; for (int i=0;i<nodes.Length;i++) nodes[i].GraphIndex = graphIndex; } } //Connect the newly create tiles with the old tiles and with each other r = r.Expand (1); //Clamp to bounds r = IntRect.Intersection (r, new IntRect (0,0,tileXCount-1,tileZCount-1)); for (int x=r.xmin;x<=r.xmax;x++) { for (int z=r.ymin;z<=r.ymax;z++) { if (x < tileXCount-1 && r.Contains (x+1, z)) { ConnectTiles (tiles[x + z*tileXCount], tiles[x+1 + z*tileXCount]); } if (z < tileZCount-1 && r.Contains (x, z+1)) { ConnectTiles (tiles[x + z*tileXCount], tiles[x + (z+1)*tileXCount]); } } } AstarProfiler.EndProfile ("ConnectTiles"); AstarProfiler.PrintResults (); }