Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Numerics;
- using System.Text;
- using TreeEditor;
- using Unity.VisualScripting;
- using UnityEditor.Experimental.GraphView;
- using UnityEngine;
- using UnityEngine.Rendering;
- using UnityEngine.Tilemaps;
- using UnityEngine.UI;
- using static UIPopupList;
- using static Unity.Burst.Intrinsics.X86.Avx;
- using Debug = UnityEngine.Debug;
- using Random = UnityEngine.Random;
- using Vector2 = UnityEngine.Vector2;
- public class WorldGenerator : MonoBehaviour
- {
- // Info:
- // - Implement "amount of plots"
- // - How many plots a given city should have, a plot is defined as a seperate grid
- // - Plots can be player-controlled, ai-controlled, or city- controlled
- // - Plot type is determined via cityPlotChance and aiCafePlotChance
- // - Start creating pre-determined plot structures for easy loading
- // - Parks, existing Cafes, etc.
- // - Plot buffs, debuffs
- // ??? - World abstract class -> City abstract class -> Plot abstract class?
- // TODO:
- // - Fix generation mess, determine best way to generate straight roads and enclosures.
- // - Enclosures will become plots for players
- [Header("Generation Edits")]
- public int gridWidth;
- public int enclosureWidth;
- public int seed;
- public float amp;
- public float jitter;
- public float frequency;
- public float borderThreshold;
- public float commercialChance;
- public float lowPopulationChance;
- public float highPopulationChance;
- private int gridSize;
- [Header("Maps")]
- public RawImage districtNoiseMap;
- public RawImage districtColorMap;
- public RawImage districtRoadMap;
- Texture2D districtMap;
- Texture2D colorMap;
- [Header("Grid Information")]
- public Grid grid;
- public Dictionary<Vector2Int, TileContainer> tilePositions;
- [Header("Prefabs and Objects")]
- public Transform parent;
- public GameObject empty;
- public GameObject tileContainer;
- public GameObject tileObject;
- public GameObject firstTileGenerated;
- public GameObject lastTileGenerated;
- public DebugManager debugger;
- [Header("Tile Prefabs")]
- public GameObject tileGrassPrefab;
- public GameObject tileConcretePrefab;
- public GameObject tileAsphaltPrefab;
- public GameObject tileSpotlightPrefab;
- void Awake()
- {
- InitializeGrid();
- GenerateDistrctsMap();
- FloodfillDistricts();
- GenerateRoadNetwork();
- }
- private void InitializeGrid()
- {
- grid = parent.gameObject.AddComponent<Grid>();
- tilePositions = new Dictionary<Vector2Int, TileContainer>();
- }
- /// <summary>
- /// Generates a grayscale image from Voronoi noise, simulating district zones
- /// </summary>
- private void GenerateDistrctsMap()
- {
- // private texture2d
- districtMap = new Texture2D(gridWidth, gridWidth);
- Color[] pix = new Color[gridWidth * gridWidth];
- // private int
- seed = UnityEngine.Random.Range(0, 1000);
- // From a custom Voronoi class
- SimplexNoise Voronoi = new SimplexNoise(seed, frequency, amp);
- // Grid width atm is 60
- float[,] arr = new float[gridWidth, gridWidth];
- //Sample the 2D noise and add it into a array.
- for (int y = 0; y < gridWidth; y++)
- {
- for (int x = 0; x < gridWidth; x++)
- {
- float fx = x / (gridWidth - 1.0f);
- float fy = y / (gridWidth - 1.0f);
- arr[x, y] = Voronoi.Sample2D(fx, fy);
- }
- }
- for (int y = 0; y < gridWidth; y++)
- {
- for (int x = 0; x < gridWidth; x++)
- {
- float n = arr[x, y];
- districtMap.SetPixel(x, y, new Color(n, n, n, 1));
- }
- }
- districtMap.Apply();
- districtMap.filterMode = FilterMode.Point;
- // districtNoiseMap is a RawImage UI element
- Texture2D districtNoise = new Texture2D(gridWidth, gridWidth);
- districtNoise = districtMap;
- districtNoise.Apply();
- districtNoiseMap.texture = districtNoise;
- }
- /// <summary>
- /// Floodfills district zones from districtMap, uses borderThreshold for checking greyscale values on Voronoi borders.
- /// </summary>
- private void FloodfillDistricts()
- {
- int width = districtMap.width;
- int height = districtMap.height;
- List<Color> usedColors = new List<Color>();
- // Loop over each pixel in the districts map
- for (int y = 0; y < height; y++)
- {
- for (int x = 0; x < width; x++)
- {
- Color pixelColor = districtMap.GetPixel(x, y);
- // If the pixel is white (above threshold) and hasn't been colored yet, floodfill it with a random color
- if (pixelColor.grayscale <= borderThreshold &&
- !usedColors.Contains(pixelColor))
- {
- Color fillColor = ColorChance();
- FloodfillBlob(x, y, pixelColor, fillColor, ref usedColors);
- }
- }
- }
- Texture2D districtColor = new Texture2D(gridWidth, gridWidth);
- districtColor = districtMap;
- districtColor.Apply();
- districtColor.filterMode = FilterMode.Point;
- districtColorMap.texture = districtColor;
- }
- private Color ColorChance()
- {
- Color[] colors = new Color[] { Color.yellow, Color.blue, Color.green };
- // Calculate the total weight of the color choices
- float totalWeight = commercialChance + lowPopulationChance + highPopulationChance;
- // Generate a random value between 0 and the total weight
- float randomValue = Random.Range(0f, totalWeight);
- // Choose a color based on the random value and the chances
- if (randomValue < commercialChance)
- {
- return Color.blue;
- }
- else if (randomValue < commercialChance + lowPopulationChance)
- {
- return Color.yellow;
- }
- else
- {
- return Color.green;
- }
- }
- /// <summary>
- /// Performs a floodfill on the current Voronoi blob. Blob defined as a white-ish space surrounding by black-ish borders
- /// </summary>
- /// <param name="x"></param>
- /// <param name="y"></param>
- /// <param name="targetColor"></param>
- /// <param name="fillColor"></param>
- /// <param name="usedColors"></param>
- private void FloodfillBlob(int x, int y, Color targetColor, Color fillColor, ref List<Color> usedColors)
- {
- int width = districtMap.width;
- int height = districtMap.height;
- Queue<Vector2Int> pixelsToCheck = new Queue<Vector2Int>();
- pixelsToCheck.Enqueue(new Vector2Int(x, y));
- while (pixelsToCheck.Count > 0)
- {
- Vector2Int pixel = pixelsToCheck.Dequeue();
- int px = pixel.x;
- int py = pixel.y;
- // Check if the pixel is within bounds and has the target color
- // TODO: Adjust to check if the pixel is within the range of the white blob
- if (px >= 0 && px < width && py >= 0 && py < height && districtMap.GetPixel(px, py) == targetColor)
- {
- // Set the pixel to the fill color and add it to the list of used colors
- districtMap.SetPixel(px, py, fillColor);
- if (!usedColors.Contains(fillColor))
- {
- usedColors.Add(fillColor);
- }
- // Add neighboring pixels to the queue to be checked
- pixelsToCheck.Enqueue(new Vector2Int(px - 1, py));
- pixelsToCheck.Enqueue(new Vector2Int(px + 1, py));
- pixelsToCheck.Enqueue(new Vector2Int(px, py - 1));
- pixelsToCheck.Enqueue(new Vector2Int(px, py + 1));
- }
- }
- }
- /// <summary>
- /// Generates a road network
- /// </summary>
- private void GenerateRoadNetwork()
- {
- for (int x = 0; x < districtMap.width; x++)
- {
- for (int y = 0; y < districtMap.height; y++)
- {
- if (x % enclosureWidth == 0) districtMap.SetPixel(x, y, Color.black);
- if (y % enclosureWidth == 0) districtMap.SetPixel(x, y, Color.black);
- }
- }
- districtMap.Apply();
- Texture2D districtRoads = new Texture2D(gridWidth, gridWidth);
- districtRoads = districtMap;
- districtRoads.Apply();
- districtRoads.filterMode = FilterMode.Point;
- districtRoadMap.texture = districtRoads;
- }
- private void CreateRoadTileContainer(Vector2Int pos, GameObject prefab, int thickness)
- {
- for (int x = 0; x <= thickness; x++)
- {
- for (int y = 0; y <= thickness; y++)
- {
- Vector2Int offset = new Vector2Int(x, y);
- Vector2Int tilePos = pos + offset;
- TryGetValue(tilePos, out TileContainer tc);
- if (tc == null) CreateTileContainer(pos, prefab);
- }
- }
- }
- private void CreateTileContainer(Vector2Int pos, GameObject tilePrefab)
- {
- // Initiate tileContainer object
- GameObject curTileContainer = Instantiate(tileContainer, parent);
- curTileContainer.name = "TileContainer X:" + (pos.x) + " Y: " + (pos.y);
- curTileContainer.transform.localPosition = new Vector3Int(pos.x, pos.y, 0);
- // Get the tile container script
- curTileContainer.TryGetComponent(out TileContainer TC);
- // Set TC position
- TC.SetGridPosition(pos);
- // Add to WorldGen dictionary
- tilePositions.Add(pos, TC);
- TC.SetGrid(grid);
- // Create tiles based on distance to closest center
- TileBase tile = Instantiate(tilePrefab, TC.gameObject.transform).GetComponent<TileBase>();
- TC.AddEntity(tile);
- }
- public void PositionCamera()
- {
- Vector2 middlePosition = Vector2.Lerp(
- firstTileGenerated.transform.position,
- lastTileGenerated.transform.position,
- 0.5f);
- Camera.main.transform.position = middlePosition;
- }
- public TileContainer TryGetValue(Vector2Int position, out TileContainer TC)
- {
- TC = tilePositions.GetValueOrDefault(position);
- return tilePositions.GetValueOrDefault(position);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement