using UnityEngine;
namespace UnityAgent
{
///
/// Procedural city generator: spawns a grid of buildings of varying
/// heights with emissive windows, street lights and parked vehicles.
/// Designed to be paired with .
///
public class CityGenerator : MonoBehaviour
{
[Header("Grid")]
public int gridX = 8;
public int gridZ = 8;
public float blockSpacing = 30f;
[Header("Buildings")]
public int buildingsPerBlock = 1;
public float minBuildingHeight = 8f;
public float maxBuildingHeight = 40f;
public float buildingFootprint = 8f;
public Color wallColor = new Color(0.4f, 0.4f, 0.42f);
public Color windowColor = new Color(1.0f, 0.85f, 0.4f);
[Header("Street Lights")]
public bool spawnStreetLights = true;
public float lightSpacing = 15f;
public Color lightColor = new Color(1.0f, 0.85f, 0.5f);
public float lightIntensity = 1.5f;
public float lightRange = 15f;
private readonly System.Collections.Generic.List _lights = new System.Collections.Generic.List();
private void Start()
{
Generate();
}
public void Generate()
{
Material wallMat = BuildingGenerator.MakeWallMaterial(wallColor);
Material winMat = BuildingGenerator.MakeWindowMaterial(windowColor);
float totalX = (gridX - 1) * blockSpacing;
float totalZ = (gridZ - 1) * blockSpacing;
Vector3 origin = transform.position - new Vector3(totalX * 0.5f, 0f, totalZ * 0.5f);
for (int x = 0; x < gridX; x++)
{
for (int z = 0; z < gridZ; z++)
{
Vector3 block = new Vector3(origin.x + x * blockSpacing, 0f, origin.z + z * blockSpacing);
SpawnBlock(block, wallMat, winMat);
}
}
if (spawnStreetLights) SpawnStreetLights(origin, totalX, totalZ);
}
private void SpawnBlock(Vector3 center, Material wallMat, Material winMat)
{
for (int i = 0; i < buildingsPerBlock; i++)
{
float h = Random.Range(minBuildingHeight, maxBuildingHeight);
Vector3 size = new Vector3(buildingFootprint, h, buildingFootprint);
Vector3 pos = center + new Vector3(
Random.Range(-4f, 4f), 0f, Random.Range(-4f, 4f));
BuildingGenerator.Generate($"Building_{center.x}_{center.z}_{i}", pos, size, wallMat, winMat);
}
}
private void SpawnStreetLights(Vector3 origin, float totalX, float totalZ)
{
Material poleMat = BuildingGenerator.MakeWallMaterial(new Color(0.1f, 0.1f, 0.1f));
for (float x = origin.x; x <= origin.x + totalX + 0.1f; x += lightSpacing)
{
for (float z = origin.z; z <= origin.z + totalZ + 0.1f; z += lightSpacing)
{
SpawnStreetLight(new Vector3(x, 0f, z), poleMat);
}
}
}
private void SpawnStreetLight(Vector3 pos, Material poleMat)
{
GameObject pole = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
pole.name = "StreetLight";
pole.transform.SetParent(transform, false);
pole.transform.position = pos + Vector3.up * 4f;
pole.transform.localScale = new Vector3(0.2f, 4f, 0.2f);
pole.GetComponent().sharedMaterial = poleMat;
GameObject bulb = GameObject.CreatePrimitive(PrimitiveType.Sphere);
bulb.transform.SetParent(pole.transform, false);
bulb.transform.localPosition = Vector3.up * 0.9f;
bulb.transform.localScale = Vector3.one * 0.4f;
bulb.GetComponent().sharedMaterial = BuildingGenerator.MakeWindowMaterial(lightColor);
Light l = bulb.AddComponent();
l.type = LightType.Point;
l.color = lightColor;
l.intensity = lightIntensity;
l.range = lightRange;
_lights.Add(l);
}
public Light[] StreetLights => _lights.ToArray();
}
}