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
}
}