| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- namespace DunGen
- {
- [AddComponentMenu("DunGen/Culling/Adjacent Room Culling")]
- public class AdjacentRoomCulling : MonoBehaviour
- {
- public delegate void VisibilityChangedDelegate(Tile tile, bool visible);
- /// <summary>
- /// How deep from the current room should tiles be considered visibile
- /// 0 = Only the current tile
- /// 1 = The current tile and all its neighbours
- /// 2 = The current tile, all its neighbours, and all THEIR neighbours
- /// etc...
- /// </summary>
- public int AdjacentTileDepth = 1;
- /// <summary>
- /// If true, tiles behind a closed door will be culled, even if they're within <see cref="AdjacentTileDepth"/>
- /// </summary>
- public bool CullBehindClosedDoors = true;
- /// <summary>
- /// If set, this transform will be used as the vantage point that rooms should be culled from.
- /// Useful for third person games where you want to cull from the character's position, not the camera
- /// </summary>
- public Transform TargetOverride = null;
- /// <summary>
- /// Whether culling should handle any components that start disabled
- /// </summary>
- public bool IncludeDisabledComponents = false;
- /// <summary>
- /// A set of override values for specific renderers.
- /// By default, this script will overwrite any renderer.enabled values we might set in
- /// gameplay code. This property lets us tell the culling that we want to override the
- /// visibility values its setting
- /// </summary>
- [NonSerialized]
- public Dictionary<Renderer, bool> OverrideRendererVisibilities = new Dictionary<Renderer, bool>();
- /// <summary>
- /// A set of override values for specific lights.
- /// By default, this script will overwrite any light.enabled values we might set in
- /// gameplay code. This property lets us tell the culling that we want to override the
- /// visibility values its setting
- /// </summary>
- [NonSerialized]
- public Dictionary<Light, bool> OverrideLightVisibilities = new Dictionary<Light, bool>();
- /// <summary>
- /// True when a dungeon has been assigned and we're ready to start culling
- /// </summary>
- public bool Ready { get; protected set; }
- public event VisibilityChangedDelegate TileVisibilityChanged;
- protected List<Tile> allTiles;
- protected List<Door> allDoors;
- protected List<Tile> oldVisibleTiles;
- protected List<Tile> visibleTiles;
- protected Dictionary<Tile, bool> tileVisibilities;
- protected Dictionary<Tile, List<Renderer>> tileRenderers;
- protected Dictionary<Tile, List<Light>> lightSources;
- protected Dictionary<Tile, List<ReflectionProbe>> reflectionProbes;
- protected Dictionary<Door, List<Renderer>> doorRenderers;
- protected Transform targetTransform { get { return (TargetOverride != null) ? TargetOverride : transform; } }
- private bool dirty;
- private DungeonGenerator generator;
- private Tile currentTile;
- private Queue<Tile> tilesToSearch;
- private List<Tile> searchedTiles;
- private Dungeon dungeon;
- protected virtual void OnEnable()
- {
- var runtimeDungeon = UnityUtil.FindObjectByType<RuntimeDungeon>();
- if (runtimeDungeon != null)
- {
- generator = runtimeDungeon.Generator;
- generator.OnGenerationStatusChanged += OnDungeonGenerationStatusChanged; ;
- if (generator.Status == GenerationStatus.Complete)
- SetDungeon(generator.CurrentDungeon);
- }
- }
- protected virtual void OnDisable()
- {
- if (generator != null)
- generator.OnGenerationStatusChanged -= OnDungeonGenerationStatusChanged;
- ClearDungeon();
- }
- public virtual void SetDungeon(Dungeon newDungeon)
- {
- if (Ready)
- ClearDungeon();
- dungeon = newDungeon;
- if (dungeon == null)
- return;
- allTiles = new List<Tile>(dungeon.AllTiles);
- allDoors = new List<Door>(GetAllDoorsInDungeon(dungeon));
- oldVisibleTiles = new List<Tile>(allTiles.Count);
- visibleTiles = new List<Tile>(allTiles.Count);
- tileVisibilities = new Dictionary<Tile, bool>();
- tileRenderers = new Dictionary<Tile, List<Renderer>>();
- lightSources = new Dictionary<Tile, List<Light>>();
- reflectionProbes = new Dictionary<Tile, List<ReflectionProbe>>();
- doorRenderers = new Dictionary<Door, List<Renderer>>();
- UpdateRendererLists();
- foreach (var tile in allTiles)
- SetTileVisibility(tile, false);
- foreach (var door in allDoors)
- {
- door.OnDoorStateChanged += OnDoorStateChanged;
- SetDoorVisibility(door, false);
- }
- Ready = true;
- dirty = true;
- }
- public virtual bool IsTileVisible(Tile tile)
- {
- bool visibility;
- if (tileVisibilities.TryGetValue(tile, out visibility))
- return visibility;
- else
- return false;
- }
- protected IEnumerable<Door> GetAllDoorsInDungeon(Dungeon dungeon)
- {
- foreach (var doorObj in dungeon.Doors)
- {
- if (doorObj == null)
- continue;
- var door = doorObj.GetComponent<Door>();
- if (door != null)
- yield return door;
- }
- }
- protected virtual void ClearDungeon()
- {
- if (!Ready)
- return;
- foreach (var door in allDoors)
- {
- SetDoorVisibility(door, true);
- door.OnDoorStateChanged -= OnDoorStateChanged;
- }
- foreach (var tile in allTiles)
- SetTileVisibility(tile, true);
- Ready = false;
- }
- protected virtual void OnDoorStateChanged(Door door, bool isOpen)
- {
- dirty = true;
- }
- protected virtual void OnDungeonGenerationStatusChanged(DungeonGenerator generator, GenerationStatus status)
- {
- if (status == GenerationStatus.Complete)
- SetDungeon(generator.CurrentDungeon);
- else if (status == GenerationStatus.Failed)
- ClearDungeon();
- }
- protected virtual void LateUpdate()
- {
- if (!Ready)
- return;
- var oldTile = currentTile;
- // If currentTile doesn't exist, we need to first look for a dungeon,
- // then search every tile to find one that encompasses this GameObject
- if (currentTile == null)
- currentTile = FindCurrentTile();
- // If currentTile does exist, but we're not in it, we can perform a
- // breadth-first search radiating from currentTile. Assuming the player
- // is likely to be in an adjacent room, this should be much quicker than
- // testing every tile in the dungeon
- else if (!currentTile.Bounds.Contains(targetTransform.position))
- currentTile = SearchForNewCurrentTile();
- if (currentTile != oldTile)
- dirty = true;
- if (dirty)
- RefreshVisibility();
- dirty = false;
- }
- protected virtual void RefreshVisibility()
- {
- var temp = visibleTiles;
- visibleTiles = oldVisibleTiles;
- oldVisibleTiles = temp;
- UpdateVisibleTiles();
- // Hide any tiles that are no longer visible
- foreach (var tile in oldVisibleTiles)
- if (!visibleTiles.Contains(tile))
- SetTileVisibility(tile, false);
- // Show tiles that are newly visible
- foreach (var tile in visibleTiles)
- if (!oldVisibleTiles.Contains(tile))
- SetTileVisibility(tile, true);
- oldVisibleTiles.Clear();
- RefreshDoorVisibilities();
- }
- protected virtual void RefreshDoorVisibilities()
- {
- foreach (var door in allDoors)
- {
- bool visible = visibleTiles.Contains(door.DoorwayA.Tile) || visibleTiles.Contains(door.DoorwayB.Tile);
- SetDoorVisibility(door, visible);
- }
- }
- protected virtual void SetDoorVisibility(Door door, bool visible)
- {
- List<Renderer> renderers;
- if (doorRenderers.TryGetValue(door, out renderers))
- {
- for (int i = renderers.Count - 1; i >= 0; i--)
- {
- var renderer = renderers[i];
- if (renderer == null)
- {
- renderers.RemoveAt(i);
- continue;
- }
- // Check for overridden renderer visibility
- bool visibleOverride;
- if (OverrideRendererVisibilities.TryGetValue(renderer, out visibleOverride))
- renderer.enabled = visibleOverride;
- else
- renderer.enabled = visible;
- }
- }
- }
- protected virtual void UpdateVisibleTiles()
- {
- visibleTiles.Clear();
- if (currentTile != null)
- visibleTiles.Add(currentTile);
- int processTileStart = 0;
- // Add neighbours down to RoomDepth (0 = just tiles containing characters, 1 = plus adjacent tiles, etc)
- for (int i = 0; i < AdjacentTileDepth; i++)
- {
- int processTileEnd = visibleTiles.Count;
- for (int t = processTileStart; t < processTileEnd; t++)
- {
- var tile = visibleTiles[t];
- // Get all connections to adjacent tiles
- foreach (var doorway in tile.UsedDoorways)
- {
- var adjacentTile = doorway.ConnectedDoorway.Tile;
- // Skip the tile if it's already visible
- if (visibleTiles.Contains(adjacentTile))
- continue;
- // No need to add adjacent rooms to the visible list when the door between them is closed
- if (CullBehindClosedDoors)
- {
- var door = doorway.DoorComponent;
- if (door != null && door.ShouldCullBehind)
- continue;
- }
- visibleTiles.Add(adjacentTile);
- }
- }
- processTileStart = processTileEnd;
- }
- }
- protected virtual void SetTileVisibility(Tile tile, bool visible)
- {
- tileVisibilities[tile] = visible;
- // Renderers
- List<Renderer> renderers;
- if (tileRenderers.TryGetValue(tile, out renderers))
- {
- for (int i = renderers.Count - 1; i >= 0; i--)
- {
- var renderer = renderers[i];
- if (renderer == null)
- {
- renderers.RemoveAt(i);
- continue;
- }
- // Check for overridden renderer visibility
- bool visibleOverride;
- if (OverrideRendererVisibilities.TryGetValue(renderer, out visibleOverride))
- renderer.enabled = visibleOverride;
- else
- renderer.enabled = visible;
- }
- }
- // Lights
- List<Light> lights;
- if (lightSources.TryGetValue(tile, out lights))
- {
- for (int i = lights.Count - 1; i >= 0; i--)
- {
- var light = lights[i];
- if (light == null)
- {
- lights.RemoveAt(i);
- continue;
- }
- // Check for overridden renderer visibility
- bool visibleOverride;
- if (OverrideLightVisibilities.TryGetValue(light, out visibleOverride))
- light.enabled = visibleOverride;
- else
- light.enabled = visible;
- }
- }
- // Reflection Probes
- List<ReflectionProbe> probes;
- if (reflectionProbes.TryGetValue(tile, out probes))
- {
- for (int i = probes.Count - 1; i >= 0; i--)
- {
- var probe = probes[i];
- if (probe == null)
- {
- probes.RemoveAt(i);
- continue;
- }
- probe.enabled = visible;
- }
- }
- if (TileVisibilityChanged != null)
- TileVisibilityChanged(tile, visible);
- }
- public virtual void UpdateRendererLists()
- {
- foreach (var tile in allTiles)
- {
- // Renderers
- List<Renderer> renderers;
- if (!tileRenderers.TryGetValue(tile, out renderers))
- tileRenderers[tile] = renderers = new List<Renderer>();
- foreach (var renderer in tile.GetComponentsInChildren<Renderer>())
- if (IncludeDisabledComponents || (renderer.enabled && renderer.gameObject.activeInHierarchy))
- renderers.Add(renderer);
- // Lights
- List<Light> lights;
- if (!lightSources.TryGetValue(tile, out lights))
- lightSources[tile] = lights = new List<Light>();
- foreach (var light in tile.GetComponentsInChildren<Light>())
- if (IncludeDisabledComponents || (light.enabled && light.gameObject.activeInHierarchy))
- lights.Add(light);
- // Reflection Probes
- List<ReflectionProbe> probes;
- if (!reflectionProbes.TryGetValue(tile, out probes))
- reflectionProbes[tile] = probes = new List<ReflectionProbe>();
- foreach (var probe in tile.GetComponentsInChildren<ReflectionProbe>())
- if (IncludeDisabledComponents || (probe.enabled && probe.gameObject.activeInHierarchy))
- probes.Add(probe);
- }
- foreach (var door in allDoors)
- {
- List<Renderer> renderers = new List<Renderer>();
- doorRenderers[door] = renderers;
- foreach (var r in door.GetComponentsInChildren<Renderer>(true))
- if (IncludeDisabledComponents || (r.enabled && r.gameObject.activeInHierarchy))
- renderers.Add(r);
- }
- }
- protected Tile FindCurrentTile()
- {
- if (dungeon == null)
- return null;
- foreach (var tile in dungeon.AllTiles)
- {
- if (tile.Bounds.Contains(targetTransform.position))
- return tile;
- }
- return null;
- }
- protected Tile SearchForNewCurrentTile()
- {
- if (tilesToSearch == null)
- tilesToSearch = new Queue<Tile>();
- if (searchedTiles == null)
- searchedTiles = new List<Tile>();
- // Add all tiles adjacent to currentTile to the search queue
- foreach (var door in currentTile.UsedDoorways)
- {
- var adjacentTile = door.ConnectedDoorway.Tile;
- if (!tilesToSearch.Contains(adjacentTile))
- tilesToSearch.Enqueue(adjacentTile);
- }
- // Breadth-first search to find the tile which contains the player
- while (tilesToSearch.Count > 0)
- {
- var tile = tilesToSearch.Dequeue();
- if (tile.Bounds.Contains(targetTransform.position))
- {
- tilesToSearch.Clear();
- searchedTiles.Clear();
- return tile;
- }
- else
- {
- searchedTiles.Add(tile);
- foreach (var door in tile.UsedDoorways)
- {
- var adjacentTile = door.ConnectedDoorway.Tile;
- if (!tilesToSearch.Contains(adjacentTile) &&
- !searchedTiles.Contains(adjacentTile))
- tilesToSearch.Enqueue(adjacentTile);
- }
- }
- }
- searchedTiles.Clear();
- return null;
- }
- }
- }
|