/// <summary> /// Makes the cell rounder by subdividing the edges and offsetting them at the middle /// </summary> /// <param name="minEdgeLength">How small the individual subdivided edges can be (smaller values produce rounder shapes, but require more geometry)</param> public static void RoundCell(VoronoiCell cell, float minEdgeLength = 500.0f, float roundingAmount = 0.5f, float irregularity = 0.1f) { List <GraphEdge> tempEdges = new List <GraphEdge>(); foreach (GraphEdge edge in cell.Edges) { if (!edge.IsSolid || edge.OutsideLevel) { tempEdges.Add(edge); continue; } //If the edge is next to an empty cell and there's another solid cell at the other side of the empty one, //don't touch this edge. Otherwise we may end up closing off small passages between cells. var adjacentEmptyCell = edge.AdjacentCell(cell); if (adjacentEmptyCell?.CellType == CellType.Solid) { adjacentEmptyCell = null; } if (adjacentEmptyCell != null) { GraphEdge adjacentEdge = null; //find the edge at the opposite side of the adjacent cell foreach (GraphEdge otherEdge in adjacentEmptyCell.Edges) { if (Vector2.Dot(adjacentEmptyCell.Center - edge.Center, adjacentEmptyCell.Center - otherEdge.Center) < 0 && otherEdge.AdjacentCell(adjacentEmptyCell)?.CellType == CellType.Solid) { adjacentEdge = otherEdge; break; } } if (adjacentEdge != null) { tempEdges.Add(edge); continue; } } List <Vector2> edgePoints = new List <Vector2>(); Vector2 edgeNormal = edge.GetNormal(cell); float edgeLength = Vector2.Distance(edge.Point1, edge.Point2); int pointCount = (int)Math.Max(Math.Ceiling(edgeLength / minEdgeLength), 1); Vector2 edgeDir = edge.Point2 - edge.Point1; for (int i = 0; i <= pointCount; i++) { if (i == 0) { edgePoints.Add(edge.Point1); } else if (i == pointCount) { edgePoints.Add(edge.Point2); } else { float centerF = 0.5f - Math.Abs(0.5f - (i / (float)pointCount)); float randomVariance = Rand.Range(0, irregularity, Rand.RandSync.Server); Vector2 extrudedPoint = edge.Point1 + edgeDir * (i / (float)pointCount) + edgeNormal * edgeLength * (roundingAmount + randomVariance) * centerF; var nearbyCells = Level.Loaded.GetCells(extrudedPoint, searchDepth: 2); bool isInside = false; foreach (var nearbyCell in nearbyCells) { if (nearbyCell == cell || nearbyCell.CellType != CellType.Solid) { continue; } //check if extruding the edge causes it to go inside another one if (nearbyCell.IsPointInside(extrudedPoint)) { isInside = true; break; } //check if another edge will be inside this cell after the extrusion Vector2 triangleCenter = (edge.Point1 + edge.Point2 + extrudedPoint) / 3; foreach (GraphEdge nearbyEdge in nearbyCell.Edges) { if (!MathUtils.LinesIntersect(nearbyEdge.Point1, triangleCenter, edge.Point1, extrudedPoint) && !MathUtils.LinesIntersect(nearbyEdge.Point1, triangleCenter, edge.Point2, extrudedPoint) && !MathUtils.LinesIntersect(nearbyEdge.Point1, triangleCenter, edge.Point1, edge.Point2)) { isInside = true; break; } } if (isInside) { break; } } if (!isInside) { edgePoints.Add(extrudedPoint); } } } for (int i = 0; i < edgePoints.Count - 1; i++) { tempEdges.Add(new GraphEdge(edgePoints[i], edgePoints[i + 1]) { Cell1 = edge.Cell1, Cell2 = edge.Cell2, IsSolid = edge.IsSolid, Site1 = edge.Site1, Site2 = edge.Site2, OutsideLevel = edge.OutsideLevel, NextToCave = edge.NextToCave, NextToMainPath = edge.NextToMainPath, NextToSidePath = edge.NextToSidePath }); } } cell.Edges = tempEdges; }
public static List <VoronoiCell> GeneratePath( List <VoronoiCell> targetCells, List <VoronoiCell> cells, List <VoronoiCell>[,] cellGrid, int gridCellSize, Rectangle limits, float wanderAmount = 0.3f, bool mirror = false) { Stopwatch sw2 = new Stopwatch(); sw2.Start(); //how heavily the path "steers" towards the endpoint //lower values will cause the path to "wander" more, higher will make it head straight to the end wanderAmount = MathHelper.Clamp(wanderAmount, 0.0f, 1.0f); List <GraphEdge> allowedEdges = new List <GraphEdge>(); List <VoronoiCell> pathCells = new List <VoronoiCell>(); VoronoiCell currentCell = targetCells[0]; currentCell.CellType = CellType.Path; pathCells.Add(currentCell); int currentTargetIndex = 1; int iterationsLeft = cells.Count; do { int edgeIndex = 0; allowedEdges.Clear(); foreach (GraphEdge edge in currentCell.edges) { if (!limits.Contains(edge.AdjacentCell(currentCell).Center)) { continue; } allowedEdges.Add(edge); } //steer towards target if (Rand.Range(0.0f, 1.0f, false) > wanderAmount || allowedEdges.Count == 0) { for (int i = 0; i < currentCell.edges.Count; i++) { if (!MathUtils.LinesIntersect(currentCell.Center, targetCells[currentTargetIndex].Center, currentCell.edges[i].point1, currentCell.edges[i].point2)) { continue; } edgeIndex = i; break; } } //choose random edge (ignoring ones where the adjacent cell is outside limits) else { //if (allowedEdges.Count==0) //{ // edgeIndex = Rand.Int(currentCell.edges.Count, false); //} //else //{ edgeIndex = Rand.Int(allowedEdges.Count, false); if (mirror && edgeIndex > 0) { edgeIndex = allowedEdges.Count - edgeIndex; } edgeIndex = currentCell.edges.IndexOf(allowedEdges[edgeIndex]); //} } currentCell = currentCell.edges[edgeIndex].AdjacentCell(currentCell); currentCell.CellType = CellType.Path; pathCells.Add(currentCell); iterationsLeft--; if (currentCell == targetCells[currentTargetIndex]) { currentTargetIndex += 1; if (currentTargetIndex >= targetCells.Count) { break; } } } while (currentCell != targetCells[targetCells.Count - 1] && iterationsLeft > 0); Debug.WriteLine("gettooclose: " + sw2.ElapsedMilliseconds + " ms"); sw2.Restart(); return(pathCells); }