예제 #1
0
        /// <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;
        }
예제 #2
0
        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);
        }