Advertisement
ThomasPixel

CityGenerator

Apr 23rd, 2023
48
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 10.54 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Numerics;
  6. using System.Text;
  7. using TreeEditor;
  8. using Unity.VisualScripting;
  9. using UnityEditor.Experimental.GraphView;
  10. using UnityEngine;
  11. using UnityEngine.Rendering;
  12. using UnityEngine.Tilemaps;
  13. using UnityEngine.UI;
  14. using static UIPopupList;
  15. using static Unity.Burst.Intrinsics.X86.Avx;
  16. using Debug = UnityEngine.Debug;
  17. using Random = UnityEngine.Random;
  18. using Vector2 = UnityEngine.Vector2;
  19.  
  20. public class WorldGenerator : MonoBehaviour
  21. {
  22.     // Info:
  23.     // - Implement "amount of plots"
  24.     //   - How many plots a given city should have, a plot is defined as a seperate grid
  25.     //   - Plots can be player-controlled, ai-controlled, or city- controlled
  26.     //   - Plot type is determined via cityPlotChance and aiCafePlotChance
  27.     //   - Start creating pre-determined plot structures for easy loading
  28.     //     - Parks, existing Cafes, etc.
  29.     //   - Plot buffs, debuffs
  30.     // ??? - World abstract class -> City abstract class -> Plot abstract class?
  31.  
  32.     // TODO:
  33.     // - Fix generation mess, determine best way to generate straight roads and enclosures.
  34.     // - Enclosures will become plots for players
  35.  
  36.     [Header("Generation Edits")]
  37.     public int gridWidth;
  38.     public int enclosureWidth;
  39.     public int seed;
  40.     public float amp;
  41.     public float jitter;
  42.     public float frequency;
  43.     public float borderThreshold;
  44.     public float commercialChance;
  45.     public float lowPopulationChance;
  46.     public float highPopulationChance;
  47.     private int gridSize;
  48.  
  49.     [Header("Maps")]
  50.     public RawImage districtNoiseMap;
  51.     public RawImage districtColorMap;
  52.     public RawImage districtRoadMap;
  53.     Texture2D districtMap;
  54.     Texture2D colorMap;
  55.  
  56.     [Header("Grid Information")]
  57.     public Grid grid;
  58.     public Dictionary<Vector2Int, TileContainer> tilePositions;
  59.  
  60.     [Header("Prefabs and Objects")]
  61.     public Transform parent;
  62.     public GameObject empty;
  63.     public GameObject tileContainer;
  64.     public GameObject tileObject;
  65.     public GameObject firstTileGenerated;
  66.     public GameObject lastTileGenerated;
  67.  
  68.     public DebugManager debugger;
  69.  
  70.     [Header("Tile Prefabs")]
  71.     public GameObject tileGrassPrefab;
  72.     public GameObject tileConcretePrefab;
  73.     public GameObject tileAsphaltPrefab;
  74.     public GameObject tileSpotlightPrefab;
  75.  
  76.  
  77.     void Awake()
  78.     {
  79.         InitializeGrid();
  80.         GenerateDistrctsMap();
  81.         FloodfillDistricts();
  82.         GenerateRoadNetwork();
  83.     }
  84.  
  85.     private void InitializeGrid()
  86.     {
  87.         grid = parent.gameObject.AddComponent<Grid>();
  88.         tilePositions = new Dictionary<Vector2Int, TileContainer>();
  89.     }
  90.  
  91.     /// <summary>
  92.     /// Generates a grayscale image from Voronoi noise, simulating district zones
  93.     /// </summary>
  94.     private void GenerateDistrctsMap()
  95.     {
  96.         // private texture2d
  97.         districtMap = new Texture2D(gridWidth, gridWidth);
  98.         Color[] pix = new Color[gridWidth * gridWidth];
  99.  
  100.         // private int
  101.         seed = UnityEngine.Random.Range(0, 1000);
  102.  
  103.         // From a custom Voronoi class
  104.         SimplexNoise Voronoi = new SimplexNoise(seed, frequency, amp);
  105.  
  106.         // Grid width atm is 60
  107.         float[,] arr = new float[gridWidth, gridWidth];
  108.  
  109.         //Sample the 2D noise and add it into a array.
  110.         for (int y = 0; y < gridWidth; y++)
  111.         {
  112.             for (int x = 0; x < gridWidth; x++)
  113.             {
  114.                 float fx = x / (gridWidth - 1.0f);
  115.                 float fy = y / (gridWidth - 1.0f);
  116.  
  117.                 arr[x, y] = Voronoi.Sample2D(fx, fy);
  118.             }
  119.         }
  120.  
  121.         for (int y = 0; y < gridWidth; y++)
  122.         {
  123.             for (int x = 0; x < gridWidth; x++)
  124.             {
  125.                 float n = arr[x, y];
  126.                 districtMap.SetPixel(x, y, new Color(n, n, n, 1));
  127.             }
  128.         }
  129.  
  130.         districtMap.Apply();
  131.         districtMap.filterMode = FilterMode.Point;
  132.  
  133.         // districtNoiseMap is a RawImage UI element
  134.         Texture2D districtNoise = new Texture2D(gridWidth, gridWidth);
  135.         districtNoise = districtMap;
  136.         districtNoise.Apply();
  137.  
  138.         districtNoiseMap.texture = districtNoise;
  139.     }
  140.  
  141.     /// <summary>
  142.     /// Floodfills district zones from districtMap, uses borderThreshold for checking greyscale values on Voronoi borders.
  143.     /// </summary>
  144.     private void FloodfillDistricts()
  145.     {
  146.         int width = districtMap.width;
  147.         int height = districtMap.height;
  148.  
  149.         List<Color> usedColors = new List<Color>();
  150.  
  151.         // Loop over each pixel in the districts map
  152.         for (int y = 0; y < height; y++)
  153.         {
  154.             for (int x = 0; x < width; x++)
  155.             {
  156.                 Color pixelColor = districtMap.GetPixel(x, y);
  157.  
  158.                 // If the pixel is white (above threshold) and hasn't been colored yet, floodfill it with a random color
  159.                 if (pixelColor.grayscale <= borderThreshold &&
  160.                     !usedColors.Contains(pixelColor))
  161.                 {
  162.                     Color fillColor = ColorChance();
  163.                     FloodfillBlob(x, y, pixelColor, fillColor, ref usedColors);
  164.                 }
  165.             }
  166.         }
  167.  
  168.         Texture2D districtColor = new Texture2D(gridWidth, gridWidth);
  169.         districtColor = districtMap;
  170.         districtColor.Apply();
  171.  
  172.         districtColor.filterMode = FilterMode.Point;
  173.  
  174.         districtColorMap.texture = districtColor;
  175.     }
  176.  
  177.     private Color ColorChance()
  178.     {
  179.         Color[] colors = new Color[] { Color.yellow, Color.blue, Color.green };
  180.  
  181.         // Calculate the total weight of the color choices
  182.         float totalWeight = commercialChance + lowPopulationChance + highPopulationChance;
  183.  
  184.         // Generate a random value between 0 and the total weight
  185.         float randomValue = Random.Range(0f, totalWeight);
  186.  
  187.         // Choose a color based on the random value and the chances
  188.         if (randomValue < commercialChance)
  189.         {
  190.             return Color.blue;
  191.         }
  192.         else if (randomValue < commercialChance + lowPopulationChance)
  193.         {
  194.             return Color.yellow;
  195.         }
  196.         else
  197.         {
  198.             return Color.green;
  199.         }
  200.     }
  201.  
  202.     /// <summary>
  203.     /// Performs a floodfill on the current Voronoi blob. Blob defined as a white-ish space surrounding by black-ish borders
  204.     /// </summary>
  205.     /// <param name="x"></param>
  206.     /// <param name="y"></param>
  207.     /// <param name="targetColor"></param>
  208.     /// <param name="fillColor"></param>
  209.     /// <param name="usedColors"></param>
  210.     private void FloodfillBlob(int x, int y, Color targetColor, Color fillColor, ref List<Color> usedColors)
  211.     {
  212.         int width = districtMap.width;
  213.         int height = districtMap.height;
  214.  
  215.         Queue<Vector2Int> pixelsToCheck = new Queue<Vector2Int>();
  216.         pixelsToCheck.Enqueue(new Vector2Int(x, y));
  217.  
  218.         while (pixelsToCheck.Count > 0)
  219.         {
  220.             Vector2Int pixel = pixelsToCheck.Dequeue();
  221.             int px = pixel.x;
  222.             int py = pixel.y;
  223.  
  224.             // Check if the pixel is within bounds and has the target color
  225.             // TODO: Adjust to check if the pixel is within the range of the white blob
  226.             if (px >= 0 && px < width && py >= 0 && py < height && districtMap.GetPixel(px, py) == targetColor)
  227.             {
  228.                 // Set the pixel to the fill color and add it to the list of used colors
  229.                 districtMap.SetPixel(px, py, fillColor);
  230.                 if (!usedColors.Contains(fillColor))
  231.                 {
  232.                     usedColors.Add(fillColor);
  233.                 }
  234.  
  235.                 // Add neighboring pixels to the queue to be checked
  236.                 pixelsToCheck.Enqueue(new Vector2Int(px - 1, py));
  237.                 pixelsToCheck.Enqueue(new Vector2Int(px + 1, py));
  238.                 pixelsToCheck.Enqueue(new Vector2Int(px, py - 1));
  239.                 pixelsToCheck.Enqueue(new Vector2Int(px, py + 1));
  240.             }
  241.         }
  242.     }
  243.  
  244.     /// <summary>
  245.     /// Generates a road network
  246.     /// </summary>
  247.     private void GenerateRoadNetwork()
  248.     {
  249.         for (int x = 0; x < districtMap.width; x++)
  250.         {
  251.             for (int y = 0; y < districtMap.height; y++)
  252.             {
  253.                 if (x % enclosureWidth == 0) districtMap.SetPixel(x, y, Color.black);
  254.                 if (y % enclosureWidth == 0) districtMap.SetPixel(x, y, Color.black);
  255.             }
  256.         }
  257.  
  258.         districtMap.Apply();
  259.  
  260.         Texture2D districtRoads = new Texture2D(gridWidth, gridWidth);
  261.         districtRoads = districtMap;
  262.         districtRoads.Apply();
  263.  
  264.         districtRoads.filterMode = FilterMode.Point;
  265.  
  266.         districtRoadMap.texture = districtRoads;
  267.     }
  268.  
  269.     private void CreateRoadTileContainer(Vector2Int pos, GameObject prefab, int thickness)
  270.     {
  271.         for (int x = 0; x <= thickness; x++)
  272.         {
  273.             for (int y = 0; y <= thickness; y++)
  274.             {
  275.                 Vector2Int offset = new Vector2Int(x, y);
  276.                 Vector2Int tilePos = pos + offset;
  277.                 TryGetValue(tilePos, out TileContainer tc);
  278.                 if (tc == null) CreateTileContainer(pos, prefab);
  279.             }
  280.         }
  281.     }
  282.  
  283.     private void CreateTileContainer(Vector2Int pos, GameObject tilePrefab)
  284.     {
  285.         // Initiate tileContainer object
  286.         GameObject curTileContainer = Instantiate(tileContainer, parent);
  287.         curTileContainer.name = "TileContainer X:" + (pos.x) + " Y: " + (pos.y);
  288.         curTileContainer.transform.localPosition = new Vector3Int(pos.x, pos.y, 0);
  289.  
  290.         // Get the tile container script
  291.         curTileContainer.TryGetComponent(out TileContainer TC);
  292.  
  293.         // Set TC position
  294.         TC.SetGridPosition(pos);
  295.  
  296.         // Add to WorldGen dictionary
  297.         tilePositions.Add(pos, TC);
  298.         TC.SetGrid(grid);
  299.  
  300.         // Create tiles based on distance to closest center
  301.         TileBase tile = Instantiate(tilePrefab, TC.gameObject.transform).GetComponent<TileBase>();
  302.         TC.AddEntity(tile);
  303.     }
  304.  
  305.  
  306.     public void PositionCamera()
  307.     {
  308.         Vector2 middlePosition = Vector2.Lerp(
  309.         firstTileGenerated.transform.position,
  310.         lastTileGenerated.transform.position,
  311.         0.5f);
  312.         Camera.main.transform.position = middlePosition;
  313.     }
  314.     public TileContainer TryGetValue(Vector2Int position, out TileContainer TC)
  315.     {
  316.         TC = tilePositions.GetValueOrDefault(position);
  317.         return tilePositions.GetValueOrDefault(position);
  318.     }
  319.  
  320. }
  321.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement