Skip to content

Instantly share code, notes, and snippets.

@Ratstail91
Created December 16, 2020 19:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ratstail91/1585a40a3380d24fab953457aeb36cef to your computer and use it in GitHub Desktop.
Save Ratstail91/1585a40a3380d24fab953457aeb36cef to your computer and use it in GitHub Desktop.
//contents based on the type of the tile
public enum DungeonContent {
EMPTY = 0, STAIRS_UP, STAIRS_DOWN, SPAWN_MONSTER, SPAWN_TREASURE
};
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class DungeonEtcher : MapEtcher {
//structures
[Serializable]
public struct TileEtch {
[SerializeField]
public string name;
[SerializeField]
public TileType type;
[SerializeField]
public Tile asset;
[SerializeField]
public DungeonContent content; //TODO: can change later
[SerializeField]
public bool collision;
}
//public variables
public TileEtch[] tileEtches;
//DOCS: returns the collision data as a separate 2d array
public override bool[,] EtchTileData(Tilemap tilemap, TileType[,] tileData) {
bool[,] collisionData = new bool[tileData.GetLength(0), tileData.GetLength(1)];
tilemap.ClearAllTiles();
for(int i = 0; i < tileData.GetLength(0); i++) {
for (int j = 0; j < tileData.GetLength(1); j++) {
foreach(TileEtch etch in tileEtches) {
if (tileData[i,j] == etch.type) {
//actually etch the data
tilemap.SetTile(new Vector3Int(i, j, 0), etch.asset);
collisionData[i,j] = etch.collision;
//if there is supposed to be content here, place it
if (etch.content != DungeonContent.EMPTY) {
switch (etch.content) {
//TODO: more
default:
//DO NOTHING
break; /* switch */
}
}
break; /* foreach */
}
}
}
}
return collisionData;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class DungeonGenerator : MapGenerator {
//parameters
[SerializeField]
int mapWidth = 100;
[SerializeField]
int mapHeight = 100;
[SerializeField]
int terminationThreshold = 10;
//structures
enum Direction {
VERTICAL = 0, HORIZONTAL = 1
}
//generation methods
public override TileType[,] GenerateTileData() {
TileType[,] tileData = new TileType[mapWidth, mapHeight];
//zero the array
for (int i = 0; i < mapWidth; i++) {
for (int j = 0; j < mapHeight; j++) {
tileData[i, j] = TileType.WALL;
}
}
PartitionSpace(0, 0, mapWidth, mapHeight, ref tileData);
return tileData;
}
Vector2Int PartitionSpace(int x1, int y1, int x2, int y2, ref TileType[,] tileData) {
//calc the width & height
int width = x2 - x1;
int height = y2 - y1;
//check the termination threshold
if (width <= terminationThreshold || height <= terminationThreshold) {
//draw the box
return GenerateRoom(x1, y1, x2, y2, ref tileData);
}
//get the partition direction
Direction direction;
if (width == height) {
direction = (Direction)Random.Range(0, 2);
} else {
direction = (Direction)(height < width ? Direction.VERTICAL : Direction.HORIZONTAL);
}
//split the partition in the middle third
switch (direction) {
case Direction.VERTICAL: {
int splitPos = Random.Range(width/3, (width/3) * 2);
Vector2Int a = PartitionSpace(x1, y1, x1 + splitPos, y2, ref tileData);
Vector2Int b = PartitionSpace(x1 + splitPos + 1, y1, x2, y2, ref tileData);
return GenerateCorridor(a, b, ref tileData);
}
case Direction.HORIZONTAL: {
int splitPos = Random.Range(height/3, (height/3) * 2);
Vector2Int a = PartitionSpace(x1, y1, x2, y1 + splitPos, ref tileData);
Vector2Int b = PartitionSpace(x1, y1 + splitPos + 1, x2, y2, ref tileData);
return GenerateCorridor(a, b, ref tileData);
}
}
throw new System.InvalidOperationException("Unreachable");
}
Vector2Int GenerateRoom(int x1, int y1, int x2, int y2, ref TileType[,] tileData) {
//calc the partition width & height
int width = x2 - x1;
int height = y2 - y1;
//TODO: prefab room chance
//room bounds
int xBegin = Random.Range(x1 + 1, x1 + width/2);
int xEnd = Random.Range(x1 + width/2 + 1, x2 - 1);
int yBegin = Random.Range(y1 + 1, y1 + height/2);
int yEnd = Random.Range(y1 + height/2 + 1, y2 - 1);
for (int i = xBegin; i <= xEnd; i++) {
for (int j = yBegin; j <= yEnd; j++) {
tileData[i, j] = TileType.ROOM;
}
}
return new Vector2Int(Random.Range(xBegin, xEnd + 1), Random.Range(yBegin, yEnd + 1));
}
Vector2Int GenerateCorridor(Vector2Int a, Vector2Int b, ref TileType[,] tileData) {
Vector2Int ret = new Vector2Int(99999,99999); //return value
if (a == ret || b == ret) {
return ret;
}
int width = (int)Mathf.Abs(a.x - b.x);
int height = (int)Mathf.Abs(a.y - b.y);
//initial direction from a
Direction direction;
if (width == height) {
direction = (Direction)Random.Range(0, 2);
} else {
direction = width > height ? Direction.HORIZONTAL : Direction.VERTICAL;
}
//draw from a to b, overwriting wall tiles only
switch(direction) {
case Direction.HORIZONTAL:
for (int i = (int)Mathf.Min(a.x, b.x); i <= (int)Mathf.Max(a.x, b.x); i++) {
if (tileData[i, a.y] == TileType.WALL) {
tileData[i, a.y] = TileType.EMPTY;
}
CheckCorridorCenter(width, height, i, a.y, ref ret);
}
for (int j = (int)Mathf.Min(a.y, b.y); j <= (int)Mathf.Max(a.y, b.y); j++) {
if (tileData[b.x, j] == TileType.WALL) {
tileData[b.x, j] = TileType.EMPTY;
}
CheckCorridorCenter(width, height, (int)Mathf.Max(a.x, b.x), j, ref ret);
}
break;
case Direction.VERTICAL:
for (int j = (int)Mathf.Min(a.y, b.y); j <= (int)Mathf.Max(a.y, b.y); j++) {
if (tileData[a.x, j] == TileType.WALL) {
tileData[a.x, j] = TileType.EMPTY;
}
CheckCorridorCenter(width, height, a.x, j, ref ret);
}
for (int i = (int)Mathf.Min(a.x, b.x); i <= (int)Mathf.Max(a.x, b.x); i++) {
if (tileData[i, b.y] == TileType.WALL) {
tileData[i, b.y] = TileType.EMPTY;
}
CheckCorridorCenter(width, height, i, (int)Mathf.Max(a.y, b.y), ref ret);
}
break;
}
return ret;
}
//utility
void CheckCorridorCenter(int width, int height, int i, int j, ref Vector2Int center) {
if (center == new Vector2Int(99999,99999) && (width + height)/2 < i + j) {
center = new Vector2Int(i, j);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class LevelManager : MonoBehaviour {
//singleton pattern
private static LevelManager _instance;
public static LevelManager Instance {
get {
if (_instance == null) {
_instance = GameObject.Find("Level Manager").GetComponent<LevelManager>();
}
return _instance;
}
}
//public access members
public Tilemap tilemap;
public bool[,] Collisions { get { return dungeonCollisionData; }}
//dungeon stuff
MapGenerator mapGenerator;
MapEtcher mapEtcher;
TileType[,] dungeonTileData = null; //save this, can regenerate the world using this
bool[,] dungeonCollisionData = null;
//entity management
List<Entity> entities = new List<Entity>();
int entityExcecutionTick = 0;
//lifecycle methods
void Awake() {
mapGenerator = GetComponent<MapGenerator>();
mapEtcher = GetComponent<MapEtcher>();
dungeonTileData = mapGenerator.GenerateTileData();
dungeonCollisionData = mapEtcher.EtchTileData(tilemap, dungeonTileData);
}
void FixedUpdate() {
HandleEntityExecution();
}
//handlers
void HandleEntityExecution() {
List<Entity> executedEntities;
List<Entity> pendingEntities;
do {
executedEntities = new List<Entity>();
pendingEntities = new List<Entity>();
foreach(Entity entity in entities) {
if (entityExcecutionTick % entity.GetExecutionSpeed() == 0) {
if (entity.Execute() == true) {
executedEntities.Add(entity); //record executed entities
} else {
pendingEntities.Add(entity); //record waiting entities
}
}
}
//only increment if nobody is waiting for input
if (pendingEntities.Count == 0) {
entityExcecutionTick++;
}
} while (executedEntities.Count == 0 && pendingEntities.Count == 0 && entities.Count > 0);
Debug.Log(entityExcecutionTick);
}
//accessors and mutators
public void RegisterEntity(Entity entity) {
entities.Add(entity);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public abstract class MapEtcher : MonoBehaviour {
public abstract bool[,] EtchTileData(Tilemap tilemap, TileType[,] tileData);
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class MapGenerator : MonoBehaviour {
public abstract TileType[,] GenerateTileData();
}
//every type of tile in the game
public enum TileType {
WALL = 0, EMPTY, ROOM, ENTRY, EXIT
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment