Skip to content

Instantly share code, notes, and snippets.

@tudddorrr
Created August 7, 2019 20:30
Show Gist options
  • Select an option

  • Save tudddorrr/460a28e8e0c82c5cdb5a36b8acb5eab8 to your computer and use it in GitHub Desktop.

Select an option

Save tudddorrr/460a28e8e0c82c5cdb5a36b8acb5eab8 to your computer and use it in GitHub Desktop.
Crawle 2 - WorldGen v1.0
using UnityEngine;
using Photon.Pun;
using System.Collections.Generic;
using Pathfinding;
using System.Linq;
using ExitGames.Client.Photon;
public class WorldGenerator : MonoBehaviour {
public static System.Random rng, masterRng;
public static float worldSize;
readonly int
maxDirt = 800,
maxTallGrass = 800,
maxDecals = 1500,
maxFruitBushes = 1000,
maxForests = 10,
maxTrees = 750,
maxRockBiomes = 30,
maxOreNodes = 300,
maxSwamps = 3,
maxSwampGrass = 1000,
maxSwampRivers = 2,
maxRivers = 10,
maxAmbientSounds = 200,
maxBranches = 200,
maxRocks = 200,
maxAnimals = 150;
// Biomes
public static Rect[] swampBounds;
// Colliders
public static List<Collider2D> colliders = new List<Collider2D>();
// Resources
GameObject dirt, tallGrass, fruitBush, oreNode, decal, swampGrass, riverSeeker, water;
GameObject[] trees, ambience;
public void Start() {
worldSize = GameObject.Find("Grass").GetComponent<SpriteRenderer>().size.x;
// Load resources
dirt = Resources.Load("WorldObjects/Dirt") as GameObject;
tallGrass = Resources.Load("WorldObjects/TallGrass") as GameObject;
fruitBush = Resources.Load("WorldObjects/FruitBush") as GameObject;
oreNode = Resources.Load("WorldObjects/OreNode") as GameObject;
decal = Resources.Load("WorldObjects/Decal") as GameObject;
swampGrass = Resources.Load("WorldObjects/SwampGrass") as GameObject;
riverSeeker = Resources.Load("WorldObjects/RiverSeeker") as GameObject;
water = Resources.Load("WorldObjects/Water") as GameObject;
trees = new GameObject[] { Resources.Load("WorldObjects/Trees/Birch") as GameObject, Resources.Load("WorldObjects/Trees/Oak") as GameObject, Resources.Load("WorldObjects/Trees/Willow") as GameObject };
ambience = new GameObject[] { Resources.Load("Sounds/Birds Ambience") as GameObject, Resources.Load("Sounds/Cicadas Ambience") as GameObject };
}
public void Generate() {
int seed;
if(PhotonNetwork.IsMasterClient) {
string sDate = System.DateTime.Now.ToUniversalTime().ToString();
System.DateTime datevalue = System.Convert.ToDateTime(sDate);
seed = (datevalue.DayOfYear + 1) * (datevalue.Minute + 1);
Hashtable hash = new Hashtable {
{ "SEED", seed }
};
PhotonNetwork.CurrentRoom.SetCustomProperties(hash);
} else {
seed = (int) PhotonNetwork.CurrentRoom.CustomProperties["SEED"];
}
rng = new System.Random(seed);
masterRng = new System.Random(seed);
// Non-networked
GenSwamps();
GenTallGrass();
GenDecals();
GenAmbientSounds();
if(PhotonNetwork.IsMasterClient) {
// Networked, unmanaged
GenWorldItems();
GenAnimals();
// Networked, managed
GenTrees();
GenOreNodes();
GenDirt();
GenFruitBushes();
GenRivers();
}
}
public static Vector2[] RandomPerlinPoint(System.Random generator) {
float randomX1 = (float)generator.NextDouble();
float randomX2 = (float)generator.NextDouble();
float randomY1 = (float)generator.NextDouble();
float randomY2 = (float)generator.NextDouble();
return new Vector2[] { new Vector2(randomX1, randomY1), new Vector2(randomX2, randomY2) };
}
public static Vector2 RandomPerlinNoisePos(System.Random generator) {
Vector2[] points = RandomPerlinPoint(generator);
Vector2 point = new Vector2(
Mathf.PerlinNoise(points[0].x, points[0].y) + generator.Next((int)-worldSize / 2, (int)worldSize / 2),
Mathf.PerlinNoise(points[1].x, points[1].y) + generator.Next((int)-worldSize / 2, (int)worldSize / 2)
);
while (Collides(point)) {
point = new Vector2(
Mathf.PerlinNoise(points[0].x, points[0].y) + generator.Next((int)-worldSize / 2, (int)worldSize / 2),
Mathf.PerlinNoise(points[1].x, points[1].y) + generator.Next((int)-worldSize / 2, (int)worldSize / 2)
);
}
return point;
}
public static Vector2 RandomPerlinNoisePos(Vector2 minBounds, Vector2 maxBounds, System.Random generator) {
Vector2[] points = RandomPerlinPoint(generator);
Vector2 point = new Vector2(
Mathf.PerlinNoise(points[0].x, points[0].y) + generator.Next((int)minBounds.x, (int)maxBounds.x),
Mathf.PerlinNoise(points[1].x, points[1].y) + generator.Next((int)minBounds.y, (int)maxBounds.y)
);
while (Collides(point)) {
point = new Vector2(
Mathf.PerlinNoise(points[0].x, points[0].y) + generator.Next((int)minBounds.x, (int)maxBounds.x),
Mathf.PerlinNoise(points[1].x, points[1].y) + generator.Next((int)minBounds.y, (int)maxBounds.y)
);
}
return point;
}
public static bool Collides(Vector2 point) {
foreach (Collider2D collider in Object.FindObjectsOfType<Collider2D>()) {
if (collider.gameObject.layer == LayerMask.NameToLayer("Obstacles") && collider.bounds.Contains(point)) return true;
}
return false;
}
Vector2[] GenBiomeArea(int minSize, int maxSize, System.Random generator) {
float width = generator.Next(minSize, maxSize);
float height = generator.Next(minSize, maxSize);
Vector2 minBounds = new Vector2(
generator.Next((int)-worldSize / 2, ((int)worldSize / 2) - (int)width),
generator.Next((int)-worldSize / 2, ((int)worldSize / 2) - (int)height)
);
Vector2 maxBounds = minBounds + new Vector2(width, height);
return new Vector2[] { minBounds, maxBounds };
}
Rect BiomeRect(Vector2[] bounds, float border) {
return Rect.MinMaxRect(bounds[0].x - border, bounds[0].y - border, bounds[1].x + border, bounds[1].y + border);
}
void GenDirt() {
GameController.instance.SetLoadingMessage("Generating dirt...");
for (int i = 0; i < maxDirt; i++) {
Object.Instantiate(dirt, RandomPerlinNoisePos(masterRng), Quaternion.identity);
}
}
void GenTallGrass() {
GameController.instance.SetLoadingMessage("Generating tall grass...");
for (int i = 0; i < maxTallGrass; i++) {
Object.Instantiate(tallGrass, RandomPerlinNoisePos(rng), Quaternion.identity);
}
}
void GenTrees() {
GameController.instance.SetLoadingMessage("Generating trees...");
for (int i = 0; i < maxForests; i++) {
Vector2[] bounds = GenBiomeArea((int)worldSize / 16, (int)worldSize / 10, masterRng);
for (int j = 0; j < maxTrees / maxForests; j++) {
int randomTree = masterRng.Next(0, 2);
Vector3 pos = masterRng.Next(0, 2) == 0 ? RandomPerlinNoisePos(masterRng) : RandomPerlinNoisePos(bounds[0], bounds[1], masterRng);
if (swampBounds.Any(b => b.Contains(pos))) randomTree = 2;
Object.Instantiate(trees[randomTree], pos, Quaternion.identity);
}
}
}
void GenFruitBushes() {
GameController.instance.SetLoadingMessage("Generating fruit bushes...");
for (int i = 0; i < maxFruitBushes; i++) {
Object.Instantiate(fruitBush, RandomPerlinNoisePos(masterRng), Quaternion.identity);
}
}
void GenOreNodes() {
GameController.instance.SetLoadingMessage("Generating ore nodes...");
for (int i = 0; i < maxRockBiomes; i++) {
Vector2[] bounds = GenBiomeArea((int)worldSize / 30, (int)worldSize / 15, masterRng);
string oreType = "";
switch (masterRng.Next(0, 4)) {
case 0:
oreType = "coppernode";
break;
case 1:
oreType = "tinnode";
break;
case 2:
oreType = "ironnode";
break;
case 3:
oreType = "coalnode";
break;
}
for (int j = 0; j < maxOreNodes / maxRockBiomes; j++) {
GameObject node = Object.Instantiate(oreNode, RandomPerlinNoisePos(bounds[0], bounds[1], masterRng), Quaternion.identity);
node.GetComponent<OreNode>().spriteName = oreType;
node.GetComponent<OreNode>().SetSprite();
}
}
}
void GenWorldItems() {
GameController.instance.SetLoadingMessage("Spawning world items...");
for (int i = 0; i < maxBranches; i++) {
PhotonNetwork.InstantiateSceneObject("WorldItems/Branch", RandomPerlinNoisePos(masterRng), Quaternion.identity);
}
for (int i = 0; i < maxRocks; i++) {
PhotonNetwork.InstantiateSceneObject("WorldItems/Rock", RandomPerlinNoisePos(masterRng), Quaternion.identity);
}
}
void GenAnimals() {
GameController.instance.SetLoadingMessage("Spawning animals...");
for (int i = 0; i < maxAnimals; i++) {
string randomAnimal = "";
switch (masterRng.Next(0, 3)) {
case 0:
randomAnimal = "Bear";
break;
case 1:
randomAnimal = "Chicken";
break;
case 2:
randomAnimal = "Rabbit";
break;
}
PhotonNetwork.InstantiateSceneObject("Animals/" + randomAnimal, RandomPerlinNoisePos(masterRng), Quaternion.identity);
}
}
void GenDecals() {
GameController.instance.SetLoadingMessage("Generating decals...");
for (int i = 0; i < maxDecals; i++) {
Object.Instantiate(decal, RandomPerlinNoisePos(rng), Quaternion.identity);
}
}
void GenSwamps() {
GameController.instance.SetLoadingMessage("Generating swamps...");
swampBounds = new Rect[maxSwamps];
for(int i = 0; i < maxSwamps; i++) {
Vector2[] bounds = GenBiomeArea((int)worldSize / 8, (int)worldSize / 3, rng);
swampBounds[i] = BiomeRect(bounds, 2f);
GenSwampWater(swampBounds[i]);
for (int j = 0; j < maxSwampGrass / maxSwamps; j++) {
Vector3 pos = RandomPerlinNoisePos(bounds[0], bounds[1], rng);
Object.Instantiate(swampGrass, pos, Quaternion.identity);
}
}
}
void GenRivers() {
GameController.instance.SetLoadingMessage("Generating rivers...");
AstarPath astarPath = GameObject.Find("A*").GetComponent<AstarPath>();
astarPath.Scan(astarPath.graphs[0]);
List<GameObject> waterPoints = new List<GameObject>();
for (int i = 0; i < maxRivers; i++) {
List<Vector2> points = new List<Vector2>();
int numPoints = masterRng.Next(3, 8);
for (int j = 0; j < numPoints; j++) {
if (j == 0 || j == numPoints - 1) {
// First and last points are on the edge of the map
switch (Random.Range(0, 4)) {
case 0:
// Top
points.Add(new Vector2(RandomPerlinNoisePos(masterRng).x, worldSize));
break;
case 1:
// Left
points.Add(new Vector2(-worldSize, RandomPerlinNoisePos(masterRng).y));
break;
case 2:
// Bottom
points.Add(new Vector2(RandomPerlinNoisePos(masterRng).x, -worldSize));
break;
case 3:
// Right
points.Add(new Vector2(worldSize, RandomPerlinNoisePos(masterRng).y));
break;
}
} else {
points.Add(RandomPerlinNoisePos(masterRng));
}
waterPoints.Add(Object.Instantiate(riverSeeker, points[j], Quaternion.identity));
}
for (int j = 0; j < numPoints - 1; j++) {
waterPoints[j].GetComponent<Seeker>().StartPath(points[j], points[j + 1], OnRiverPathComplete);
}
}
}
void OnRiverPathComplete(Path p) {
foreach (Vector3 waypoint in p.vectorPath) {
Object.Instantiate(water, waypoint, Quaternion.identity);
}
}
void GenAmbientSounds() {
if (PhotonNetwork.IsMasterClient) GameController.instance.SetLoadingMessage("Generating ambience...");
for (int i = 0; i < maxAmbientSounds; i++) {
int randomAmbience = rng.Next(0, 2);
Vector3 pos = RandomPerlinNoisePos(rng);
Object.Instantiate(ambience[randomAmbience], pos, Quaternion.identity);
}
}
void GenSwampWater(Rect bounds) {
AstarPath astarPath = GameObject.Find("A*").GetComponent<AstarPath>();
astarPath.Scan(astarPath.graphs[0]);
List<GameObject> waterPoints = new List<GameObject>();
for (int i = 0; i < maxSwampRivers; i++) {
List<Vector2> points = new List<Vector2>();
int numPoints = masterRng.Next(8, 16);
for (int j = 0; j < numPoints; j++) {
if(j == numPoints - 1) {
points.Add(points[0]);
} else {
points.Add(new Vector2(rng.Next((int)bounds.x, (int)(bounds.x + bounds.width)), rng.Next((int)bounds.y, (int)(bounds.y + bounds.height))));
}
waterPoints.Add(Object.Instantiate(riverSeeker, points[j], Quaternion.identity));
}
for (int j = 0; j < numPoints - 1; j++) {
waterPoints[j].GetComponent<Seeker>().StartPath(points[j], points[j + 1], OnSwampRiverPathComplete);
}
}
}
void OnSwampRiverPathComplete(Path p) {
foreach (Vector3 waypoint in p.vectorPath) {
GameObject swampWater = Object.Instantiate(water, waypoint, Quaternion.identity);
swampWater.GetComponent<SpriteRenderer>().color = new Color(0.5f, 0.8f, 0.5f);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment