Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using System;
- using Unity.Mathematics;
- using UnityEditor;
- public class WorldGenerator : MonoBehaviour
- {
- // Base World Data
- public int ChunkSize = 75;
- public string Seed = "";
- [Range(1f, 300f)]
- public float PerlinScale = 100f;
- [Range(1, 100)]
- public int PerlinOctaves = 5;
- public float persistence = 2f;
- public float lacunarity = 2f;
- [Range(0.001f, 3.000f)]
- public float PerlinBaseAmplitude = 1f;
- // Random World Offset
- private float xOffset;
- private float yOffset;
- // Pseudo Random Number Generator
- private System.Random pseudoRandom;
- // Chunk Data Split into Sections (Each Chunk having Coords (x, y))
- public Dictionary<string, WorldChunk> chunks = new Dictionary<string, WorldChunk>();
- public List<WorldChunk> ActiveChunks = new List<WorldChunk>();
- // Get previous load position in world
- private Vector2Int prevChunkPos = new Vector2Int(0,0);
- //============================================================
- // Set Warm-Up Data
- //============================================================
- private void Awake() {
- // Get/Create Seed
- if (Seed == ""){
- Seed = GenerateRandomSeed();
- }
- // Get Random Number Generator
- pseudoRandom = new System.Random(Seed.GetHashCode());
- // Set Offsets from seed (new world each time)
- xOffset = pseudoRandom.Next(-10000, 10000);
- yOffset = pseudoRandom.Next(-10000, 10000);
- // Using to Clear while Making Test Adjustments
- chunks.Clear();
- ActiveChunks.Clear();
- // Generate Starting Chunk
- for (int x = -1; x <= 1; x++)
- {
- for (int y = -1; y <= 1; y++)
- {
- // Draw Test Chunks
- GenerateChunk(x, y);
- WorldChunk c = GetChunk(x,y);
- // Enable the Chunk and Add it To The Active List
- c.enabled = true;
- ActiveChunks.Add(c);
- }
- }
- }
- private void Update() {
- int2 chunkPos = new int2(0, 0);
- // Get X/Y Offsets for camera to correctly load new chunks (this will be updated to work with camera bounds later instead this is just temporary)
- if (Camera.main.transform.position.x >= 0){
- chunkPos.x = (int)((Camera.main.transform.position.x+ChunkSize/2) / ChunkSize);
- } else {
- chunkPos.x = (int)((Camera.main.transform.position.x-ChunkSize/2) / ChunkSize);
- }
- if (Camera.main.transform.position.y >= 0){
- chunkPos.y = (int)((Camera.main.transform.position.y+ChunkSize/2) / ChunkSize);
- } else {
- chunkPos.y = (int)((Camera.main.transform.position.y-ChunkSize/2) / ChunkSize);
- }
- // Generate new Chunks if they don't exist & Enable Active Chunks
- EnableChunks(chunkPos);
- }
- // ===
- // Enable Chunk at position and surrounding chunks, disable all active chunks outsize area
- // ===
- private void EnableChunks(int2 chunkPosition){
- // Check if needs to update
- Vector2Int cpos = new Vector2Int(chunkPosition.x, chunkPosition.y);
- if (!PositionIsNew(cpos)){
- return;
- }
- // Get offsets
- List<int2> offsets = new List<int2>();
- // Add 1, 1 & -1, -1 and so on for full square
- offsets.Add(new int2(0, 1));
- offsets.Add(new int2(0, -1));
- offsets.Add(new int2(1, 0));
- offsets.Add(new int2(-1, 0));
- offsets.Add(new int2(0, 0));
- // Set all Active Chunks Inactive
- foreach (int2 offset in offsets)
- {
- for (int i = 0; i < ActiveChunks.Count; i++)
- {
- // Check if the Chunk exists in active List (this is to reduce lag when loading and unloading chunks)
- // So you don't cycle through all the chunks to see what is enabled
- if (ActiveChunks[i] != null){
- // If it does disable it and remove it from the list
- ActiveChunks[i].enabled = false;
- ActiveChunks.RemoveAt(i);
- }
- }
- }
- // Cycle offsets and Enable All Chunks within
- foreach (int2 offset in offsets)
- {
- WorldChunk offsetChunk = GetChunk(chunkPosition.x + offset.x, chunkPosition.y + offset.y);
- offsetChunk.enabled = true;
- ActiveChunks.Add(offsetChunk);
- }
- prevChunkPos = cpos;
- }
- // ===
- // BOOL - Check if current pos is the same as old
- // ===
- public bool PositionIsNew(Vector2Int position){
- return (prevChunkPos != position);
- }
- //============================================================
- // Generation Code
- //============================================================
- // ===
- // Create New Chunks
- // ===
- public void GenerateChunk(int x, int y){
- // Set Key to use
- string key = $"{x},{y}";
- // Check if key exists if not Generate New Chunk
- if (!chunks.ContainsKey(key)){
- // Add Chunk, Set Position in chunk grid (for calling and block data later), Then Generate data
- chunks.Add(key, new WorldChunk(ChunkSize));
- chunks[key].Position = new int2(x, y);
- GenerateChunkData(chunks[key]);
- }
- }
- // ===
- // Get Current Chunk From X/Y or INt2
- // ===
- public WorldChunk GetChunk(int x, int y){
- // Set Key to use
- string key = $"{x},{y}";
- // Check if key exists if not Generate New Chunk
- if (chunks.ContainsKey(key)){
- return chunks[key];
- } else {
- GenerateChunk(x,y);
- return chunks[key];
- }
- }
- public WorldChunk GetChunk(int2 intpos){
- return GetChunk(intpos.x, intpos.y);
- }
- // ===
- // Fill Chunks with Perlin Data
- // ===
- private void GenerateChunkData(WorldChunk chunk){
- // Set min / max
- float maxNoiseHeight = float.MaxValue;
- float minNoiseHeight = float.MinValue;
- // Set Max Height for World
- float maxPossibleHeight = 0;
- float amplitude = PerlinBaseAmplitude;
- for (int i = 0; i < PerlinOctaves; i++)
- {
- maxPossibleHeight += amplitude;
- amplitude *= persistence;
- }
- // Set Data to Chunk
- for (int x = 0; x <= ChunkSize; x++)
- {
- for (int y = 0; y <= ChunkSize; y++)
- {
- int2 cellPos = chunk.CellPosition(x, y);
- amplitude = PerlinBaseAmplitude;
- float freq = 1;
- float noiseHeight = 0;
- // Get Perlin Map
- for (int i = 0; i < PerlinOctaves; i++)
- {
- float px = (float)(cellPos.x + xOffset) / PerlinScale * freq + xOffset;
- float py = (float)(cellPos.y + yOffset) / PerlinScale * freq + yOffset;
- // Set Temp Sample For Testing (This will change for Map Data (Hills and Water) later)
- float PerlinValue = Mathf.PerlinNoise(px, py) * 2 - 1;
- noiseHeight += PerlinValue * amplitude;
- // Increase amp and freq
- amplitude *= persistence;
- freq *= lacunarity;
- }
- // Adjust Min and Max
- if (noiseHeight > maxNoiseHeight){
- maxNoiseHeight = noiseHeight;
- } else if (noiseHeight < minNoiseHeight){
- minNoiseHeight = noiseHeight;
- }
- // Set Sample for Chunk
- chunk.Sample[x,y] = noiseHeight;
- }
- }
- // Normalize Sample to fit world Sample Height
- for (int x = 0; x <= ChunkSize; x++)
- {
- for (int y = 0; y <= ChunkSize; y++)
- {
- float normalizedHeight = (chunk.Sample[x,y] + 1) / (2f * maxPossibleHeight / 1.75f);
- chunk.Sample[x,y] = Mathf.Clamp(normalizedHeight, 0, int.MaxValue);
- }
- }
- }
- // ===
- // Generate Random Seed of Length
- // ===
- private string GenerateRandomSeed(int maxCharAmount = 10, int minCharAmount = 10){
- //Set Characters To Pick from
- const string glyphs= "abcdefghijklmnopqrstuvwxyz0123456789";
- //Set Length from min to max
- int charAmount = UnityEngine.Random.Range(minCharAmount, maxCharAmount);
- // Set output Variable
- string output = "";
- // Do Random Addition
- for(int i=0; i<charAmount; i++)
- {
- output += glyphs[UnityEngine.Random.Range(0, glyphs.Length)];
- }
- // Output New Random String
- return output;
- }
- //============================================================
- // Draw Example
- //============================================================
- private void OnDrawGizmos() {
- if (!EditorApplication.isPlaying){
- // For Live Editing Values before playing (Must enable Gizmos)
- Awake();
- }
- // For Each WorldChunk in the chunk Data
- foreach (WorldChunk c in chunks.Values)
- {
- // Check if it exists (Foreach is stupid sometimes... When live editing)
- if (c != null){
- if (c.enabled == true){
- // Get World Positions for Chunk (Should probably Set to a Variable in the Chunk Data)
- Vector3 ChunkPosition = new Vector3(c.Position.x * ChunkSize, c.Position.y * ChunkSize);
- // For Each X & For Each Y in the chunk
- for (int x = 0; x <= ChunkSize; x++)
- {
- for (int y = 0; y <= ChunkSize; y++)
- {
- // Get Cell position
- Vector3 cellPos = c.VectorCellPosition(x,y);
- // Get Temp Sample and set to color
- float samp = c.Sample[x,y];
- if (cellPos.x == 0 && cellPos.y == 0 && c.Position.x == 0 && c.Position.y == 0){
- Gizmos.color = new Color(0f, 1f, 0f);
- }else {
- Gizmos.color = new Color(samp, samp, samp);
- }
- // Draw Tile as Sample black or white.
- Gizmos.DrawCube(cellPos, Vector3.one);
- }
- }
- }
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement