Created
May 21, 2019 14:05
-
-
Save KHalkjaer/748d7978729d1e731fa4c675b30658f6 to your computer and use it in GitHub Desktop.
Level generator for procedural roguelike
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
using System.Collections.Generic; | |
public class LevelGeneratorV2 : MonoBehaviour { | |
[Range(30, 100)] | |
public int width; | |
[Range(30, 100)] | |
public int height; | |
[Range(3, 20)] | |
public int roomCount; | |
[Range(1, 10)] | |
public int minRoomRadius; | |
[Range(1, 20)] | |
public int maxRoomRadius; | |
public int[,] mapData; | |
public float expandChance; | |
public float diagonalExpandChance; | |
public int seed; | |
public bool useSeed; | |
public GameObject[] floor; | |
public GameObject wall; | |
public GameObject wallDestructible; | |
public GameObject player; | |
public GameObject[] enemies; | |
public float enemySpawnChance; | |
public bool playTesting; | |
public GameObject levelLight; | |
private Color quadColor; | |
private float genTime; | |
private Vector2 curRoom; | |
public List<Vector3> roomPos = new List<Vector3>(); | |
private List<float> roomDist = new List<float>(); | |
private Vector2 startRoom; | |
private Vector2 endRoom; | |
private float minDist; | |
public bool enemy1Spawned; | |
public bool enemy2Spawned; | |
// Use this for initialization | |
void Start () { | |
if(useSeed) | |
Random.seed = seed; | |
genTime = Time.realtimeSinceStartup; | |
// Instantiate the map variable | |
mapData = new int [width,height]; | |
// Set everything to 0 | |
for(int w = 0; w < width; w++){ | |
for(int h = 0; h < height; h++){ | |
mapData[w,h] = 0; | |
} | |
} | |
// Pick Room Centers | |
for(int r = 0; r < roomCount; r++){ | |
mapData[Random.Range(maxRoomRadius+2,width-maxRoomRadius-2),Random.Range(maxRoomRadius+2,height-maxRoomRadius-2)] = Random.Range(minRoomRadius,maxRoomRadius); | |
} | |
// Add room centers to list | |
for(int w = 1; w < width-1; w++){ | |
for(int h = 1; h < height-1; h++){ | |
if( (mapData[w,h]) != 0){ | |
curRoom = new Vector2(w,h); | |
roomPos.Add(curRoom); | |
} | |
} | |
} | |
// For each room, find the closest room that's not connected to another room and make a path there | |
for (int y = 0; y < roomPos.Count; y++){ | |
startRoom = roomPos[y]; | |
minDist = 1000; | |
// For each room | |
for (int x = 0; x < roomPos.Count; x++){ | |
float dist = Vector2.Distance(roomPos[y], roomPos[x]); | |
if(dist != 0 && dist < minDist && roomPos[x].z == 0){ | |
minDist = dist; | |
endRoom = roomPos[x]; | |
} | |
for (int z = 0; z < roomPos.Count; z++){ | |
if (roomPos[z].x == endRoom.x && roomPos[z].y == endRoom.y){ | |
endRoom = roomPos[z]; | |
} | |
} | |
roomPos[x] = new Vector3(roomPos[x].x,roomPos[x].y,1); | |
} | |
// Pick randomly if the path to the next room should be created on the x-axis or y-axis first | |
float XorY = Random.value; | |
if (XorY >= 0.5){ | |
// X AXIS FIRST | |
if(startRoom.x < endRoom.x){ | |
for(int w = 1; w < (int)endRoom.x - (int)startRoom.x; w++){ | |
mapData[(int)startRoom.x+w,(int)startRoom.y] = 2; | |
} | |
startRoom.x = endRoom.x; | |
} | |
if(startRoom.x > endRoom.x){ | |
for(int w = 1; w < (int)startRoom.x - (int)endRoom.x; w++){ | |
mapData[(int)startRoom.x-w,(int)startRoom.y] = 2; | |
} | |
startRoom.x = endRoom.x; | |
} | |
// Y AXIS | |
if(startRoom.y < endRoom.y){ | |
for(int h = 1; h < (int)endRoom.y - (int)startRoom.y; h++){ | |
mapData[(int)startRoom.x,(int)startRoom.y+h] = 2; | |
} | |
} | |
if(startRoom.y > endRoom.y){ | |
for(int h = 1; h < (int)startRoom.y - (int)endRoom.y; h++){ | |
mapData[(int)startRoom.x,(int)startRoom.y-h] = 2; | |
} | |
} | |
} | |
if(XorY < 0.5){ | |
// Y AXIS FIRST | |
if(startRoom.y < endRoom.y){ | |
for(int h = 1; h < (int)endRoom.y - (int)startRoom.y; h++){ | |
mapData[(int)startRoom.x,(int)startRoom.y+h] = 2; | |
} | |
startRoom.y = endRoom.y; | |
} | |
if(startRoom.y > endRoom.y){ | |
for(int h = 1; h < (int)startRoom.y - (int)endRoom.y; h++){ | |
mapData[(int)startRoom.x,(int)startRoom.y-h] = 2; | |
} | |
startRoom.y = endRoom.y; | |
} | |
// X AXIS | |
if(startRoom.x < endRoom.x){ | |
for(int w = 1; w < (int)endRoom.x - (int)startRoom.x; w++){ | |
mapData[(int)startRoom.x+w,(int)startRoom.y] = 2; | |
} | |
} | |
if(startRoom.x > endRoom.x){ | |
for(int w = 1; w < (int)startRoom.x - (int)endRoom.x; w++){ | |
mapData[(int)startRoom.x-w,(int)startRoom.y] = 2; | |
} | |
} | |
} | |
} | |
// Expand Rooms from center | |
for (int x = 0; x < maxRoomRadius; x++){ | |
for(int w = 1; w < width-1; w++){ | |
for(int h = 1; h < height-1; h++){ | |
if( (mapData[w,h]) != 0){ | |
int nextVar = mapData[w,h]-1; | |
if (mapData[w,h+1] == 0) | |
mapData[w,h+1] = nextVar; | |
if (mapData[w+1,h+1] == 0) | |
mapData[w+1,h+1] = nextVar; | |
if (mapData[w+1,h] == 0) | |
mapData[w+1,h] = nextVar; | |
if (mapData[w+1,h-1] == 0) | |
mapData[w+1,h-1] = nextVar; | |
if( (mapData[w,h-1]) == 0) | |
mapData[w,h-1] = nextVar; | |
if (mapData[w-1,h-1] == 0) | |
mapData[w-1,h-1] = nextVar; | |
if (mapData[w-1,h] == 0) | |
mapData[w-1,h] = nextVar; | |
if (mapData[w-1,h+1] == 0) | |
mapData[w-1,h+1] = nextVar; | |
} | |
} | |
} | |
} | |
// Chance based expand | |
for(int w = 1; w < width-1; w++){ | |
for(int h = 1; h < height-1; h++){ | |
if( (mapData[w,h]) == 1){ | |
if(mapData[w,h+1] == 0){ | |
float randomizer = Random.value; | |
if (randomizer < expandChance){ | |
mapData[w,h+1] = 1; | |
} | |
} | |
if(mapData[w+1,h+1] == 0){ | |
float randomizer = Random.value; | |
if (randomizer < diagonalExpandChance){ | |
mapData[w+1,h+1] = 1; | |
} | |
} | |
if(mapData[w+1,h] == 0){ | |
float randomizer = Random.value; | |
if (randomizer < expandChance){ | |
mapData[w+1,h] = 1; | |
} | |
} | |
if(mapData[w+1,h-1] == 0){ | |
float randomizer = Random.value; | |
if (randomizer < diagonalExpandChance){ | |
mapData[w+1,h-1] = 1; | |
} | |
} | |
if(mapData[w,h-1] == 0){ | |
float randomizer = Random.value; | |
if (randomizer < expandChance){ | |
mapData[w,h-1] = 1; | |
} | |
} | |
if(mapData[w-1,h-1] == 0){ | |
float randomizer = Random.value; | |
if (randomizer < diagonalExpandChance){ | |
mapData[w-1,h-1] = 1; | |
} | |
} | |
if(mapData[w-1,h] == 0){ | |
float randomizer = Random.value; | |
if (randomizer < expandChance){ | |
mapData[w-1,h] = 1; | |
} | |
} | |
if(mapData[w-1,h+1] == 0){ | |
float randomizer = Random.value; | |
if (randomizer < diagonalExpandChance){ | |
mapData[w-1,h+1] = 1; | |
} | |
} | |
} | |
} | |
} | |
// WALL CODE | |
// Create walls around rooms | |
for(int w = 1; w < width-1; w++){ | |
for(int h = 1; h < height-1; h++){ | |
if( (mapData[w,h]) == 1){ | |
if(mapData[w,h+1] == 0) | |
mapData[w,h+1] = 30; | |
if(mapData[w+1,h+1] == 0) | |
mapData[w+1,h+1] = 30; | |
if(mapData[w+1,h] == 0) | |
mapData[w+1,h] = 30; | |
if(mapData[w+1,h-1] == 0) | |
mapData[w+1,h-1] = 30; | |
if(mapData[w,h-1] == 0) | |
mapData[w,h-1] = 30; | |
if(mapData[w-1,h-1] == 0) | |
mapData[w-1,h-1] = 30; | |
if(mapData[w-1,h] == 0) | |
mapData[w-1,h] = 30; | |
if(mapData[w-1,h+1] == 0) | |
mapData[w-1,h+1] = 30; | |
} | |
} | |
} | |
for(int w = 1; w < width-1; w++){ | |
for(int h = 1; h < height-1; h++){ | |
if( (mapData[w,h]) == 30){ | |
if(mapData[w,h+1] == 0) | |
mapData[w,h+1] = 29; | |
if(mapData[w+1,h+1] == 0) | |
mapData[w+1,h+1] = 29; | |
if(mapData[w+1,h] == 0) | |
mapData[w+1,h] = 29; | |
if(mapData[w+1,h-1] == 0) | |
mapData[w+1,h-1] = 29; | |
if(mapData[w,h-1] == 0) | |
mapData[w,h-1] = 29; | |
if(mapData[w-1,h-1] == 0) | |
mapData[w-1,h-1] = 29; | |
if(mapData[w-1,h] == 0) | |
mapData[w-1,h] = 29; | |
if(mapData[w-1,h+1] == 0) | |
mapData[w-1,h+1] = 29; | |
} | |
} | |
} | |
for(int w = 1; w < width-1; w++){ | |
for(int h = 1; h < height-1; h++){ | |
if( (mapData[w,h]) == 29){ | |
if(mapData[w,h+1] == 0) | |
mapData[w,h+1] = 28; | |
if(mapData[w+1,h+1] == 0) | |
mapData[w+1,h+1] = 28; | |
if(mapData[w+1,h] == 0) | |
mapData[w+1,h] = 28; | |
if(mapData[w+1,h-1] == 0) | |
mapData[w+1,h-1] = 28; | |
if(mapData[w,h-1] == 0) | |
mapData[w,h-1] = 28; | |
if(mapData[w-1,h-1] == 0) | |
mapData[w-1,h-1] = 28; | |
if(mapData[w-1,h] == 0) | |
mapData[w-1,h] = 28; | |
if(mapData[w-1,h+1] == 0) | |
mapData[w-1,h+1] = 28; | |
} | |
} | |
} | |
// Make sure there aren't any holes | |
for(int w = 0; w < width; w++){ | |
if( (mapData[w,0]) != 0){ | |
mapData[w,0] = 28; | |
} | |
if( (mapData[w,height-1]) != 0){ | |
mapData[w,height-1] = 28; | |
} | |
} | |
// Same but other axis | |
for(int h = 0; h < width; h++){ | |
if( (mapData[0,h]) != 0){ | |
mapData[0,h] = 28; | |
} | |
if( (mapData[width-1,h]) != 0){ | |
mapData[width-1,h] = 28; | |
} | |
} | |
//*/ | |
// Build all map features | |
for(int w = 0; w < width; w++){ | |
for(int h = 0; h < height; h++){ | |
if( (mapData[w,h]) <= maxRoomRadius && mapData[w,h] > 0){ | |
if(!playTesting){ | |
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad); | |
quad.transform.position = new Vector3(w, 0, h); | |
quad.transform.Rotate(90.0f, 0.0f, 0.0f); | |
quadColor = new Color(1-(float)mapData[w,h]/maxRoomRadius,0,0,0); | |
quad.GetComponent<Renderer>().material.color = quadColor; | |
} | |
if(playTesting) | |
Instantiate(floor[Random.Range(0,floor.Length)], new Vector3(w, 0, h), Quaternion.identity); | |
} | |
if( (mapData[w,h]) == 30){ | |
if(!playTesting){ | |
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad); | |
quad.transform.position = new Vector3(w, 0, h); | |
quad.transform.Rotate(90.0f, 0.0f, 0.0f); | |
quad.GetComponent<Renderer>().material.color = Color.green; | |
} | |
if(playTesting) | |
Instantiate(wallDestructible, new Vector3(w, 0, h), Quaternion.identity); | |
} | |
if( (mapData[w,h]) == 29){ | |
if(!playTesting){ | |
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad); | |
quad.transform.position = new Vector3(w, 0, h); | |
quad.transform.Rotate(90.0f, 0.0f, 0.0f); | |
quad.GetComponent<Renderer>().material.color = Color.yellow; | |
} | |
if(playTesting) | |
Instantiate(wallDestructible, new Vector3(w, 0, h), Quaternion.identity); | |
} | |
if( (mapData[w,h]) == 28){ | |
if(!playTesting){ | |
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad); | |
quad.transform.position = new Vector3(w, 0, h); | |
quad.transform.Rotate(90.0f, 0.0f, 0.0f); | |
quad.GetComponent<Renderer>().material.color = Color.black; | |
} | |
if(playTesting) | |
Instantiate(wall, new Vector3(w, 0, h), Quaternion.identity); | |
} | |
} | |
} | |
if(playTesting){ | |
for (int x = 0; x < roomPos.Count; x++){ | |
float enemyChance = Random.value; | |
if (enemyChance < enemySpawnChance){ | |
// Instantiate (enemies[Random.Range(0,enemies.Length)], new Vector3 (roomPos[x].x-2,0,roomPos[x].y), transform.rotation); | |
if(!enemy1Spawned){ | |
Instantiate (GameControl.control.enemies[GameControl.control.firstEnemy[GameControl.control.currentLevel]], new Vector3 (roomPos[x].x-2,0,roomPos[x].y), transform.rotation); | |
enemy1Spawned = true; | |
} | |
if(!enemy2Spawned){ | |
Instantiate (GameControl.control.enemies[GameControl.control.secondEnemy[GameControl.control.currentLevel]], new Vector3 (roomPos[x].x+2,0,roomPos[x].y), transform.rotation); | |
enemy2Spawned = true; | |
} | |
} | |
} | |
Instantiate (player, new Vector3 (endRoom.x+3,0,endRoom.y+-3), transform.rotation); | |
Instantiate (levelLight, transform.position, transform.rotation); | |
AstarPath.active.Scan(); | |
} | |
genTime = Time.realtimeSinceStartup - genTime; | |
Debug.Log ("Level generated in " + genTime + " seconds"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment