Hello!
So for the past few hours I’ve been working on a 2D procedural terrain system (Basically trying to clone the terraria terrain generation). However I quickly encountered a problem that I should’ve seen coming.
As you can see it can generate a fairly simple terrain currently. However the problem is with efficiently, I quickly discovered that there is no way my generation script will be able to handle generating a terraria sized world (I tried and Unity ran out of memory). So I was wondering if there was an easy shortcut to solve my problem. The terrain generation currently generates the world in chunks so I was wondering if I should make it so that the chunk is generated, serialized and deleted so that it can be loaded up when it needs to be rendered. Another problem is that it takes waaaay too long to generate even a world this size (10 seconds or so, which may not seem like much but when I want to generate a much larger terrain with biomes it is going to take forever).
Here’s my current script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class Tile
{
public GameObject tile; // The tile you want to spawn
public float pernlinMultiplier; // how big the veins are
public float popThreshold; // The chance of it spawning (the lower the more common and the larger the groups)
public int seed;
}
[System.Serializable]
public class Biome
{
public string name;
public GameObject baseTile; // This will be the main tile in the biome
public int rarity; // Rarity of the biome
public List<Tile> tiles = new List<Tile>(); // List of tiles that can spawn in the biome
public int maxHeight; // Max height that the biome can spawn at
public int minHeight; // Min height the biome can spawn at
}
public class Terraingen : MonoBehaviour
{
public int worldSeed;
public int chunkWidth; // Width of the rendering chunks
public int chunkDepth; // Depth of the rendering chunks
public int worldWidth;
public int worldDepth;
public int biomeMaxSize;
public int biomeMinSize;
public float hillMultiplier; // Hill depth
public float hillSample; // Hill closeness
public int hillSeed;
public float perlinMultiplier; // Basically determines the size of caves
public float popThreshold = 0.5f; // Chance of caves, the smaller the more common they are
public List<Biome> biomes = new List<Biome>();
public List<GameObject> chunks = new List<GameObject>();
void Start ()
{
Generate();
}
void Generate()
{
hillSeed = worldSeed;
foreach (Biome b in biomes)
{
for (int i = 0; i < b.tiles.Count; ++i)
{
b.tiles[i].seed = worldSeed * (i + 1);
}
}
for (int i = 0; i < Mathf.Round(worldWidth / chunkWidth); ++i) // Figure out how many chunks will fit in the world width wise
{
for (int i1 = 0; i1 < Mathf.Round(worldDepth / chunkDepth); ++i1) // figure out how many chunks will fit in the world height wise
{
GenerateChunk(i * chunkWidth, i1 * chunkDepth); // Spawn the chunks according to the world size vs chunk size
}
}
}
void GenerateChunk(int x, int y)
{
GameObject chunk = new GameObject(); // Instantiate the chunk parent
chunk.name = "Chunk";
chunk.AddComponent<BoxCollider2D>();
BoxCollider2D bc = chunk.GetComponent<BoxCollider2D>();
bc.offset = new Vector2(chunkWidth / 2, chunkDepth / 2);
bc.size = new Vector2(chunkWidth, chunkDepth);
chunk.transform.position = new Vector3(x, y, 1);
for (int i = 0; i < chunkWidth; ++i) // Cycle through each row of the chunk
{
for (int i1 = 0; i1 < chunkDepth; ++i1) // Cycle through each column of the chunk
{
if (!((i1 + y) > worldDepth - (Mathf.PerlinNoise(x + i / hillSample, hillSeed) * hillMultiplier))) // This part is to generate hills
{
PlaceTile(i + x, i1 + y, chunk, GetOccupyingTile(i + x, i1 + y, 0));
}
}
}
chunks.Add(chunk);
}
GameObject GetOccupyingTile(int x, int y, int biome)
{
GameObject tile = biomes[biome].baseTile;
foreach (Tile t in biomes[biome].tiles)
{
float val = Mathf.PerlinNoise((x + t.seed) * t.pernlinMultiplier, (y + t.seed) * t.pernlinMultiplier);
if (val >= t.popThreshold)
{
tile = t.tile;
}
}
return tile;
}
void PlaceTile(int x, int y, GameObject chunk, GameObject tile)
{
GameObject tileToPlace = (GameObject)GameObject.Instantiate(tile, new Vector3(x, y, 1), Quaternion.identity);
tileToPlace.transform.parent = chunk.transform;
}
}
Any help or suggestions would be appreciated,
Many thanks,
Aiden Leeming