Example #1
0
    private GameObject PlaceStreetLamps(Terrain terrain)
    {
        var result = new GameObject("Lamps");

        List <Vector2[]> pathLines = new List <Vector2[]>();

        // Collect all roads
        foreach (var areaData in AreaDataGraph.GetAllNodeData())
        {
            var data       = areaData;
            var validPaths = areaData.Paths.Where(path => path.All(vtx => vtx.IsInsidePolygon(data.Polygon))).ToList();
            pathLines = pathLines.Union(validPaths).ToList();
        }

        // Place lamps along the paths, alternating side and skipping one
        bool spawnRight = true;
        bool skip       = true;

        foreach (var line in pathLines)
        {
            skip = !skip;
            if (skip)
            {
                continue;
            }

            var pathCenter = (line[0] + line[1]) / 2f;
            var normal     = spawnRight ? (Vector2)Vector3.Cross(line[0] - line[1], Vector3.back).normalized : (Vector2)Vector3.Cross(line[0] - line[1], Vector3.forward).normalized;
            var position2D = pathCenter + normal * LampPathOffset;
            var position   = new Vector3(position2D.x, 0, position2D.y);
            var rotation   = Quaternion.LookRotation(new Vector3(pathCenter.x, 0, pathCenter.y) - position, Vector3.up);

            position += new Vector3(0, terrain.SampleHeight(position), 0);

            var lamp = Object.Instantiate(StreetLamp, position, rotation);
            lamp.CorrectAngleTolerance(LampAngleTolerance);
            lamp.transform.parent = result.transform;
            var lampClear = lamp.GetComponent <BoxCollider>().Get2DPolygon();
            ClearPolygons.Add(lampClear.OffsetToCenter(lampClear.GetPolygonCenter(), -1).ToArray());

            // Flip side
            spawnRight = !spawnRight;
        }

        return(result);
    }
Example #2
0
    private GameObject PlaceChurch(AreaData areaData)
    {
        var building2DPolygon = ChurchPrefab.GetComponent <BoxCollider>().Get2DPolygon();
        var border            = areaData.Polygon;

        // Try to place the building on the side of each path
        foreach (var path in areaData.Paths)
        {
            if (path.Any(vtx => !vtx.IsInsidePolygon(areaData.Polygon)))
            {
                continue;
            }

            var pathCenter  = (path[0] + path[1]) / 2f;
            var leftNormal  = (Vector2)Vector3.Cross(path[0] - path[1], Vector3.forward).normalized;
            var rightNormal = -leftNormal;


            float rightAngle = Vector2.SignedAngle(Vector2.up, leftNormal);
            var   rightPoly  = building2DPolygon.Select(vtx => (Vector2)(Quaternion.AngleAxis(rightAngle, Vector3.forward) * vtx) + pathCenter + rightNormal * PathOffset).ToList();

            float leftAngle = Vector2.SignedAngle(Vector2.up, rightNormal);
            var   leftPoly  = building2DPolygon.Select(vtx => (Vector2)(Quaternion.AngleAxis(leftAngle, Vector3.forward) * vtx) + pathCenter + leftNormal * PathOffset).ToList();

            Vector2 leftCenter  = leftPoly.GetPolygonCenter();
            Vector2 rightCenter = rightPoly.GetPolygonCenter();

            // Try right side placement
            bool rightInvalid = rightPoly.Any(vtx => !vtx.IsInsidePolygon(border)) ||
                                ClearPolygons.Any(clearPoly => rightPoly.Any(vtx => vtx.IsInsidePolygon(clearPoly)) ||
                                                  clearPoly.Any(vtx => vtx.IsInsidePolygon(rightPoly)) ||
                                                  rightCenter.IsInsidePolygon(clearPoly) ||
                                                  clearPoly.GetPolygonCenter().IsInsidePolygon(rightPoly));

            if (!rightInvalid)
            {
                ClearPolygons.Add(rightPoly.ToArray());
                var position2D = pathCenter + rightNormal * PathOffset;
                var position   = new Vector3(position2D.x, 0, position2D.y);
                var rotation   = Quaternion.LookRotation(new Vector3(pathCenter.x, 0, pathCenter.y) - position,
                                                         Vector3.up);
                var go = Object.Instantiate(ChurchPrefab, position, rotation);
                return(go);
            }

            // Try left side placement
            bool leftInvalid = leftPoly.Any(vtx => !vtx.IsInsidePolygon(border)) ||
                               ClearPolygons.Any(clearPoly => leftPoly.Any(vtx => vtx.IsInsidePolygon(clearPoly)) ||
                                                 clearPoly.Any(vtx => vtx.IsInsidePolygon(leftPoly) ||
                                                               leftCenter.IsInsidePolygon(clearPoly)) ||
                                                 clearPoly.GetPolygonCenter().IsInsidePolygon(leftPoly));

            if (!leftInvalid)
            {
                ClearPolygons.Add(leftPoly.ToArray());
                var position2D = pathCenter + leftNormal * PathOffset;
                var position   = new Vector3(position2D.x, 0, position2D.y);
                var rotation   = Quaternion.LookRotation(new Vector3(pathCenter.x, 0, pathCenter.y) - position,
                                                         Vector3.up);
                var go = Object.Instantiate(ChurchPrefab, position, rotation);
                return(go);
            }
        }

        return(null);
    }
Example #3
0
    private List <Vector2[]> GetValidRectangles(AreaData areaData)
    {
        var result = new List <Vector2[]>();
        var count  = 0;

        do
        {
            var     fitPossible = false;
            Vector2 topLeft     = Vector2.zero;
            Vector2 bottomRight = Vector2.zero;
            Vector2 topRight    = Vector2.zero;
            Vector2 bottomLeft  = Vector2.zero;
            Vector2 up          = Vector2.zero;
            Vector2 left        = Vector2.zero;

            // Find a suitable start point for the rectangle
            bool wasRight = false;
            foreach (var path in areaData.Paths)
            {
                if (path.Any(vtx => !vtx.IsInsidePolygon(areaData.Polygon)))
                {
                    continue;
                }

                Vector2 pathCenter  = (path[0] + path[1]) / 2f;
                Vector2 leftNormal  = Vector3.Cross(path[0] - path[1], Vector3.forward).normalized;
                Vector2 rightNormal = -leftNormal;
                float   offset      = (PathOffset + 3);

                // Try right side placement
                bottomRight = pathCenter + rightNormal * offset;
                bool rightInvalid = ClearPolygons.Any(poly => bottomRight.IsInsidePolygon(poly));
                if (!rightInvalid)
                {
                    up       = rightNormal;
                    left     = Vector3.Cross(leftNormal, Vector3.forward).normalized;
                    wasRight = true;
                    break;
                }

                // Try left side placement
                bottomRight = pathCenter + leftNormal * offset;
                bool leftInvalid = ClearPolygons.Any(poly => bottomRight.IsInsidePolygon(poly));
                if (!leftInvalid)
                {
                    up   = leftNormal;
                    left = Vector3.Cross(leftNormal, Vector3.forward).normalized;
                    break;
                }

                bottomRight = Vector2.zero;
            }

            // Early break if previous search failed
            if (up == Vector2.zero || left == Vector2.zero)
            {
                break;
            }

            // Expand until not possible anymore in both directions
            bottomRight = bottomRight - up * 2 - left * 2;
            topLeft     = bottomRight;
            topRight    = bottomRight;
            bottomLeft  = bottomRight;
            bool expandLeft, expandUp;
            do
            {
                expandLeft = false;
                expandUp   = false;

                // Expand left and check
                topLeft    += left;
                bottomLeft += left;
                if (!ClearPolygons.Any(poly => topLeft.IsInsidePolygon(poly)) &&
                    !ClearPolygons.Any(poly => bottomLeft.IsInsidePolygon(poly)) &&
                    topLeft.IsInsidePolygon(areaData.Polygon) &&
                    bottomLeft.IsInsidePolygon(areaData.Polygon))
                {
                    expandLeft  = true;
                    fitPossible = true;
                }
                else
                {
                    topLeft    -= left;
                    bottomLeft -= left;
                }

                // Expand right and check
                topLeft  += up;
                topRight += up;
                if (!ClearPolygons.Any(poly => topLeft.IsInsidePolygon(poly)) &&
                    !ClearPolygons.Any(poly => topRight.IsInsidePolygon(poly)) &&
                    topLeft.IsInsidePolygon(areaData.Polygon) &&
                    topRight.IsInsidePolygon(areaData.Polygon))
                {
                    expandUp    = true;
                    fitPossible = true;
                }
                else
                {
                    topLeft  -= up;
                    topRight -= up;
                }
            } while (expandLeft || expandUp);

            // LOOP CONDITION BREAK
            if (!fitPossible)
            {
                break;
            }

            float upGuard   = 4;
            var   rectangle = new[] { bottomRight, bottomLeft, topLeft - up * upGuard, topRight - up * upGuard };
            result.Add(rectangle);
            if (wasRight)
            {
                ClearPolygons.Add(rectangle.OffsetToCenter(rectangle.GetPolygonCenter(), -10).ToArray());
            }
            else
            {
                var sortedRectangle = rectangle.OffsetToCenter(rectangle.GetPolygonCenter(), -10).ToList();
                sortedRectangle.SortVertices(sortedRectangle.GetPolygonCenter());
                ClearPolygons.Add(sortedRectangle.ToArray());
            }
            count++;
        } while (count < 5); // INFINITE LOOP SAFE GUARD

        return(result);
    }