Created
May 8, 2015 17:20
-
-
Save xDavidLeon/4143e989fb167099fedb to your computer and use it in GitHub Desktop.
Zelda Dungeon Generator for Unity 3D
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 Dungeon : MonoSingleton<Dungeon> | |
{ | |
[System.Serializable] | |
public class Position | |
{ | |
public int x = 0; | |
public int y = 0; | |
public Position(int _x, int _y) | |
{ | |
x = _x; | |
y = _y; | |
} | |
public override bool Equals(object obj) { | |
if(Object.ReferenceEquals(this, obj)) { | |
return true; | |
} | |
Position instance = obj as Position; | |
if(instance == null) { | |
return false; | |
} | |
return this.x == instance.x && this.y == instance.y; | |
} | |
public override int GetHashCode() { | |
return this.x.GetHashCode() ^ this.y.GetHashCode(); | |
} | |
} | |
public int seed = 0; | |
// Dungeon Rooms | |
public int DUNGEON_SIZE_X = 20; | |
public int DUNGEON_SIZE_Y = 20; | |
// Size of 3D Model Prefab in World Space | |
public int ROOM_SIZE_X = 14; | |
public int ROOM_SIZE_Z = 9; | |
public int MAX_ROOMS = 30; | |
public int num_rooms = 0; | |
private int MAX_TRIES = 100; | |
// Demo Room Prefab | |
public GameObject roomBasicPrefab; | |
public GameObject roomForbidden; | |
// Room structure | |
public Room[,] rooms; | |
public List<Room> roomsList; | |
// Pointer to Boss Room "Demo" GameObject | |
private Room bossRoom; | |
private GameObject bossRoomGO; | |
private GameObject roomContainer; | |
public int NUM_FORBIDDEN_POSITIONS = 10; | |
public List<Position> forbiddenPositions; | |
public bool forbiddenRoomsAsGameObjects = true; | |
public float debgugWaitTime = 0.25f; | |
public override void Init () | |
{ | |
if (seed == 0) ResetRNG(); | |
else Random.seed = seed; | |
if (DUNGEON_SIZE_X <= 0) DUNGEON_SIZE_X = Constants.DUNGEON_SIZE_X; | |
if (DUNGEON_SIZE_Y <= 0) DUNGEON_SIZE_Y = Constants.DUNGEON_SIZE_Y; | |
if (ROOM_SIZE_X <= 0) ROOM_SIZE_X = Constants.ROOM_SIZE_X; | |
if (ROOM_SIZE_Z <= 0) ROOM_SIZE_Z = Constants.ROOM_SIZE_Z; | |
} | |
public void CreateNewDungeon() | |
{ | |
// Create room structure | |
Clean (); | |
GenerateDungeon(); | |
if (roomContainer == null) roomContainer = new GameObject("Rooms") as GameObject; | |
GenerateGameRooms(); | |
//bossRoom.roomScript.BuyRoom(true); | |
//foreach (Room r in rooms) r.roomScript.FirstStart(); | |
// Bounds b = new Bounds(new Vector3(DUNGEON_SIZE_X*ROOM_SIZE_X/2 - ROOM_SIZE_X/2,1.5f, DUNGEON_SIZE_Y*ROOM_SIZE_Z/2 - ROOM_SIZE_Z/2),new Vector3(DUNGEON_SIZE_X*ROOM_SIZE_X,3.0f, DUNGEON_SIZE_Y*ROOM_SIZE_Z)); | |
// AstarPath.active.UpdateGraphs(b); | |
} | |
void Update () | |
{ | |
if (Input.GetKeyDown(KeyCode.Space)) | |
{ | |
ResetRNG(); | |
CreateNewDungeon(); | |
} | |
} | |
public void GenerateDungeon() | |
{ | |
// Create our first room at a random position | |
GenerateBossRoom(); | |
GenerateForbiddenPositions(); | |
// Generate childrens | |
while (num_rooms < MAX_ROOMS) | |
{ | |
Room r = GetRandomRoom(); | |
r.GenerateChild(); | |
} | |
} | |
public void ResetRNG() | |
{ | |
Random.seed = System.Guid.NewGuid().GetHashCode(); | |
seed = Random.seed; | |
} | |
private void GenerateBossRoom() | |
{ | |
bossRoom = AddRoom(null, DUNGEON_SIZE_X/2,DUNGEON_SIZE_Y/2); // null parent because it's the first node | |
bossRoom.generation = 0; | |
} | |
private void GenerateGameRoom(Room room) | |
{ | |
if (room == null) return; | |
// Real world position | |
float worldX = room.x * ROOM_SIZE_X; | |
float worldZ = room.y * ROOM_SIZE_Z; | |
GameObject g = GameObject.Instantiate(roomBasicPrefab, new Vector3(worldX, 0, worldZ), Quaternion.identity) as GameObject; | |
Bounds roomBounds = new Bounds(transform.position, new Vector3(14, 3, 9)); | |
//AstarPath.active.UpdateGraphs(roomBounds); | |
// Add the room info to the GameObject | |
RoomScript gameRoom = g.GetComponent<RoomScript>(); | |
gameRoom.room = room; | |
room.gameRoom = g; | |
room.roomScript = gameRoom; | |
if (room == bossRoom) | |
{ | |
bossRoomGO = g; | |
bossRoomGO.name = "Boss Room"; | |
Camera.main.GetComponent<TouchCamera>().SetTarget(new Vector3(GetBossRoomGO().transform.position.x, 0, GetBossRoomGO().transform.position.z)); | |
} | |
else | |
{ | |
g.name = "Room " + gameRoom.room.x + " " + gameRoom.room.y; | |
} | |
g.transform.parent = roomContainer.transform; | |
foreach (Room r in room.children) GenerateGameRoom(r); | |
} | |
void GenerateGameRooms() | |
{ | |
// For each room in our matrix generate a 3D Model from Prefab | |
if (forbiddenRoomsAsGameObjects) | |
{ | |
foreach (Position pos in forbiddenPositions) | |
{ | |
// Real world position | |
float worldX = pos.x * ROOM_SIZE_X; | |
float worldZ = pos.y * ROOM_SIZE_Z; | |
// GameObject rPrefab = roomlist[Random.Range(0, roomlist.Length)] as GameObject; | |
//GameObject rPrefab = roomlist[2] as GameObject; | |
GameObject g = GameObject.Instantiate(roomForbidden, new Vector3(worldX, 0, worldZ), Quaternion.identity) as GameObject; | |
g.name = "ForbiddenRoom " + pos.x + " " + pos.y; | |
g.transform.parent = roomContainer.transform; | |
} | |
} | |
GenerateGameRoom(bossRoom); | |
} | |
public void Clean() | |
{ | |
num_rooms = 0; | |
bossRoomGO = null; | |
if (rooms != null) | |
{ | |
foreach (Room r in rooms) | |
{ | |
if (r == null) continue; | |
if(r.gameRoom) GameObject.Destroy(r.gameRoom); | |
} | |
} | |
rooms = new Room[DUNGEON_SIZE_X,DUNGEON_SIZE_Y]; | |
if (roomsList != null) roomsList.Clear(); | |
roomsList = new List<Room>(); | |
if (forbiddenPositions != null) forbiddenPositions.Clear(); | |
forbiddenPositions = new List<Position>(NUM_FORBIDDEN_POSITIONS); | |
if (roomContainer != null) foreach (Transform t in roomContainer.transform) GameObject.Destroy(t.gameObject); | |
} | |
// Helper Methods | |
public Room AddRoom(Room parent, int x, int y) | |
{ | |
Room room = new Room(parent, x, y); | |
rooms[x,y] = room; | |
roomsList.Add(room); | |
num_rooms++; | |
return room; | |
} | |
public GameObject GetBossRoomGO() | |
{ | |
return bossRoomGO; | |
} | |
public Room GetBossRoom() | |
{ | |
return bossRoom; | |
} | |
public Room GetLastRoom() | |
{ | |
Room last = bossRoom; | |
foreach (Room r in rooms) | |
{ | |
if (r == null) continue; | |
if (r.generation > last.generation) last = r; | |
} | |
return last; | |
} | |
public Room GetRandomRoom() | |
{ | |
if (roomsList.Count == 0) return null; | |
int pos = Random.Range(0,roomsList.Count-1); | |
return roomsList[pos]; | |
} | |
public Position GetRandomPosition() | |
{ | |
int roomX = Random.Range (0, DUNGEON_SIZE_X-1); | |
int roomY = Random.Range (0, DUNGEON_SIZE_Y-1); | |
return new Position(roomX, roomY); | |
} | |
private void GenerateForbiddenPositions() | |
{ | |
int tries = 0; | |
while (forbiddenPositions.Count < NUM_FORBIDDEN_POSITIONS && tries < MAX_TRIES) | |
{ | |
tries++; | |
Position p = GetRandomPosition(); | |
if (rooms[p.x,p.y] != null) continue; | |
if (forbiddenPositions.Contains(new Position(p.x,p.y))) continue; | |
//if (p.x == 0) continue; | |
//if (p.x == DUNGEON_SIZE_X-1) continue; | |
//if (p.y == 0) continue; | |
//if (p.y == DUNGEON_SIZE_Y-1) continue; | |
if (p.x > 0 && rooms[p.x -1, p.y] != null) continue; | |
if (forbiddenPositions.Contains(new Position(p.x-1,p.y))) continue; | |
if (forbiddenPositions.Contains(new Position(p.x-1,p.y-1))) continue; | |
if (forbiddenPositions.Contains(new Position(p.x-1,p.y+1))) continue; | |
if (p.x < DUNGEON_SIZE_X && rooms[p.x +1, p.y] != null) continue; | |
if (forbiddenPositions.Contains(new Position(p.x+1,p.y))) continue; | |
if (forbiddenPositions.Contains(new Position(p.x+1,p.y-1))) continue; | |
if (forbiddenPositions.Contains(new Position(p.x+1,p.y+1))) continue; | |
if (p.y > 0 && rooms[p.x, p.y - 1] != null) continue; | |
if (forbiddenPositions.Contains(new Position(p.x,p.y-1))) continue; | |
if (rooms[p.x, p.y+1] != null) continue; | |
if (p.y < DUNGEON_SIZE_Y && forbiddenPositions.Contains(new Position(p.x, p.y + 1))) continue; | |
forbiddenPositions.Add(p); | |
} | |
} | |
public Room GetRoomAt(int posX, int posY) | |
{ | |
if (posX < 0) return null; | |
if (posX >= DUNGEON_SIZE_X) return null; | |
if (posY < 0) return null; | |
if (posY >= DUNGEON_SIZE_Y) return null; | |
return rooms[posX,posY]; | |
} | |
public Room GetRoomAtWorld(float posX, float posY) | |
{ | |
int x = Mathf.RoundToInt(posX/ROOM_SIZE_X); | |
int y = Mathf.RoundToInt(posY/ROOM_SIZE_Z); | |
return GetRoomAt(x,y); | |
} | |
} |
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; | |
[System.Serializable] | |
public class Room | |
{ | |
// pointer for easy access | |
private Dungeon dungeon; | |
// pointer to Room GO | |
public GameObject gameRoom; | |
public RoomScript roomScript; | |
// Properties | |
public int x; | |
public int y; | |
// parent in dungeon tree | |
public Room parent; | |
// children rooms | |
public List<Room> children; | |
// lower = older or closest to first node | |
public int generation = 0; | |
// max tries for trying to reproduce | |
private static int MAX_TRIES = 3; | |
public bool owned = false; | |
// [System.Serializable] | |
// public enum ROOM_STATE | |
// { | |
// ROOM_HIDDEN = 0, | |
// ROOM_DISCOVERED = 1, | |
// ROOM_OWNED = 2 | |
// }; | |
// public ROOM_STATE roomState = ROOM_STATE.ROOM_HIDDEN; | |
public Room(Room _parent, int _x, int _y) | |
{ | |
parent = _parent; | |
x = _x; | |
y = _y; | |
dungeon = Dungeon.instance; | |
children = new List<Room>(3); | |
} | |
public bool IsFirstNode() | |
{ | |
if (generation == 0) return true; | |
return false; | |
} | |
/// <summary> | |
/// DEPRECATED - Use GenerateChild instead | |
/// Generates up to 3 child rooms that will communicate with this one. | |
/// </summary> | |
public void GenerateChildren() | |
{ | |
if (dungeon.num_rooms >= dungeon.MAX_ROOMS) return; | |
if (children.Count >= 3) return; | |
if (NumEmptyNeighbours() == 0) return; | |
int num_tries = 0; | |
int num_children = Random.Range(1,4); | |
while (children.Count < num_children && num_tries < MAX_TRIES) | |
{ | |
int dir_child = GetValidDirection(0); | |
if (dir_child >= 0) | |
{ | |
Room r = AddChild(dir_child); | |
children.Add(r); | |
r.GenerateChildren(); | |
num_tries = 0; | |
} | |
else | |
{ | |
num_tries++; | |
continue; | |
} | |
} | |
foreach(Room r in children) | |
{ | |
r.generation = generation+1; | |
r.GenerateChildren(); | |
} | |
} | |
/// <summary> | |
/// Generates a child room. | |
/// </summary> | |
public void GenerateChild() | |
{ | |
if (dungeon.num_rooms >= dungeon.MAX_ROOMS) return; | |
if (children.Count >= 3) return; | |
if (NumEmptyNeighbours() == 0) return; | |
int dir_child = GetValidDirection(0); | |
if (dir_child == -1) return; | |
Room r = AddChild(dir_child); | |
children.Add(r); | |
r.generation = generation+1; | |
} | |
private Room AddChild(int direction) | |
{ | |
if (direction == 0) return dungeon.AddRoom(this,x-1,y); // Left | |
if (direction == 1) return dungeon.AddRoom(this,x+1,y); // Right | |
if (direction == 2) return dungeon.AddRoom(this,x,y+1); // Top | |
if (direction == 3) return dungeon.AddRoom(this,x,y-1); // Bottom | |
return null; | |
} | |
private int GetValidDirection(int num_tries) | |
{ | |
if (num_tries > MAX_TRIES) return -1; | |
int direction = Random.Range(0,4); | |
if (direction == 0) // Left | |
{ | |
if (x == 0) return GetValidDirection(num_tries+1); | |
if (GetLeft() != null) return GetValidDirection(num_tries+1); | |
if(dungeon.forbiddenPositions.Contains(new Dungeon.Position(x-1,y))) return GetValidDirection(num_tries+1); | |
} | |
else if (direction == 1) // Right | |
{ | |
if (x >= dungeon.DUNGEON_SIZE_X - 1) return GetValidDirection(num_tries+1); | |
if (GetRight() != null) return GetValidDirection(num_tries+1); | |
if(dungeon.forbiddenPositions.Contains(new Dungeon.Position(x+1,y))) return GetValidDirection(num_tries+1); | |
} | |
else if (direction == 2) // Top | |
{ | |
if (y >= dungeon.DUNGEON_SIZE_Y - 1) return GetValidDirection(num_tries+1); | |
if (GetTop() != null) return GetValidDirection(num_tries+1); | |
if(dungeon.forbiddenPositions.Contains(new Dungeon.Position(x,y+1))) return GetValidDirection(num_tries+1); | |
} | |
else if (direction == 3) // Bottom | |
{ | |
if (y == 0) return GetValidDirection(num_tries+1); | |
if (GetBottom() != null) return GetValidDirection(num_tries+1); | |
if(dungeon.forbiddenPositions.Contains(new Dungeon.Position(x,y-1))) return GetValidDirection(num_tries+1); | |
} | |
return direction; | |
} | |
public bool IsConnectedTo(Room room) | |
{ | |
if (room == null) return false; | |
if (room.parent == this) return true; | |
if (room == this.parent) return true; | |
return false; | |
} | |
public Room GetRight() | |
{ | |
int tileX = x + 1; | |
if (tileX >= dungeon.DUNGEON_SIZE_X) return null; | |
int tileY = y; | |
return dungeon.rooms[tileX, tileY]; | |
} | |
public Room GetLeft() | |
{ | |
int tileX = x - 1; | |
if (tileX < 0) return null; | |
int tileY = y; | |
return dungeon.rooms[tileX, tileY]; | |
} | |
public Room GetTop() | |
{ | |
int tileY = y + 1; | |
if (tileY >= dungeon.DUNGEON_SIZE_Y) return null; | |
int tileX = x; | |
return dungeon.rooms[tileX, tileY]; | |
} | |
public Room GetBottom() | |
{ | |
int tileY = y - 1; | |
if (tileY < 0) return null; | |
int tileX = x; | |
return dungeon.rooms[tileX, tileY]; | |
} | |
public bool IsThereRoomsAround() | |
{ | |
if (GetTop() != null) return true; | |
if (GetBottom() != null) return true; | |
if (GetLeft() != null) return true; | |
if (GetRight() != null) return true; | |
return false; | |
} | |
public int NumNeighbours() | |
{ | |
int n = 0; | |
if (GetTop() != null) n++; | |
if (GetBottom() != null) n++; | |
if (GetLeft() != null) n++; | |
if (GetRight() != null) n++; | |
return n; | |
} | |
public int NumEmptyNeighbours() | |
{ | |
int n = 0; | |
if (GetTop() == null && y < dungeon.DUNGEON_SIZE_Y - 1) n++; | |
if (GetBottom() == null && y > 0) n++; | |
if (GetLeft() == null && x > 0) n++; | |
if (GetRight() == null && x < dungeon.DUNGEON_SIZE_X - 1) n++; | |
return n; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment