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); }
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); }
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); }