Advertisement
azrith001

WorldGenerator.cs

Aug 14th, 2020
1,539
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 10.80 KB | None | 0 0
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System;
  5. using Unity.Mathematics;
  6. using UnityEditor;
  7.  
  8. public class WorldGenerator : MonoBehaviour
  9. {
  10.  
  11.     // Base World Data
  12.     public int ChunkSize = 75;
  13.     public string Seed = "";
  14.     [Range(1f, 300f)]
  15.     public float PerlinScale = 100f;
  16.     [Range(1, 100)]
  17.     public int PerlinOctaves = 5;
  18.     public float persistence = 2f;
  19.     public float lacunarity = 2f;
  20.     [Range(0.001f, 3.000f)]
  21.     public float PerlinBaseAmplitude = 1f;
  22.     // Random World Offset
  23.     private float xOffset;
  24.     private float yOffset;
  25.     // Pseudo Random Number Generator
  26.     private System.Random pseudoRandom;
  27.  
  28.     // Chunk Data Split into Sections (Each Chunk having Coords (x, y))
  29.     public Dictionary<string, WorldChunk> chunks = new Dictionary<string, WorldChunk>();
  30.     public List<WorldChunk> ActiveChunks = new List<WorldChunk>();
  31.  
  32.     // Get previous load position in world
  33.     private Vector2Int prevChunkPos = new Vector2Int(0,0);
  34.  
  35.  
  36.     //============================================================
  37.     // Set Warm-Up Data
  38.     //============================================================
  39.     private void Awake() {
  40.         // Get/Create Seed
  41.         if (Seed == ""){
  42.             Seed = GenerateRandomSeed();
  43.         }
  44.         // Get Random Number Generator
  45.         pseudoRandom = new System.Random(Seed.GetHashCode());
  46.          // Set Offsets from seed (new world each time)
  47.         xOffset = pseudoRandom.Next(-10000, 10000);
  48.         yOffset = pseudoRandom.Next(-10000, 10000);
  49.  
  50.         // Using to Clear while Making Test Adjustments
  51.         chunks.Clear();
  52.         ActiveChunks.Clear();
  53.         // Generate Starting Chunk
  54.         for (int x = -1; x <= 1; x++)
  55.         {
  56.             for (int y = -1; y <= 1; y++)
  57.             {
  58.                 // Draw Test Chunks
  59.                 GenerateChunk(x, y);
  60.                 WorldChunk c = GetChunk(x,y);
  61.                 // Enable the Chunk and Add it To The Active List
  62.                 c.enabled = true;
  63.                 ActiveChunks.Add(c);
  64.             }
  65.         }
  66.     }
  67.  
  68.     private void Update() {
  69.         int2 chunkPos = new int2(0, 0);
  70.         // 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)
  71.         if (Camera.main.transform.position.x >= 0){
  72.             chunkPos.x = (int)((Camera.main.transform.position.x+ChunkSize/2) / ChunkSize);
  73.         } else {
  74.             chunkPos.x = (int)((Camera.main.transform.position.x-ChunkSize/2) / ChunkSize);
  75.         }
  76.  
  77.         if (Camera.main.transform.position.y >= 0){
  78.             chunkPos.y = (int)((Camera.main.transform.position.y+ChunkSize/2) / ChunkSize);
  79.         } else {
  80.             chunkPos.y = (int)((Camera.main.transform.position.y-ChunkSize/2) / ChunkSize);
  81.         }
  82.  
  83.         // Generate new Chunks if they don't exist & Enable Active Chunks
  84.         EnableChunks(chunkPos);
  85.     }
  86.  
  87.     // ===
  88.     //  Enable Chunk at position and surrounding chunks, disable all active chunks outsize area
  89.     // ===
  90.     private void EnableChunks(int2 chunkPosition){
  91.         // Check if needs to update
  92.         Vector2Int cpos = new Vector2Int(chunkPosition.x, chunkPosition.y);
  93.         if (!PositionIsNew(cpos)){
  94.             return;
  95.         }
  96.         // Get offsets
  97.         List<int2> offsets = new List<int2>();
  98.         // Add 1, 1 & -1, -1  and so on for full square
  99.         offsets.Add(new int2(0, 1));
  100.         offsets.Add(new int2(0, -1));
  101.         offsets.Add(new int2(1, 0));
  102.         offsets.Add(new int2(-1, 0));
  103.         offsets.Add(new int2(0, 0));
  104.         // Set all Active Chunks Inactive
  105.         foreach (int2 offset in offsets)
  106.         {
  107.             for (int i = 0; i < ActiveChunks.Count; i++)
  108.             {
  109.                 // Check if the Chunk exists in active List (this is to reduce lag when loading and unloading chunks)
  110.                 // So you don't cycle through all the chunks to see what is enabled
  111.                 if (ActiveChunks[i] != null){
  112.                     // If it does disable it and remove it from the list
  113.                     ActiveChunks[i].enabled = false;
  114.                     ActiveChunks.RemoveAt(i);
  115.                 }
  116.             }
  117.         }
  118.         // Cycle offsets and Enable All Chunks within
  119.         foreach (int2 offset in offsets)
  120.         {
  121.             WorldChunk offsetChunk = GetChunk(chunkPosition.x + offset.x, chunkPosition.y + offset.y);
  122.             offsetChunk.enabled = true;
  123.             ActiveChunks.Add(offsetChunk);
  124.         }
  125.         prevChunkPos = cpos;
  126.     }
  127.  
  128.     // ===
  129.     //  BOOL - Check if current pos is the same as old
  130.     // ===
  131.     public bool PositionIsNew(Vector2Int position){
  132.         return (prevChunkPos != position);
  133.     }
  134.  
  135.     //============================================================
  136.     // Generation Code
  137.     //============================================================
  138.  
  139.     // ===
  140.     //  Create New Chunks
  141.     // ===
  142.     public void GenerateChunk(int x, int y){
  143.         // Set Key to use
  144.         string key = $"{x},{y}";
  145.         // Check if key exists if not Generate New Chunk
  146.         if (!chunks.ContainsKey(key)){
  147.             // Add Chunk, Set Position in chunk grid (for calling and block data later), Then Generate data
  148.             chunks.Add(key, new WorldChunk(ChunkSize));
  149.             chunks[key].Position = new int2(x, y);
  150.             GenerateChunkData(chunks[key]);
  151.         }
  152.     }
  153.     // ===
  154.     //  Get Current Chunk From X/Y or INt2
  155.     // ===
  156.     public WorldChunk GetChunk(int x, int y){
  157.         // Set Key to use
  158.         string key = $"{x},{y}";
  159.         // Check if key exists if not Generate New Chunk
  160.         if (chunks.ContainsKey(key)){
  161.             return chunks[key];
  162.         } else {
  163.             GenerateChunk(x,y);
  164.             return chunks[key];
  165.         }
  166.     }
  167.  
  168.     public WorldChunk GetChunk(int2 intpos){
  169.         return GetChunk(intpos.x, intpos.y);
  170.     }
  171.     // ===
  172.     //  Fill Chunks with Perlin Data
  173.     // ===
  174.     private void GenerateChunkData(WorldChunk chunk){
  175.         // Set min / max
  176.         float maxNoiseHeight = float.MaxValue;
  177.         float minNoiseHeight = float.MinValue;
  178.        // Set Max Height for World
  179.         float maxPossibleHeight = 0;
  180.         float amplitude = PerlinBaseAmplitude;
  181.         for (int i = 0; i < PerlinOctaves; i++)
  182.         {
  183.             maxPossibleHeight += amplitude;
  184.             amplitude *= persistence;
  185.         }
  186.         // Set Data to Chunk
  187.         for (int x = 0; x <= ChunkSize; x++)
  188.         {
  189.             for (int y = 0; y <= ChunkSize; y++)
  190.             {
  191.                 int2 cellPos = chunk.CellPosition(x, y);
  192.  
  193.                 amplitude = PerlinBaseAmplitude;
  194.                 float freq = 1;
  195.                 float noiseHeight = 0;
  196.                 // Get Perlin Map
  197.                 for (int i = 0; i < PerlinOctaves; i++)
  198.                 {
  199.                     float px = (float)(cellPos.x + xOffset) / PerlinScale * freq + xOffset;
  200.                     float py = (float)(cellPos.y + yOffset) / PerlinScale * freq + yOffset;
  201.  
  202.                     // Set Temp Sample For Testing (This will change for Map Data (Hills and Water) later)
  203.                     float PerlinValue = Mathf.PerlinNoise(px, py) * 2 - 1;
  204.                     noiseHeight += PerlinValue * amplitude;
  205.  
  206.                     // Increase amp and freq
  207.                     amplitude *= persistence;
  208.                     freq *= lacunarity;
  209.                 }
  210.  
  211.                 // Adjust Min and Max
  212.                 if (noiseHeight > maxNoiseHeight){
  213.                     maxNoiseHeight = noiseHeight;
  214.                 } else if (noiseHeight < minNoiseHeight){
  215.                     minNoiseHeight = noiseHeight;
  216.                 }
  217.                
  218.                 // Set Sample for Chunk
  219.                 chunk.Sample[x,y] = noiseHeight;
  220.             }
  221.         }
  222.  
  223.         // Normalize Sample to fit world Sample Height
  224.         for (int x = 0; x <= ChunkSize; x++)
  225.         {
  226.             for (int y = 0; y <= ChunkSize; y++)
  227.             {
  228.                 float normalizedHeight = (chunk.Sample[x,y] + 1) / (2f * maxPossibleHeight / 1.75f);
  229.                 chunk.Sample[x,y] = Mathf.Clamp(normalizedHeight, 0, int.MaxValue);
  230.             }
  231.         }
  232.     }
  233.  
  234.     // ===
  235.     //  Generate Random Seed of Length
  236.     // ===
  237.     private string GenerateRandomSeed(int maxCharAmount = 10, int minCharAmount = 10){
  238.         //Set Characters To Pick from
  239.         const string glyphs= "abcdefghijklmnopqrstuvwxyz0123456789";
  240.         //Set Length from min to max
  241.         int charAmount = UnityEngine.Random.Range(minCharAmount, maxCharAmount);
  242.         // Set output Variable
  243.         string output = "";
  244.         // Do Random Addition
  245.         for(int i=0; i<charAmount; i++)
  246.         {
  247.             output += glyphs[UnityEngine.Random.Range(0, glyphs.Length)];
  248.         }
  249.         // Output New Random String
  250.         return output;
  251.     }
  252.  
  253.     //============================================================
  254.     // Draw Example
  255.     //============================================================
  256.     private void OnDrawGizmos() {
  257.         if (!EditorApplication.isPlaying){
  258.         // For Live Editing Values before playing (Must enable Gizmos)
  259.         Awake();
  260.         }
  261.         // For Each WorldChunk in the chunk Data
  262.         foreach (WorldChunk c in chunks.Values)
  263.         {
  264.             // Check if it exists (Foreach is stupid sometimes... When live editing)
  265.             if (c != null){
  266.                 if (c.enabled == true){
  267.                 // Get World Positions for Chunk (Should probably Set to a Variable in the Chunk Data)
  268.                 Vector3 ChunkPosition = new Vector3(c.Position.x * ChunkSize, c.Position.y * ChunkSize);
  269.                 // For Each X & For Each Y in the chunk
  270.                 for (int x = 0; x <= ChunkSize; x++)
  271.                     {
  272.                         for (int y = 0; y <= ChunkSize; y++)
  273.                         {
  274.                            
  275.                             // Get Cell position
  276.                             Vector3 cellPos = c.VectorCellPosition(x,y);
  277.                             // Get Temp Sample and set to color
  278.                             float samp = c.Sample[x,y];
  279.                             if (cellPos.x == 0 && cellPos.y == 0 && c.Position.x == 0 && c.Position.y == 0){
  280.                                 Gizmos.color = new Color(0f, 1f, 0f);
  281.                             }else {
  282.                                 Gizmos.color = new Color(samp, samp, samp);
  283.                             }
  284.                             // Draw Tile as Sample black or white.
  285.                             Gizmos.DrawCube(cellPos, Vector3.one);
  286.                         }
  287.                     }
  288.                 }
  289.                
  290.             }
  291.         }
  292.        
  293.     }
  294. }
  295.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement