using System.Collections; using System.Collections.Generic; using UnityEngine; public class MapGenerator : MonoBehaviour { [SerializeField] private GameObject _mapContainer; //The game object that will parent all rooms and hallways //Every room that gets placed into the scene will be set as a child of this object [SerializeField] private GameObject[] _rooms; //An array containing all of the room prefabs to be used in generation [SerializeField] private Sprite _verticalHallwaySprite; //The sprite to be used as the vertical hallway [SerializeField] private Sprite _horizontalHallwaySprite; //The sprite to be used as the horizontal hallway private GameObject _currentRoom; //The variable that keeps track of the currently selected room [SerializeField] private int _numberOfRooms = 2; //The number of rooms that will be generated by this program private List _placedRooms = new List(); //A list containing all of the rooms currently placed into the scene private int _placedRoomNum; //The location of the current room in the placed rooms list private float _verticalGap; //The size of the vertical hallway sprite on the y axis private float _horizontalGap; //The size of the horizontal hallway sprite on the x axis // Start is called before the first frame update void Start() { //Creating the starting room by choosing a random one from the list of room prefabs, //setting the current room to this starting room, starting the room counter at 1, and //adding it to the list of placed rooms GameObject startRoom = Instantiate(_rooms[Random.Range(0, _rooms.Length)], transform.position, Quaternion.identity); startRoom.transform.parent = _mapContainer.transform; _currentRoom = startRoom; int roomCount = 1; _placedRooms.Add(startRoom); _placedRoomNum = 1; //Initializing the vertical hallway prefab by creating a new game object containing a SpriteRenderer, //Rigidbody2D, and BoxCollider, assigning all the appropriate values to those based off the sprite GameObject verticalHallway = new GameObject("VerticalHallway",typeof(SpriteRenderer),typeof(Rigidbody2D),typeof(BoxCollider2D)); SpriteRenderer vHallSpriteRenderer = verticalHallway.GetComponent(); vHallSpriteRenderer.sprite = _verticalHallwaySprite; vHallSpriteRenderer.drawMode = SpriteDrawMode.Sliced; _verticalGap = vHallSpriteRenderer.size.y; verticalHallway.GetComponent().size = vHallSpriteRenderer.size; verticalHallway.GetComponent().bodyType = RigidbodyType2D.Static; //Initializing the horizontal hallway prefab by creating a new game object containing a SpriteRenderer, //Rigidbody2D, and BoxCollider, assigning all the appropriate values to those based off the sprite GameObject horizontalHallway = new GameObject("HorizontalHallway", typeof(SpriteRenderer), typeof(Rigidbody2D), typeof(BoxCollider2D)); SpriteRenderer hHallSpriteRenderer = horizontalHallway.GetComponent(); hHallSpriteRenderer.sprite = _horizontalHallwaySprite; hHallSpriteRenderer.drawMode = SpriteDrawMode.Sliced; _horizontalGap = hHallSpriteRenderer.size.x; horizontalHallway.GetComponent().size = hHallSpriteRenderer.size; horizontalHallway.GetComponent().bodyType = RigidbodyType2D.Static; //Deletes the in-scene verticalHallway and horizontalHallway objects while retaining the prefabs created above Destroy(verticalHallway); Destroy(horizontalHallway); //Begins the while loop that will generate the entire map, only exiting when the number of placed rooms exceeds the assigned number of rooms for the map while (roomCount < _numberOfRooms) { //Creates the list of lists that contains all of the hallway nodes associated with the current room List> currentRoomNodes = new List>(); currentRoomNodes.Add(_currentRoom.GetComponent().topNodes); currentRoomNodes.Add(_currentRoom.GetComponent().leftNodes); currentRoomNodes.Add(_currentRoom.GetComponent().rightNodes); currentRoomNodes.Add(_currentRoom.GetComponent().bottomNodes); //Keep track of how many available hallways the current room has and set the currently observed node to null int numAdjacents = currentRoomNodes[0].Count + currentRoomNodes[1].Count + currentRoomNodes[2].Count + currentRoomNodes[3].Count; GameObject currentNode = null; //Starts the loop counter to keep track of if the program is "infinitely looping" in case of need for a reset int loopCounter = 0; //Starts the for loop that will fill all open adjacencies of the current room if possible for (int node = 0; node < numAdjacents; node++) { loopCounter++; //Choose a random room from the prefabs list to attempt to generate as the next room connected to the current room GameObject nextRoomPrefab = _rooms[Random.Range(0, _rooms.Length)]; List> nextRoomNodes = new List>(); nextRoomNodes.Add(nextRoomPrefab.GetComponent().topNodes); nextRoomNodes.Add(nextRoomPrefab.GetComponent().leftNodes); nextRoomNodes.Add(nextRoomPrefab.GetComponent().rightNodes); nextRoomNodes.Add(nextRoomPrefab.GetComponent().bottomNodes); //Set the observed node for the next room to null and choose a random direction that the room will connect to the side of GameObject nextNode = null; int nodeDirection = Random.Range(0, 4); //Set the trackers for the current room's node and next room's node to 0 int firstNodeNum = 0; int secondNodeNum = 0; //Check for if the current room and next room have open adjacencies in the randomly selected direction if (currentRoomNodes[nodeDirection].Count > 0 && nextRoomNodes[3 - nodeDirection].Count > 0) { //Both rooms have open adjacencies, so this sets the trackers to random nodes within that availability range firstNodeNum = Random.Range(0, currentRoomNodes[nodeDirection].Count); secondNodeNum = Random.Range(0, nextRoomNodes[3 - nodeDirection].Count); currentNode = currentRoomNodes[nodeDirection][firstNodeNum]; nextNode = nextRoomNodes[3 - nodeDirection][secondNodeNum]; } else { //One or both rooms do not have open adjacencies, so this cancels the attempt to instantiate any and decrements the loop counter by 1 nodeDirection = 4; node--; } //Checks for if the attempt to instantiate a new room was cancelled if (nodeDirection != 4) { //Initializes the variables to determine the x and y position of the next room float nextRoomXPos = 0; float nextRoomYPos = 0; //If the connection between the current room and next room is vertical, it does the vertical calculation //Otherwise, it does the horizontal calculation if (nodeDirection == 0 || nodeDirection == 3) { //Sets the x and y position variables for the next room to where it would be in the scene relative to the current room //taking into account the vertical gap needed between the rooms to fit a hallway in between them nextRoomXPos = (currentNode.transform.position.x + (nextRoomPrefab.transform.position.x - nextNode.transform.position.x)); nextRoomYPos = _verticalGap + currentNode.transform.position.y + (nextRoomPrefab.transform.position.y - nextNode.transform.position.y); } else { //Same as first part of if statement, but for horizontal gap calculation nextRoomXPos = _horizontalGap + currentNode.transform.position.x + (nextRoomPrefab.transform.position.x - nextNode.transform.position.x); nextRoomYPos = (currentNode.transform.position.y + (nextRoomPrefab.transform.position.x - nextNode.transform.position.y)); } //Intantiating the next room into the scene with the determined prefab and coordinates GameObject nextRoom = Instantiate(nextRoomPrefab, new Vector2(nextRoomXPos, nextRoomYPos), Quaternion.identity); nextRoom.transform.parent = _mapContainer.transform; //Initializes variables to track potential collisions between the next room and any current objects in the scene List collisions = new List(); ContactFilter2D empty = new ContactFilter2D(); nextRoom.GetComponent().OverlapCollider(empty.NoFilter(), collisions); //If the next room has any collisions, delete it and decrement the loop counter by 1 if (collisions.Count > 0) { Destroy(nextRoom); node--; } else { //Since the next room had no collisions, add it to the list of rooms placed in the scene and increment the room count by 1 _placedRooms.Add(nextRoom); roomCount++; //Depending on the generated node direction, generates a hallway between the current room and next room and //removes the nodes used to link them together as to signal that they're taken GameObject hallway; switch (nodeDirection) { default: continue; case 0: hallway = Instantiate(verticalHallway, new Vector2(currentNode.transform.position.x, currentNode.transform.position.y + (_verticalGap / 2)), Quaternion.identity); hallway.transform.parent = _mapContainer.transform; _currentRoom.GetComponent().topNodes.RemoveAt(firstNodeNum); currentRoomNodes[nodeDirection].Remove(currentNode); nextRoom.GetComponent().bottomNodes.RemoveAt(secondNodeNum); break; case 1: hallway = Instantiate(horizontalHallway, new Vector2(currentNode.transform.position.x - (_horizontalGap / 2), currentNode.transform.position.y), Quaternion.identity); hallway.transform.parent = _mapContainer.transform; _currentRoom.GetComponent().leftNodes.RemoveAt(firstNodeNum); currentRoomNodes[nodeDirection].Remove(currentNode); nextRoom.GetComponent().rightNodes.RemoveAt(secondNodeNum); break; case 2: hallway = Instantiate(horizontalHallway, new Vector2(currentNode.transform.position.x + (_horizontalGap / 2), currentNode.transform.position.y), Quaternion.identity); hallway.transform.parent = _mapContainer.transform; _currentRoom.GetComponent().rightNodes.RemoveAt(firstNodeNum); currentRoomNodes[nodeDirection].Remove(currentNode); nextRoom.GetComponent().leftNodes.RemoveAt(secondNodeNum); break; case 3: hallway = Instantiate(verticalHallway, new Vector2(currentNode.transform.position.x, currentNode.transform.position.y - (_verticalGap / 2)), Quaternion.identity); hallway.transform.parent = _mapContainer.transform; _currentRoom.GetComponent().bottomNodes.RemoveAt(firstNodeNum); currentRoomNodes[nodeDirection].Remove(currentNode); nextRoom.GetComponent().topNodes.RemoveAt(secondNodeNum); break; } } } //Ends the generation process if it successfully generated the number of rooms requested if (roomCount >= _numberOfRooms) { return; } //Checks for if the current room has run out of open adjacencies or if it has looped too many times if (node == numAdjacents - 1 || loopCounter > 20000) { //Sets the current room to the chronologically next room that was placed into the scene _currentRoom = _placedRooms[_placedRoomNum]; //Increments the placed room tracker and resets the loop counter _placedRoomNum++; loopCounter = 0; //Checks for if the program has hit an infinite loop by seeing if there are no more available rooms and less than the desired number of rooms in the scene if (_placedRoomNum == _placedRooms.Count && roomCount < _numberOfRooms) { //Since it fell into an infinite loop, this deletes every room generated so far and restarts the generation process with a new starting room //***THIS IS NOT A SOLUTION FOR THE INFINITE LOOP AND IS SIMPLY A TEMPORARY WORKAROUND, WILL HOPEFULLY FIND AN ACTUAL FIX SOON*** for (int i = _placedRooms.Count-1; i >= 0; i--) { Destroy(_placedRooms[i]); } _placedRooms = new List(); startRoom = Instantiate(_rooms[Random.Range(0, _rooms.Length)], transform.position, Quaternion.identity); startRoom.transform.parent = _mapContainer.transform; _currentRoom = startRoom; roomCount = 1; _placedRooms.Add(startRoom); _placedRoomNum = 1; //Notifies the user that the generator encountered the infinite loop error Debug.Log("Encountered generation error, resetting map"); } break; } } } } }