Skip to content
Snippets Groups Projects
TerrainGenerator.cs 6.12 KiB
Newer Older
Chang, Ryan's avatar
Chang, Ryan committed
using System.Threading;
Chang, Ryan's avatar
Chang, Ryan committed
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
Chang, Ryan's avatar
Chang, Ryan committed
using Unity.Jobs;
using Unity.Collections;
Chang, Ryan's avatar
Chang, Ryan committed

[System.Serializable]
public struct Octave
{
    public float amplitude;
    public float frequency;
    public NoiseType type;
}

[System.Serializable]
public enum NoiseType
{
    MathfPerlin,
    MathematicsPerlin,
    Simplex,
    Celluar
Chang, Ryan's avatar
Chang, Ryan committed
}

public class TerrainGenerator : MonoBehaviour
{
Chang, Ryan's avatar
Chang, Ryan committed
    [Header("User settings")]
    public List<Octave> octaves;

    [Tooltip("The seed of the generation.")]
    public Vector2 crawlStartPosition;

    public bool generateNewSeed = true;

    public bool refresh;
Chang, Ryan's avatar
Chang, Ryan committed

    [Tooltip("How fine grain the generated terrain will be. " +
        "Larger values = more precise. Recommended value is 513.")]
Chang, Ryan's avatar
Chang, Ryan committed
    public int samples = 513;
Chang, Ryan's avatar
Chang, Ryan committed

Chang, Ryan's avatar
Chang, Ryan committed
    [Header("Autogenerated values")]
    [Tooltip("Which terrain grid are we on right now?")]
    public Vector2Int currentGridPosition;

    [Tooltip("Dictionary of previously loaded terrains.")]
Chang, Ryan's avatar
Chang, Ryan committed
    public Dictionary<Vector2Int, TerrainData> previouslyLoaded = new();
Chang, Ryan's avatar
Chang, Ryan committed

    /// <summary>
Chang, Ryan's avatar
Chang, Ryan committed
    /// Threads used for generating the neighboring and current terrains.
Chang, Ryan's avatar
Chang, Ryan committed
    /// </summary>
Chang, Ryan's avatar
Chang, Ryan committed
    private Thread[] generationThreads = new Thread[9];
Chang, Ryan's avatar
Chang, Ryan committed

Chang, Ryan's avatar
Chang, Ryan committed
    private List<TerrainGenThread> generators = new();
Chang, Ryan's avatar
Chang, Ryan committed
    private List<TerrainGenJob> generatorJobs = new();
    private List<JobHandle> generatorHandles = new();
Chang, Ryan's avatar
Chang, Ryan committed

    private void ThreadGenerationFinished(TerrainGenThread genThread)
Chang, Ryan's avatar
Chang, Ryan committed
    {
Chang, Ryan's avatar
Chang, Ryan committed
        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;
    }

Chang, Ryan's avatar
Chang, Ryan committed
    private void ThreadGenerationFinished(TerrainGenJob genJob)
    {
        TerrainData data = Instantiate(Resources.Load<TerrainData>("Template"));
        data.SetHeights(0, 0, genJob.GetHeights());

        GameObject obj = Terrain.CreateTerrainGameObject(data);
        obj.name = genJob.InitialCrawlPosition.ToString() + "Terrain";
        obj.transform.SetParent(transform, false);
        Vector3 offset = new Vector3(genJob.GridPosition.x, 0, genJob.GridPosition.y) *
            data.size.x;
        obj.transform.position += offset;
    }

Chang, Ryan's avatar
Chang, Ryan committed
    private void GenerateThread(Vector2 crawlPos, Vector2Int gridPos, ref Thread thread,
        ref List<TerrainGenThread> genThreads, bool forceRefresh)
    {
        if (!previouslyLoaded.ContainsKey(gridPos) || forceRefresh)
Chang, Ryan's avatar
Chang, Ryan committed
        {
Chang, Ryan's avatar
Chang, Ryan committed
            TerrainData data = Instantiate(Resources.Load<TerrainData>("Template"));
            TerrainGenThread genThread = new(crawlPos, gridPos, octaves, samples, data);
            genThreads.Add(genThread);
Chang, Ryan's avatar
Chang, Ryan committed
            ThreadStart starter = new(genThread.Generate);
Chang, Ryan's avatar
Chang, Ryan committed
            thread = new Thread(starter);
Chang, Ryan's avatar
Chang, Ryan committed
            previouslyLoaded[gridPos] = data;
            thread.Start();
Chang, Ryan's avatar
Chang, Ryan committed
        }
Chang, Ryan's avatar
Chang, Ryan committed
    }

Chang, Ryan's avatar
Chang, Ryan committed
    private void GenerateThread(Vector2 crawlPos, Vector2Int gridPos, NativeArray<Octave> octaves,
        ref List<TerrainGenJob> genJobs, ref List<JobHandle> handles, bool forceRefresh)
    {
        if (!previouslyLoaded.ContainsKey(gridPos) || forceRefresh)
        {
            TerrainGenJob genJob = new(crawlPos, gridPos, octaves, samples);
            genJobs.Add(genJob);
            JobHandle jobHandle = genJob.Schedule();
            handles.Add(jobHandle);
        }
    }

