-
-
Save modality/6abbd6d175e1218c5abfc26b7a2a7ba4 to your computer and use it in GitHub Desktop.
Volume Based Randomized Themer
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.Generic; | |
using DungeonArchitect; | |
using DungeonArchitect.Graphs; | |
using DungeonArchitect.Utils; | |
using DungeonArchitect.Flow.Domains.Tilemap; | |
using DungeonArchitect.Builders.GridFlow; | |
/// <summary> | |
/// Spawns various theme override volumes around rooms and corridors. | |
/// We treat each node as a room and process the tiles under each node to | |
/// generate volumes. | |
/// </summary> | |
public class RoomThemer : DungeonEventListener | |
{ | |
public Graph startRoomTheme; | |
public Graph goalRoomTheme; | |
public Graph[] roomThemes; | |
/// <summary> | |
/// The template required to clone and duplicate a theme override volume. | |
/// Supply the reference of the theme override volume prefab here. | |
/// </summary> | |
public Volume themeOverrideVolumePrefab; | |
Dictionary<IntVector2, Graph> themeDictionary = new Dictionary<IntVector2, Graph>(); | |
// Dictionary<NodeCoord, (farthestSouthWest, farthestNorthEast)> | |
Dictionary<IntVector2, (Vector3, Vector3)> roomMinMaxDictionary = new Dictionary<IntVector2, (Vector3, Vector3)>(); | |
// Show the volumes we've generated, for debugging purposes. | |
[SerializeField] | |
List<GameObject> managedVolumes = new List<GameObject>(); | |
Vector3 gridSize = new Vector3(4, 4, 4); | |
// Gonna use this for consistentcy | |
PMRandom random; | |
public override void OnPostDungeonLayoutBuild(Dungeon dungeon, DungeonModel model) | |
{ | |
DestroyManagedVolumes(); | |
// Make sure we are working with the grid flow builder | |
var gridFlowModel = model as GridFlowDungeonModel; | |
if (gridFlowModel == null) return; | |
var cfg = GetComponent<GridFlowDungeonConfig>(); | |
gridSize = cfg.gridSize; | |
random = new PMRandom(cfg.Seed); | |
BuildDictionaries(gridFlowModel); | |
Vector3 halfGrid = new Vector3(gridSize.x * 0.5f, gridSize.y * 0.5f, gridSize.z * 0.5f); | |
// Group all the tiles by their parent node. | |
foreach (var cell in gridFlowModel.tilemap.Cells) | |
{ | |
if (cell.CellType != FlowTilemapCellType.Empty) // ignore empty tiles | |
{ | |
var worldPosition = TileWorldPosition(cell.TileCoord); | |
// if the node isn't already in the lookup add the lookup and tile position. | |
if(!roomMinMaxDictionary.ContainsKey(cell.NodeCoord)) | |
{ | |
roomMinMaxDictionary.Add(cell.NodeCoord, (worldPosition, worldPosition)); | |
continue; | |
} | |
roomMinMaxDictionary[cell.NodeCoord] = CalcMinMax(roomMinMaxDictionary[cell.NodeCoord], worldPosition); | |
} | |
} | |
// Generate the volumes | |
foreach(var kv in roomMinMaxDictionary) | |
{ | |
var lookup = kv.Key; | |
var southWest = kv.Value.Item1; | |
var northEast = kv.Value.Item2; | |
var halfWidth = (northEast.x - southWest.x) * 0.5f; | |
var halfDepth = (northEast.z - southWest.z) * 0.5f; | |
var center = new Vector3(southWest.x + halfWidth, gridSize.y, southWest.z + halfDepth); | |
// Fixed height, and we shift by the grid size to ensure full encapsulation. Otherwise the volume would be based on tile centers. | |
var scale = new Vector3(halfWidth * 2f, 10f, halfDepth * 2f); | |
// Spawn the volume object. | |
var volumeObject = Instantiate(themeOverrideVolumePrefab.gameObject) as GameObject; | |
volumeObject.transform.position = center; | |
volumeObject.transform.localScale = scale; | |
var volume = volumeObject.GetComponent<ThemeOverrideVolume>(); | |
volume.dungeon = dungeon; // Let the volume know that it belongs to this dungeon | |
volume.overrideTheme = themeDictionary[lookup]; // Assign the theme we'd like this volume to override | |
// Save a reference to the volume so we can destroy it when it is rebuilt the next time (or we will end up with duplicate volumes on rebuilds) | |
managedVolumes.Add(volumeObject); | |
} | |
} | |
// Clean up resources. | |
public override void OnDungeonDestroyed(Dungeon dungeon) | |
{ | |
DestroyManagedVolumes(); | |
themeDictionary.Clear(); | |
roomMinMaxDictionary.Clear(); | |
} | |
// Clean up resources. | |
public override void OnPostDungeonBuild(Dungeon dungeon, DungeonModel model) | |
{ | |
themeDictionary.Clear(); | |
roomMinMaxDictionary.Clear(); | |
} | |
void BuildDictionaries(GridFlowDungeonModel gridFlowModel) | |
{ | |
themeDictionary.Clear(); | |
foreach (var node in gridFlowModel.layoutGraph.Nodes) | |
{ | |
if (node.pathIndex >= 0) // ignore empty nodes | |
{ | |
// if starter node then use starter theme | |
if (node.items.Exists(x => x.markerName == "SpawnPoint")) | |
{ | |
themeDictionary.Add(new IntVector2((int)node.coord.x, (int)node.coord.y), startRoomTheme); | |
continue; | |
} | |
// if goal node then use goal theme | |
if (node.items.Exists(x => x.markerName == "LevelGoal")) | |
{ | |
themeDictionary.Add(new IntVector2((int)node.coord.x, (int)node.coord.y), goalRoomTheme); | |
continue; | |
} | |
// Record a random theme for lookup later. | |
themeDictionary.Add(new IntVector2((int)node.coord.x, (int)node.coord.y), GetRandomTheme()); | |
} | |
} | |
} | |
(Vector3, Vector3) CalcMinMax((Vector3, Vector3) old, Vector3 p) | |
{ | |
var southWest = old.Item1; | |
var northEast = old.Item2; | |
if (southWest.x > p.x) southWest.x = p.x; | |
if (southWest.z > p.z) southWest.z = p.z; | |
// Need to add grid size so we go to the farthest corner. | |
if (northEast.x < p.x + gridSize.x) northEast.x = p.x + gridSize.x; | |
if (northEast.z < p.z + gridSize.z) northEast.z = p.z + gridSize.z; | |
return (southWest, northEast); | |
} | |
Vector3 TileWorldPosition(IntVector2 tilePosition) | |
{ | |
return new Vector3(gridSize.x * tilePosition.x, 0, gridSize.z * tilePosition.y); | |
} | |
Graph GetRandomTheme() | |
{ | |
if (roomThemes.Length == 0) | |
{ | |
return null; | |
} | |
// Pick a random theme from the supplied theme list | |
return roomThemes[random.UniformRandom.Range(0, roomThemes.Length - 1)]; | |
} | |
void DestroyManagedVolumes() | |
{ | |
foreach (var volume in managedVolumes) | |
{ | |
if (Application.isPlaying) | |
{ | |
Destroy(volume); | |
} | |
else | |
{ | |
DestroyImmediate(volume); | |
} | |
} | |
managedVolumes.Clear(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment