Skip to content

Instantly share code, notes, and snippets.

@xDavidLeon
Created May 8, 2015 17:20
Show Gist options
  • Save xDavidLeon/4143e989fb167099fedb to your computer and use it in GitHub Desktop.
Save xDavidLeon/4143e989fb167099fedb to your computer and use it in GitHub Desktop.
Zelda Dungeon Generator for Unity 3D
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);
}
}
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