using System; using System.Collections; using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; using UnityEngine.Tilemaps; using UnityEngine.UIElements; using static UnityEditor.Progress; //[Serializable] //public class Ore //{ // public string name; // /// // /// The lower the numer the higher the amount of ores that will spawn // /// Higher number means less ore // /// // [Tooltip("The lower the numer the higher the amount of ores that will spawn. Higher number means less ore.")] // [Range(1, 100000)] // public int weight; // /// // /// The lower the number the more dense the ore will spawn (big clusters // /// Higher number means little clusters (more spread) // /// // [Tooltip("The lower the number the more dense the ore will spawn (big clusters. Higher number means little clusters (more spread).")] // [Range(10, 100000)] // public int clusterWeight; // public CustomRuleTile tile; // public int maxSpawnHeight; // public int minSpawnHeight; //} public class GenerateTileMap : MonoBehaviour { public int? seed; public static int maxWidth = 256; public static int maxHeight = 384; public static int maxGroundHeight = 256; private float scale; private float offsetX; private float offsetY; Tilemap tilemap; public CustomRuleTile forestRuleTile; public TileBase borderTile; public List generateables; //public List tiles; private void Awake() { tilemap = GetComponent(); #if DEBUG seed = 0123456789; #endif if (seed == null) { seed = GenerateSeed(9); } SetSettingsFromSeed(seed.Value); transform.position = new Vector3((maxWidth / 2) * -1, (maxGroundHeight + 1) * -1, transform.position.z); LoadGenerateablesFromResources(); } private void LoadGenerateablesFromResources() { // Load all Item prefabs from the "Resources/Items" folder GameObject[] generateablePrefabs = Resources.LoadAll("Generateable"); generateables = new List(); foreach (GameObject prefab in generateablePrefabs) { Generateable generateable = prefab.GetComponent(); if (generateable != null) { generateable.tile = ScriptableObject.CreateInstance(); generateable.tile.m_DefaultGameObject = prefab; generateable.tile.m_DefaultSprite = generateable.sprite; generateables.Add(generateable); forestRuleTile.siblings.Add(generateable.tile); } else { Debug.LogWarning($"Prefab {prefab.name} does not have an Item component"); } } GenerateableDatabase.Instance.InitializeFromGenerateables(generateables); if (generateables.Count == 0) { Debug.LogWarning("No items found in Resources/Items folder"); } } public void SetSettingsFromSeed(int seed) { UnityEngine.Random.State randomState = UnityEngine.Random.state; UnityEngine.Random.InitState(seed); scale = UnityEngine.Random.Range(17f, 23f); offsetX = UnityEngine.Random.Range(-10000f, 10000f); offsetY = UnityEngine.Random.Range(-10000f, 10000f); UnityEngine.Random.state = randomState; } private int GenerateSeed(int size) { System.Random rand = new System.Random(); string seedNumbers = "0123456789"; char[] chars = new char[size]; for (int i = 0; i < size; i++) { chars[i] = seedNumbers[rand.Next(seedNumbers.Length)]; } return int.Parse(new string(chars)); } public IEnumerator GenerateTiles(Action finishedCallback, List destroyedTiles) { // generate ground for (int x = 1; x < maxWidth; x++) { for (int y = 1; y < maxGroundHeight; y++) { float xPerlin = ((float)x / maxWidth) * scale + offsetX; float yPerlin = ((float)y / maxHeight) * scale + offsetY; float perlinNoise = Mathf.PerlinNoise(xPerlin, yPerlin); if (perlinNoise <= 0.7f) { Vector3Int tileSpawnCoord = new Vector3Int(x, y); if (!destroyedTiles.Contains(tileSpawnCoord)) { tilemap.SetTile(tileSpawnCoord, forestRuleTile); } } } // Update UI every 8 lines if ((x % 8) == 0) { yield return null; } } if (generateables != null) { foreach (Generateable generateable in generateables) { for (int x = 0; x < maxWidth; x++) { for (int y = generateable.minSpawnHeight; y < generateable.maxSpawnHeight; y++) { float xPerlin = ((float)x / maxWidth) * (float)generateable.clusterWeight + offsetX; float yPerlin = ((float)y / maxHeight) * (float)generateable.clusterWeight + offsetY; float perlinNoise = Mathf.PerlinNoise(xPerlin, yPerlin); if (perlinNoise <= (1f / (float)generateable.weight)) { Vector3Int tileSpawnCoord = new Vector3Int(x, y); if (!destroyedTiles.Contains(tileSpawnCoord) && tilemap.HasTile(tileSpawnCoord)) { // Check potential cluster size before placing int clusterSize = CountPotentialClusterSize(x, y, generateable.weight, generateable.clusterWeight); if (clusterSize >= generateable.minClusterSize) { tilemap.SetTile(tileSpawnCoord, generateable.tile); } //tilemap.SetTile(tileSpawnCoord, generateable.tile); } } } // Update UI every 8 lines if ((x % 8) == 0) { yield return null; } } } } // generate borders for (int x = 0; x <= maxWidth; x += maxWidth) { for (int y = 0; y <= maxHeight; y++) { tilemap.SetTile(new Vector3Int(x, y), borderTile); } } yield return null; for (int y = 0; y <= maxHeight; y += maxHeight) { for (int x = 1; x <= maxWidth; x++) { tilemap.SetTile(new Vector3Int(x, y), borderTile); } } yield return null; finishedCallback(); } private int CountPotentialClusterSize(int startX, int startY, int weight, int clusterWeight) { int size = 0; Queue toCheck = new Queue(); HashSet checked_positions = new HashSet(); toCheck.Enqueue(new Vector2Int(startX, startY)); checked_positions.Add(new Vector2Int(startX, startY)); while (toCheck.Count > 0) { Vector2Int current = toCheck.Dequeue(); size++; // Check all 8 neighboring tiles for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { if (dx == 0 && dy == 0) continue; Vector2Int neighbor = new Vector2Int(current.x + dx, current.y + dy); if (checked_positions.Contains(neighbor)) continue; float xPerlin = ((float)neighbor.x / maxWidth) * clusterWeight + offsetX; float yPerlin = ((float)neighbor.y / maxHeight) * clusterWeight + offsetY; float perlinNoise = Mathf.PerlinNoise(xPerlin, yPerlin); if (perlinNoise <= (1f / (float)weight)) { toCheck.Enqueue(neighbor); checked_positions.Add(neighbor); } } } } return size; } }