///populates random dungeon rooms with stuff, including placing player character, stairs, enemies void PopulateDungeon() { ///create dungeon controller that all entities will reference Instantiate(dungeonController, new Vector3(), transform.rotation); //places dungeon controller first to take advantage of automatic turn order tracking //Instantiate(playerObject, GetRandomRoomFromQueue().getCoords(), transform.rotation); //places player character in a random room dungeon_room targetRoom = GetRandomRoomFromQueue(); //pick a random room to place the stairs in if (createdRoomsQueue.Count >= 2) //if we can have stairs in separate room from Player (if we grab top room, there is another room after for player to go into) { // Debug.Log("Preventing Stairs appearing in player's room"); tappedRoomsStack.Push(createdRoomsQueue.Dequeue()); //prevent the stairs from appearing in the same room as the player (we already grabbed the stairs room, now we're just keeping it separate from random rooms list) } //clear the tile that the stairs occupy Vector3 stairsPos = targetRoom.getRandomTileInRoom(); //grab a random position in the room, TODO: remove that tile, and place the stairs tile in its place Vector2 topRightLoc = new Vector2(stairsPos.x + 0.25f, stairsPos.y + 0.25f); Vector2 botLeftLoc = new Vector2(stairsPos.x - 0.25f, stairsPos.y - 0.25f); Collider2D collider = Physics2D.OverlapArea(topRightLoc, botLeftLoc, TileMonoBehavior.tileLayerMask);//build a collider at stairs position to see if there is already a tile there if (collider != null) { Destroy(collider.gameObject); } //place stairs into the dungeon Instantiate(stairsTilePrefab, stairsPos, transform.rotation); targetRoom = GetRandomRoomFromQueue(); //playe player into the dungeon Vector3 playerPos = targetRoom.getRandomTileInRoom(); while (playerPos.x == stairsPos.x && playerPos.y == stairsPos.y) //if player directly overlaps stairs, move player someplace else { playerPos = targetRoom.getRandomTileInRoom(); } Instantiate(playerObject, playerPos, transform.rotation); if (tappedRoomsStack.Count > 0) //shift all of the removed elements back onto the queue, preparation for placing in rest of the dungeon { createdRoomsQueue.Enqueue(tappedRoomsStack.Pop()); } //TODO: insert spectacular monster/item spawning algorithm here! int failCount = 0; //used to kill while loop after too many failed cases while (numberOfMonsters > 0 && (failCount < 10)) { //pick a tile, any tile... TileMonoBehavior tileAtSpawnPos = null; dungeon_room monsterSpawnRoom = GetRandomRoomFromQueue(); Vector3 monsterSpawnPos = monsterSpawnRoom.getRandomTileInRoom(); //check if tile already occupied topRightLoc = new Vector2(monsterSpawnPos.x + 0.25f, monsterSpawnPos.y + 0.25f); //build a collider to fetch the tile there botLeftLoc = new Vector2(monsterSpawnPos.x - 0.25f, monsterSpawnPos.y - 0.25f); collider = Physics2D.OverlapArea(topRightLoc, botLeftLoc, TileMonoBehavior.tileLayerMask); if (collider != null) //we find a tile there { tileAtSpawnPos = collider.gameObject.GetComponent <TileMonoBehavior>(); } if (tileAtSpawnPos != null && !tileAtSpawnPos.IsOccupied()) //if we found a tile and it is not occupied, spawn a monster there { Debug.Log("Creating monster at " + monsterSpawnPos.x + "," + monsterSpawnPos.y); GameObject spawnedEnemy = (GameObject)Instantiate(basicMonster, monsterSpawnPos, transform.rotation); //place a monster there, connect it to the tile tileAtSpawnPos.ConnectToEntity(spawnedEnemy.GetComponent <Entity>()); numberOfMonsters--; failCount = 0; } else { failCount++; } } if (failCount == 10) { Debug.Log("Failed to spawn all monsters, remaining monsters: " + numberOfMonsters); } }
// Update is called once per frame void Update() { /**on each update, pick a random room from list, try to generate a new room relative to that room * if no room in list (means we have exhausted all rooms to create around, or we have not created first room), give up on creating room * after creating rooms, populate them with a player, a set of stairs, and any relevant entities **/ if (roomCount < numberOfRooms) ///create numberOfRooms rooms { if (roomCount == 0) //creating very first room { int newSize = Random.Range(minRoomSize, maxRoomSize + 1); //size of new room dungeon_room newRoom = TryCreateRoom(new Vector3(transform.position.x, transform.position.y, TileMonoBehavior.tileZLayer), newSize, 0, Entity.MoveDirection.up); if (newRoom != null) //if room successfully created { createdRoomsQueue.Enqueue(newRoom); roomCount++; } else { roomCount = numberOfRooms; //forces Generator to stop generating GracefullyExit(); return; } } else if (createdRoomsQueue.Peek() != null) //not first element, and there is a next element to grab { dungeon_room oldRoom = GetRandomRoomFromQueue(); //picks an old room to generate new room relative to if (oldRoom == null) { roomCount = numberOfRooms; return; } dungeon_room newRoom = null; //the room we hope to create int roomSize = Random.Range(minRoomSize, maxRoomSize + 1); //the size we want the new room to be float distance = Random.Range(minDisplacement, maxDisplacement + 1); Vector3 newRoomLocation = new Vector3(); Vector3 oldRoomLocation = oldRoom.getCoords(); Entity.MoveDirection direction = (Entity.MoveDirection)Random.Range(0, 4); ///direction can be 0 up, 2 left, 1 down, 3 right Entity.MoveDirection originalDirection = direction; do ///try to create room at given direction, if it fails, keep trying with new direction until there are no more directions to pick { switch (direction) { case Entity.MoveDirection.up: //up { float relativePosition = Mathf.Ceil(oldRoom.getSize() / 2f) + distance + Mathf.Floor(roomSize / 2f); newRoomLocation = new Vector3(oldRoomLocation.x, oldRoomLocation.y + relativePosition, 0); break; } case Entity.MoveDirection.left: //left { float relativePosition = Mathf.Floor(oldRoom.getSize() / 2f) + distance + Mathf.Ceil(roomSize / 2f); newRoomLocation = new Vector3(oldRoomLocation.x - relativePosition, oldRoomLocation.y, 0); break; } case Entity.MoveDirection.down: //down { float relativePosition = Mathf.Floor(oldRoom.getSize() / 2f) + distance + Mathf.Ceil(roomSize / 2f); newRoomLocation = new Vector3(oldRoomLocation.x, oldRoomLocation.y - relativePosition, 0); break; } case Entity.MoveDirection.right: //right { float relativePosition = Mathf.Ceil(oldRoom.getSize() / 2f) + distance + Mathf.Floor(roomSize / 2f); //if size % 2 = 0, even, then nudge 1 back newRoomLocation = new Vector3(oldRoomLocation.x + relativePosition, oldRoomLocation.y); break; } } ///default case, it tries to initialize room at 0,0,0 newRoom = TryCreateRoom(newRoomLocation, roomSize, distance, direction); //try to create the room at specified location if (newRoom != null) //if room creation succeeded, store the room in list for later use { roomCount++; createdRoomsQueue.Enqueue(newRoom); } else if (direction == Entity.MoveDirection.right) { //else keep trying new directions until all exhausted direction = Entity.MoveDirection.up; } else { direction++; } }while (newRoom == null && direction != originalDirection); ///stop iterating when we successfully create a room, or when we reach original direction if (newRoom != null) //successfully created room, prepare for creating next room { while (tappedRoomsStack.Count != 0) //move all previously invalidated dungeon rooms back into queue again { createdRoomsQueue.Enqueue(tappedRoomsStack.Pop()); } } } //reaching here, this is not first room, so there exists at least one room, but we can't make any more rooms, so stop and cleanup else //createdRooms.Peek() == 0, so no more rooms to pick from. We can't build dungeon, so give up. { roomCount = numberOfRooms; //forces Generator to stop generating } } else //we are finished creating rooms { PopulateDungeon(); Destroy(this.gameObject); } }