/// <summary> /// Disables colliders of individual tilemaps in a given room template. /// The goal is to try to keep triggers functioning. /// </summary> public static void DisableRoomTemplateColliders(GameObject roomTemplate) { var tilemaps = PostProcessUtils.GetTilemaps(roomTemplate, x => x.IgnoreWhenDisablingColliders); foreach (var tilemap in tilemaps) { // Iterate through all the colliders foreach (var collider in tilemap.GetComponents <Collider2D>()) { // If the collider is not used by composite collider and it is not a trigger, destroy it if (!collider.usedByComposite && !collider.isTrigger) { Destroy(collider); } else if (collider.usedByComposite) { // If the collider is used by composite but that composite does not exist or is not a trigger, destroy it if (collider.composite == null || !collider.composite.isTrigger) { Destroy(collider); } } } } }
public virtual void Initialize() { // Remove all children game objects foreach (var child in transform.Cast <Transform>().ToList()) { PostProcessUtils.Destroy(child.gameObject); } // Add room template component if (gameObject.GetComponent <RoomTemplateSettings>() == null) { gameObject.AddComponent <RoomTemplateSettings>(); } // Create tilemaps root var tilemapsRoot = new GameObject(GeneratorConstants.TilemapsRootName); tilemapsRoot.transform.parent = gameObject.transform; // Init tilemaps InitializeTilemaps(tilemapsRoot); // Fix positions tilemapsRoot.transform.localPosition = Vector3.zero; transform.localPosition = Vector3.zero; // Add doors InitializeDoors(); }
/// <summary> /// Disables tilemap renderers in a given room template. /// </summary> public static void DisableRoomTemplateRenderers(GameObject roomTemplate) { var tilemaps = PostProcessUtils.GetTilemaps(roomTemplate, x => x.IgnoreWhenDisablingRenderers); foreach (var tilemap in tilemaps) { var tilemapRenderer = tilemap.GetComponent <TilemapRenderer>(); Destroy(tilemapRenderer); } }
public void RemoveOutlineOverride() { if (!HasOutlineOverride()) { return; } var tilemapsRoot = RoomTemplateUtils.GetTilemapsRoot(gameObject); var outlineOverride = tilemapsRoot.transform.Find(GeneratorConstants.OutlineOverrideLayerName).gameObject; PostProcessUtils.Destroy(outlineOverride); }
private void RegisterCallbacks(PriorityCallbacks <DungeonGeneratorPostProcessCallback> callbacks) { if (config.InitializeSharedTilemaps) { callbacks.RegisterCallback(PostProcessPriorities.InitializeSharedTilemaps, (level, description) => { // PostProcessUtils.InitializeSharedTilemaps(level, tilemapLayersHandler, config.TilemapMaterial); PostProcessUtils.InitializeSharedTilemaps(level, config.TilemapLayersStructure, defaultTilemapLayersHandlerFactory(), config.TilemapLayersHandler, config.TilemapLayersExample, config.TilemapMaterial); PostProcessUtils.SetTilemapsMaterial(level, config.TilemapMaterial); }); } if (config.CopyTilesToSharedTilemaps) { callbacks.RegisterCallback(PostProcessPriorities.CopyTilesToSharedTilemaps, (level, description) => { PostProcessUtils.CopyTilesToSharedTilemaps(level); }); } if (config.CenterGrid) { callbacks.RegisterCallback(PostProcessPriorities.CenterGrid, (level, description) => { PostProcessUtils.CenterGrid(level); }); } if (config.DisableRoomTemplatesRenderers) { callbacks.RegisterCallback(PostProcessPriorities.DisableRoomTemplateRenderers, (level, description) => { PostProcessUtils.DisableRoomTemplatesRenderers(level); }); } if (config.DisableRoomTemplatesColliders) { callbacks.RegisterCallback(PostProcessPriorities.DisableRoomTemplateColliders, (level, description) => { PostProcessUtils.DisableRoomTemplatesColliders(level); }); } }
/// <summary> /// Copies tiles from a given room template to given destination tilemaps. /// </summary> /// <remarks> /// One important aspect of this method is how to handle already existing tiles in destination tilemaps. /// /// When deleteNonNullTiles is true, it computes all non-null positions across all layers in the room template. /// After that, it deletes all tiles on these positions in destination tilemaps. /// /// When deleteTilesInsideOutline is true, it computes all tiles inside the outline of the room template and /// deletes them from the destination tilemaps. /// So even if there is a hole inside the room template, the position is still removed. /// /// deleteNonNullTiles and deleteTilesInsideOutline can be combined together. /// </remarks> /// <param name="roomInstance">Room instance to be copied to the destination tilemaps.</param> /// <param name="destinationTilemaps">List of destination tilemaps.</param> /// <param name="deleteNonNullTiles">Whether to delete non-null tiles from destination tilemaps.</param> /// <param name="deleteTilesInsideOutline">Whether to delete all tiles insides the outline from destination tilemaps.</param> public static void CopyTiles(RoomInstance roomInstance, List <Tilemap> destinationTilemaps, bool deleteNonNullTiles, bool deleteTilesInsideOutline) { PostProcessUtils.CopyTiles(roomInstance, destinationTilemaps, deleteNonNullTiles, deleteTilesInsideOutline); }
/// <summary> /// Copies to from individual room templates to shared tilemaps. /// </summary> /// <remarks> /// The order is important. First, copy all basic rooms and only then copy corridor rooms. /// </remarks> /// <param name="level"></param> public static void CopyTilesToSharedTilemaps(GeneratedLevel level) { PostProcessUtils.CopyTilesToSharedTilemaps(level); }
/// <summary> /// Sets a given material to all shared tilemap layers. /// </summary> /// <param name="level"></param> /// <param name="tilemapMaterial"></param> public static void SetTilemapsMaterial(GeneratedLevel level, Material tilemapMaterial) { PostProcessUtils.SetTilemapsMaterial(level, tilemapMaterial); }
/// <summary> /// Initializes shared tilemaps of a given level. /// </summary> /// <param name="level">Generated level.</param> /// <param name="mode">Tilemap layers mode.</param> /// <param name="defaultTilemapLayersHandler">Default tilemap layers handler. Used for the Default mode.</param> /// <param name="customTilemapLayersHandler">Custom tilemap layers handler. Used for the Custom mode.</param> /// <param name="example">Example game object for tilemaps structure. Used for the FromExample mode.</param> public static void InitializeSharedTilemaps(DungeonGeneratorLevelGrid2D level, TilemapLayersStructureModeGrid2D mode, ITilemapLayersHandlerGrid2D defaultTilemapLayersHandler, ITilemapLayersHandlerGrid2D customTilemapLayersHandler, GameObject example) { PostProcessUtils.InitializeSharedTilemaps(level, mode, defaultTilemapLayersHandler, customTilemapLayersHandler, example); }
/// <summary> /// Position the grid so that the level is centered at (0,0). /// </summary> /// <param name="level"></param> /// <param name="compressBounds">Whether to compress bounds of individual tilemaps before computing the center.</param> public static void CenterGrid(DungeonGeneratorLevelGrid2D level, bool compressBounds = false) { PostProcessUtils.CenterGrid(level, compressBounds); }
/// <summary> /// Gets the center point of given tilemaps- /// </summary> /// <param name="tilemaps"></param> /// <param name="compressBounds">Whether to compress bounds of individual tilemaps before computing the center.</param> /// <returns></returns> public static Vector3 GetTilemapsCenter(List <Tilemap> tilemaps, bool compressBounds = false) { return(PostProcessUtils.GetTilemapsCenter(tilemaps, compressBounds)); }
// TODO: where to put this? public static void Destroy(Object gameObject) { PostProcessUtils.Destroy(gameObject); }
/// <summary> /// Disables colliders of individual room template tilemaps in the generated level. /// The goal is to try to keep triggers functioning. /// </summary> /// <param name="level"></param> public static void DisableRoomTemplateColliders(GeneratedLevel level) { PostProcessUtils.DisableRoomTemplatesColliders(level); }
public override IEnumerator Process() { var levelDescription = Payload.LevelDescription; if (config.Timeout <= 0) { throw new ArgumentException($"{nameof(config.Timeout)} must be greater than 0", nameof(config.Timeout)); } var rootGameObject = config.RootGameObject; // If the root game objects was not set in the config, we do the following: // 1. Check if there already exists a game objects with a name reserved for the generated level // 2. Otherwise, we create a new empty game object if (rootGameObject == null) { rootGameObject = GameObject.Find("Generated Level"); if (rootGameObject == null) { rootGameObject = new GameObject("Generated Level"); } } // We delete all the children from the root game object - we do not want to combine levels from different runs of the algorithm foreach (var child in rootGameObject.transform.Cast <Transform>().ToList()) { child.transform.parent = null; PostProcessUtils.Destroy(child.gameObject); } // The LevelDescription class must be converted to MapDescription var levelDescriptionGrid2D = levelDescription.GetLevelDescription(); levelDescriptionGrid2D.MinimumRoomDistance = 1; levelDescriptionGrid2D.RoomTemplateRepeatModeOverride = GeneratorUtils.GetRepeatMode(config.RepeatModeOverride); var configuration = new GraphBasedGeneratorConfiguration <RoomBase>() { EarlyStopIfTimeExceeded = TimeSpan.FromMilliseconds(config.Timeout), }; // We create the instance of the dungeon generator and inject the correct Random instance var generator = new GraphBasedGeneratorGrid2D <RoomBase>(levelDescriptionGrid2D, configuration); generator.InjectRandomGenerator(Payload.Random); // Run the generator in a different class so that the computation is not blocking LayoutGrid2D <RoomBase> layout = null; var task = Task.Run(() => layout = generator.GenerateLayout()); while (!task.IsCompleted) { yield return(null); } // Throw an exception when a timeout is reached // TODO: this should be our own exception and not a generic exception if (layout == null) { if (task.Exception != null) { if (task.Exception.InnerException != null) { throw task.Exception.InnerException; } throw task.Exception; } else { throw new TimeoutException(); } } // Transform the level to its Unity representation var generatedLevel = GeneratorUtils.TransformLayout(layout, levelDescription, rootGameObject); var stats = new GeneratorStats() { Iterations = generator.IterationsCount, TimeTotal = generator.TimeTotal, }; Debug.Log($"Layout generated in {stats.TimeTotal / 1000f:F} seconds"); Debug.Log($"{stats.Iterations} iterations needed, {stats.Iterations / (stats.TimeTotal / 1000d):0} iterations per second"); ((IGraphBasedGeneratorPayload)Payload).GeneratedLevel = generatedLevel; Payload.GeneratorStats = stats; yield return(null); }
public static GeneratedLevel TransformLayout(LayoutGrid2D <RoomBase> layout, LevelDescription levelDescription, GameObject rootGameObject) { // var layoutCenter = GetLayoutCenter(layout); var prefabToRoomTemplateMapping = levelDescription.GetPrefabToRoomTemplateMapping(); var corridorToConnectionMapping = levelDescription.GetCorridorToConnectionMapping(); // Prepare an object to hold instantiated room templates var roomTemplateInstancesRoot = new GameObject(GeneratorConstants.RoomsRootName); roomTemplateInstancesRoot.transform.parent = rootGameObject.transform; // Initialize rooms var layoutData = new Dictionary <RoomBase, RoomInstance>(); var layoutRooms = layout.Rooms.ToDictionary(x => x.Room, x => x); foreach (var layoutRoom in layoutRooms.Values) { var roomTemplatePrefab = prefabToRoomTemplateMapping.GetByValue(layoutRoom.RoomTemplate); // Instantiate room template var roomTemplateInstance = Object.Instantiate(roomTemplatePrefab); roomTemplateInstance.transform.SetParent(roomTemplateInstancesRoot.transform); roomTemplateInstance.name = $"{layoutRoom.Room.GetDisplayName()} - {roomTemplatePrefab.name}"; // Compute correct room position var position = layoutRoom.Position.ToUnityIntVector3(); roomTemplateInstance.transform.position = position; // Correct the position based on the grid // This is important when there is some cell spacing or when the level is isometric var tilemapsHolder = roomTemplateInstance.transform.Find(GeneratorConstants.TilemapsRootName).gameObject; if (tilemapsHolder != null) { var grid = tilemapsHolder.GetComponent <Grid>(); roomTemplateInstance.transform.position = grid.CellToLocal(position); } // Compute outline polygon var polygon = new Polygon2D(layoutRoom.Outline + layoutRoom.Position); var connection = layoutRoom.IsCorridor ? corridorToConnectionMapping[layoutRoom.Room] : null; var roomInstance = new RoomInstance(layoutRoom.Room, layoutRoom.IsCorridor, connection, roomTemplatePrefab, roomTemplateInstance, position, polygon); // Add room info to the GameObject var roomInfo = roomTemplateInstance.GetComponent <RoomInfo>(); if (roomInfo != null) { PostProcessUtils.Destroy(roomInfo); } roomInfo = roomTemplateInstance.AddComponent <RoomInfo>(); roomInfo.RoomInstance = roomInstance; layoutData.Add(layoutRoom.Room, roomInstance); } foreach (var roomInstance in layoutData.Values) { roomInstance.SetDoors(TransformDoorInfo(layoutRooms[roomInstance.Room].Doors, layoutData)); } // Add level info var levelInfo = rootGameObject.GetComponent <LevelInfo>(); if (levelInfo != null) { PostProcessUtils.Destroy(levelInfo); } levelInfo = rootGameObject.AddComponent <LevelInfo>(); levelInfo.RoomInstances = layoutData.Values.ToList(); return(new GeneratedLevel(layoutData, layout, rootGameObject)); }