using System.Threading; using System.Collections; using System.Collections.Generic; using UnityEngine; using Unity.Mathematics; [System.Serializable] public struct Octave { public float amplitude; public float frequency; public NoiseType type; } [System.Serializable] public enum NoiseType { MathfPerlin, MathematicsPerlin, Simplex, Celluar } public class TerrainGenerator : MonoBehaviour { [Header("User settings")] public List<Octave> octaves; [Tooltip("The seed of the generation.")] public Vector2 crawlStartPosition; public bool generateNewSeed = true; public bool refresh; [Tooltip("How fine grain the generated terrain will be. " + "Larger values = more precise. Recommended value is 513.")] public int samples = 513; [Header("Autogenerated values")] [Tooltip("Which terrain grid are we on right now?")] public Vector2Int currentGridPosition; [Tooltip("Dictionary of previously loaded terrains.")] public Dictionary<Vector2Int, TerrainData> previouslyLoaded = new(); /// <summary> /// Threads used for generating the neighboring and current terrains. /// </summary> private Thread[] generationThreads = new Thread[9]; private List<TerrainGenThread> generators = new(); private void ThreadGenerationFinished(TerrainGenThread genThread) { genThread.TerrainData.SetHeights(0, 0, genThread.GetHeights()); GameObject obj = Terrain.CreateTerrainGameObject(genThread.TerrainData); obj.name = genThread.InitialCrawlPosition.ToString() + "Terrain"; obj.transform.SetParent(transform, false); Vector3 offset = new Vector3(genThread.GridPosition.x, 0, genThread.GridPosition.y) * genThread.TerrainData.size.x; obj.transform.position += offset; } private void GenerateThread(Vector2 crawlPos, Vector2Int gridPos, ref Thread thread, ref List<TerrainGenThread> genThreads, bool forceRefresh) { if (!previouslyLoaded.ContainsKey(gridPos) || forceRefresh) { TerrainData data = Instantiate(Resources.Load<TerrainData>("Template")); TerrainGenThread genThread = new(crawlPos, gridPos, octaves, samples, data); genThreads.Add(genThread); ThreadStart starter = new ThreadStart(genThread.Generate); thread = new Thread(starter); previouslyLoaded[gridPos] = data; thread.Start(); } } public virtual void GenerateSurronding(Vector2 initCrawlPos, bool forceRefresh = false) { int count = 0; for (int i = -1; i <= 1; i++) { for (int j = 1; j >= -1; j--) { GenerateThread(initCrawlPos + new Vector2(i, j), currentGridPosition + new Vector2Int(i, j), ref generationThreads[count], ref generators, forceRefresh); count++; } } StartCoroutine(JoinThreads(10f)); } private IEnumerator JoinThreads(float time) { yield return new WaitForSecondsRealtime(time); foreach (var thread in generationThreads) { if (thread != null) { thread.Join(); } } foreach (var genThread in generators) { if (genThread != null) { ThreadGenerationFinished(genThread); } } generators.Clear(); } private void Start() { GenerateSurronding(crawlStartPosition); } private void Update() { if (refresh) { refresh = false; foreach (Transform child in transform) Destroy(child.gameObject); GenerateSurronding(crawlStartPosition, true); } } }