/// <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);
 }
Beispiel #14
0
        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);
        }
Beispiel #15
0
        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));
        }