    private NativeArray<Octave> nOctaves;

Chang, Ryan's avatar
Chang, Ryan committed
    public virtual void GenerateSurronding(Vector2 initCrawlPos, bool forceRefresh = false)
Chang, Ryan's avatar
Chang, Ryan committed
    {
Chang, Ryan's avatar
Chang, Ryan committed
        int count = 0;
Chang, Ryan's avatar
Chang, Ryan committed

        nOctaves = new(octaves.ToArray(), Allocator.Persistent);

Chang, Ryan's avatar
Chang, Ryan committed
        for (int i = -1; i <= 1; i++)
        {
            for (int j = 1; j >= -1; j--)
            {
Chang, Ryan's avatar
Chang, Ryan committed
                //GenerateThread(initCrawlPos + new Vector2(i, j),
                //    currentGridPosition + new Vector2Int(i, j),
                //    ref generationThreads[count], ref generators,
                //    forceRefresh);
Chang, Ryan's avatar
Chang, Ryan committed
                GenerateThread(initCrawlPos + new Vector2(i, j),
                    currentGridPosition + new Vector2Int(i, j),
Chang, Ryan's avatar
Chang, Ryan committed
                    nOctaves,
                    ref generatorJobs, ref generatorHandles,
Chang, Ryan's avatar
Chang, Ryan committed
                    forceRefresh);
                count++;
            }
        }
Chang, Ryan's avatar
Chang, Ryan committed
        StartCoroutine(JoinThreads(10f));
    }

    private IEnumerator JoinThreads(float time)
    {
        yield return new WaitForSecondsRealtime(time);

Chang, Ryan's avatar
Chang, Ryan committed
        foreach (var thread in generationThreads)
Chang, Ryan's avatar
Chang, Ryan committed
            if (thread != null)
            {
Chang, Ryan's avatar
Chang, Ryan committed
                thread.Join();
Chang, Ryan's avatar
Chang, Ryan committed
            }
Chang, Ryan's avatar
Chang, Ryan committed
        // Code to ensure jobs are finished. This should optimally occur
        // sometime after.
        StartCoroutine(JoinOnGeneratorThreads(0.1f));
    }
Chang, Ryan's avatar
Chang, Ryan committed

Chang, Ryan's avatar
Chang, Ryan committed
    private IEnumerator JoinOnGeneratorThreads(float timeToWait)
    {
        yield return new WaitForSecondsRealtime(timeToWait);

        //foreach (var thread in generationThreads)
        //{
        //    if (thread != null)
        //    {
        //        thread.Join();
        //    }
        //}

        //foreach (var genThread in generators)
        //{
        //    if (genThread != null)
        //    {
        //        ThreadGenerationFinished(genThread);
        //    }
        //}

        //generators.Clear();

        foreach (var handle in generatorHandles)
Chang, Ryan's avatar
Chang, Ryan committed
            handle.Complete();
Chang, Ryan's avatar
Chang, Ryan committed
        foreach (var job in generatorJobs)
        {
            ThreadGenerationFinished(job);
        }

        generatorJobs.Clear();
        generatorHandles.Clear();
        nOctaves.Dispose();
Chang, Ryan's avatar
Chang, Ryan committed
    private void Start()
    {
Chang, Ryan's avatar
Chang, Ryan committed
        GenerateSurronding(crawlStartPosition);
Chang, Ryan's avatar
Chang, Ryan committed
    }

    private void Update()
    {
Chang, Ryan's avatar
Chang, Ryan committed
        if (refresh)
        {
            refresh = false;

            foreach (Transform child in transform)
                Destroy(child.gameObject);

            GenerateSurronding(crawlStartPosition, true);
        }
Chang, Ryan's avatar
Chang, Ryan committed
    }
Chang, Ryan's avatar
Chang, Ryan committed

    private void OnDestroy()
    {
        nOctaves.Dispose();
    }
Chang, Ryan's avatar
Chang, Ryan committed
}