Brandons-MassiveLoop-CSharp-Scripts / ChunkLoader.cs
ChunkLoader.cs
Raw
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ML.SDK;

public class ChunkLoader_2 : MonoBehaviour
{
    // Prefabs for the different types of blocks used in terrain generation
    public GameObject grassBlockPrefab;
    public GameObject dirtBlockPrefab;
    public GameObject stoneBlockPrefab;

    // Chunk and world configuration
    public int chunkSize = 16; // Size of a single chunk (e.g., 16x16 blocks)
    public int worldWidth = 10; // Number of chunks in the X direction
    public int worldDepth = 10; // Number of chunks in the Z direction
    public float noiseScale = 8f; // Scale factor for noise generation
    public int noiseHeight = 5; // Maximum height of terrain generated by noise
    public int dirtLayerDepth = 3; // Depth of the dirt layer below the grass

    // Dictionary to store active chunks, indexed by their position
    private Dictionary<Vector3Int, GameObject> activeChunks = new Dictionary<Vector3Int, GameObject>();

    // Button to trigger world generation
    public MLClickable StartButton;

    // Event constants and tokens for handling networked events
    const string EVENT_GEN_WORLD = "OnGenerateWorldEvent";
    private EventToken tokenGen;

    // Called when the player clicks the start button
    public void OnplayerClickStart(MLPlayer player)
    {
        // Invoke the world generation event across the network
        this.InvokeNetwork(EVENT_GEN_WORLD, EventTarget.All, null);
    }

    // Called when the world generation event is received
    public void OnGenWorldEvent(object[] args)
    {
        // Start the coroutine to generate the world
        StartCoroutine(GenerateWorld());
    }

    void Start()
    {
        // Attach event listener to the start button if it exists
        if (StartButton != null)
        {
            StartButton.OnPlayerClick.AddListener(OnplayerClickStart);
        }

        // Register a network event handler for world generation
        tokenGen = this.AddEventHandler(EVENT_GEN_WORLD, OnGenWorldEvent);
    }

    // Coroutine to generate the entire world in layers
    private IEnumerator GenerateWorld()
    {
        // Initialize chunks (divide the world into chunks and store them)
        for (int chunkX = 0; chunkX < worldWidth; chunkX++)
        {
            for (int chunkZ = 0; chunkZ < worldDepth; chunkZ++)
            {
                Vector3Int chunkPos = new Vector3Int(chunkX, 0, chunkZ); // Chunk position in world
                GameObject chunk = new GameObject($"Chunk_{chunkPos.x}_{chunkPos.z}"); // Create a chunk GameObject
                chunk.transform.SetParent(transform); // Parent the chunk to the ChunkLoader
                activeChunks.Add(chunkPos, chunk); // Add chunk to the active chunks dictionary
            }
        }

        // Generate terrain layer by layer
        for (int heightLayer = 0; heightLayer <= noiseHeight + dirtLayerDepth; heightLayer++)
        {
            foreach (var chunkPos in activeChunks.Keys)
            {
                GameObject chunk = activeChunks[chunkPos]; // Get the chunk GameObject
                yield return StartCoroutine(GenerateLayer(chunk, chunkPos, heightLayer)); // Generate this layer for the chunk
            }
        }
    }

    // Coroutine to generate a single terrain layer for a chunk
    private IEnumerator GenerateLayer(GameObject chunk, Vector3Int chunkPos, int heightLayer)
    {
        int batchSize = 32; // Number of blocks to generate per frame
        int blocksGenerated = 0; // Counter for blocks generated

        for (int x = 0; x < chunkSize; x++)
        {
            for (int z = 0; z < chunkSize; z++)
            {
                // Calculate the world position of the block
                int worldX = chunkPos.x * chunkSize + x;
                int worldZ = chunkPos.z * chunkSize + z;

                // Generate the surface height using Perlin noise
                int surfaceHeight = Mathf.RoundToInt(GenerateNoise(worldX, worldZ) * noiseHeight);

                // Determine which block to place based on height
                if (heightLayer == surfaceHeight)
                {
                    // Place grass block at the surface
                    Vector3 grassBlockPos = new Vector3(worldX, heightLayer, worldZ);
                    Instantiate(grassBlockPrefab, grassBlockPos, Quaternion.identity, chunk.transform);
                }
                else if (heightLayer < surfaceHeight && heightLayer >= surfaceHeight - dirtLayerDepth)
                {
                    // Place dirt block below the surface
                    Vector3 dirtBlockPos = new Vector3(worldX, heightLayer, worldZ);
                    Instantiate(dirtBlockPrefab, dirtBlockPos, Quaternion.identity, chunk.transform);
                }
                else if (heightLayer < surfaceHeight - dirtLayerDepth)
                {
                    // Place stone block below the dirt layer
                    Vector3 stoneBlockPos = new Vector3(worldX, heightLayer, worldZ);
                    Instantiate(stoneBlockPrefab, stoneBlockPos, Quaternion.identity, chunk.transform);
                }

                blocksGenerated++; // Increment the block counter

                // Pause after generating a batch of blocks to avoid frame lag
                if (blocksGenerated >= batchSize)
                {
                    blocksGenerated = 0; // Reset the counter
                    yield return null; // Wait for the next frame
                }
            }
        }
    }

    // Function to generate a Perlin noise value for a given position
    private float GenerateNoise(float x, float z)
    {
        return Mathf.PerlinNoise(x / noiseScale, z / noiseScale); // Normalize coordinates by noiseScale
    }
}