Exemplo n.º 1
0
        /// <summary>
        /// This method is called recursively to generate the level. Given a
        /// room that is partially initialized, in that only some of its doors
        /// are connected to other rooms, spawn new rooms for the remaining
        /// doors that do not have an associated room. It will then call
        /// SpawnChildRooms on those new rooms.
        /// </summary>
        /// <param name="room">
        /// The RoomConnectionBehaviour of a partially initialized room.
        /// Partially initialized meaning it has some doors that are not yet
        /// connected to other rooms.
        /// </param>
        /// <param name="parent">
        /// The transform of the parent gameobject, the instantiated rooms
        /// comprising the level will be a child of the given transform.
        /// </param>
        /// <param name="grid">
        /// The RoomGrid to use while constructing the level.
        /// </param>
        /// <param name="depth">
        /// The depth of the level to be generated. Depth should be a value
        /// larger than zero. The depth is used to limit the recursion of this
        /// method.
        /// </param>
        /// <param name="disableChildRooms">
        /// Whether or not child-rooms (any room that is not the starting room)
        /// should be initially disabled. Normally rooms should only be enabled
        /// when the player enters them however it is sometimes useful for
        /// debugging to have all rooms enabled.
        /// </param>
        private static void SpawnChildRooms(RoomConnectionBehaviour room, Transform parent, RoomGrid grid, int depth, bool disableChildRooms)
        {
            bool enclosedPrefabRequired = depth == 1;

            bool DoorIsNotConnected(DoorConnectionBehaviour door) => door.ConnectingDoor == null;

            Debug.Assert(depth > 0 || room.Doors.Where(DoorIsNotConnected).Count() == 0, "depth reached zero however there are still rooms with unconnected doors.");
            foreach (DoorConnectionBehaviour currentDoor in room.Doors.Where(DoorIsNotConnected))
            {
                // find room prefab
                Vector2Int newPosition   = room.Position + currentDoor.Direction.ToVector2Int();
                GameObject newRoomPrefab = grid.SelectPrefabFor(newPosition, enclosedPrefabRequired, depth);

                // create room using prefab
                GameObject newRoomInstance      = InstanceFactory.InstantiateRoom(newRoomPrefab, parent, newPosition);
                RoomConnectionBehaviour newRoom = newRoomInstance.GetComponent <RoomConnectionBehaviour>();
                if (disableChildRooms)
                {
                    newRoom.gameObject.SetActive(false);
                }
                grid.Add(newRoom);

                // position room in scene
                newRoom.transform.position = new Vector3
                {
                    x = newPosition.x * RoomPlacementOffset.x,
                    y = newPosition.y * RoomPlacementOffset.y,
                    z = 0,
                };

                // recursively spawn child-rooms
                SpawnChildRooms(newRoom, parent, grid, depth - 1, disableChildRooms);
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Generate a new randomised level. This is an expensive method and
        /// should be called infrequently.
        /// </summary>
        /// <param name="startingRoomPrefab">
        /// A room prefab to use as the starting room.
        /// </param>
        /// <param name="parent">
        /// The transform of the parent gameobject, the instantiated rooms
        /// comprising the level will be a child of the given transform.
        /// </param>
        /// <param name="depth">
        /// The depth of the level to be generated. Depth should be a value
        /// larger than zero. A larger depth value will result in a larger
        /// level being generated.
        /// </param>
        /// <param name="disableChildRooms">
        /// Whether or not child-rooms (any room that is not the starting room)
        /// should be initially disabled. Normally rooms should only be enabled
        /// when the player enters them however it is sometimes useful for
        /// debugging to have all rooms enabled.
        /// </param>
        /// <returns>
        /// Returns the fully instantiated and initialized starting room. All
        /// rooms in the level are accessible via the starting room through the
        /// connected doors in each room which form a "level graph" of
        /// interconnected rooms.
        /// </returns>
        public static GameObject GenerateLevel(GameObject startingRoomPrefab, Transform parent, int depth, int?seed = null, bool disableChildRooms = true)
        {
            // NOTE: Due to how level generation is implemented, there is
            // always the small possibility that despite best efforts a
            // guaranteed room wont be able to find a viable position to spawn
            // while upholding all of the required constraints.
            //
            // To ensure the guaranteed rooms are included in the level, the
            // level will re-generate itself if it detects that not all the
            // guaranteed rooms were spawned.

            // set level generation seed
            if (!seed.HasValue)
            {
                seed = (int)DateTime.Now.Ticks;
            }
            UnityEngine.Random.InitState(seed.Value);
            Log.Info($"Generating level with depth {Log.Cyan(depth)} and seed {Log.Cyan(seed.Value)}", LogCategory.LevelGeneration);

            const int attempts = 25;

            for (int i = 0; i < attempts; i++)
            {
                RoomGrid   grid   = new RoomGrid(depth);
                Vector2Int centre = new Vector2Int(0, 0);
                GameObject startingRoomInstance      = InstanceFactory.InstantiateRoom(startingRoomPrefab, parent, centre);
                RoomConnectionBehaviour startingRoom = startingRoomInstance.GetComponent <RoomConnectionBehaviour>();
                grid.Add(startingRoom);
                SpawnChildRooms(startingRoom, parent, grid, depth, disableChildRooms);

                bool levelVerfied = grid.Verify();
                if (!levelVerfied)
                {
                    Log.Warning($"Generated level did not pass verification check. Regenerating... (attempt #{i+1})", LogCategory.LevelGeneration);
                    foreach (Transform childTransform in parent.transform)
                    {
                        GameObject.Destroy(childTransform.gameObject);
                    }
                    continue;
                }

                return(startingRoomInstance);
            }

            throw new Exception("GenerateLevel failed. This is likely due to an invalid SpawnProbability configuration.");
        }