Rooms have inhabitants now!
- Filip Walczak

- Apr 19, 2020
- 3 min read
In the previous post about the RPG game I mentioned that I'll be refactoring the room generation code, because its a big, big mess - so I did. The other night when I was trying to sleep I remembered something that I didn't find particularly useful at first, but at the moment this was probably exactly what I needed - the Enum Flags Attribute. This attribute allows the enum to take multiple values as long as every member is the next power of two. In other words every member of the enumeration is declared as one bit in the bit field. This is the enum that I used:
[Flags]
enum RoomOpenings {
None = 0, //0000
Top = 1, //0001
Right = 2, //0010
Down = 4, //0100
Left = 8, //1000
}By declaring the enum like that, we can now do magic tricks like this:
RoomOpenings openings = RoomOpenings.Down | RoomOpenings.Right;
// 0100 (decimal 4)
//OR 0010 (decimal 2)
// = 0110 (decimal 6)where by using the OR ( | ) bitwise operation we can "add" the bits together which results in the enum field being both RoomOpenings.Down and RoomOpenings.Right at the same time.
What's best about this is that Unity lets you set these values in the inspector like this:


So I set all the openings for the rooms and remade the CreateRoom method that i've shown you before, which looks like this now:
public static GameObject CreateRoom(RoomPosition currentPosition, RoomPosition nextPosition) {
RoomOpenings nextOpenings = (RoomOpenings) nextPosition.direction;
RoomOpenings currentOpenings = 0;
// we have to assign the opposite value, because
// current position direction tells us from which direction we came
// so if we have an opening on the top in the previous room
// we have to have an opening down in the current
switch (currentPosition.direction) {
case Direction.Top:
currentOpenings = (RoomOpenings)Direction.Down; break;
case Direction.Right:
currentOpenings = (RoomOpenings) Direction.Left; break;
case Direction.Down:
currentOpenings = (RoomOpenings) Direction.Top; break;
case Direction.Left:
currentOpenings = (RoomOpenings) Direction.Right; break;
}
RoomOpenings desiredOpenings;
// checks if this is the last room to generate
if (nextPosition == currentPosition)
desiredOpenings = currentOpenings;
else
desiredOpenings = nextOpenings | currentOpenings;
return Object.Instantiate(
roomPrefabs.First
(g => g.GetComponent<Room>().roomOpenings == desiredOpenings),
currentPosition.vector2,
Quaternion.identity);
}In order to be able to assign Direction to RoomOpenings I had to give it the same values:
enum Direction {
None = 0,
Top = 1,
Right = 2,
Down = 4,
Left = 8
}I think this looks definitely better than the previous approach and if one day I would like to maybe add openings on the corners I won't have to change anything and it will still work - awesome!
As you can see in the title, the player got company now. Although the enemies were in the game before, I didn't actually have any system which placed them in the level. Now they appear randomly in the rooms in the level generation phase.
When I first thought about spawning the enemies I had one problem in mind - how to easily make sure that they spawn on proper positions and still maintain the randomness. I came up with the idea of a list of possible "spawn points" from which the game will choose where to spawn the enemies.

These green diamonds are all the possible points on which enemies can spawn. The game object consists of a transform and a SpawnPoint script, which at the moment is completely empty and is only attached so I can easily find in the code all the game objects which are spawn points in the particular room. In the future I could add for example a EnemyStrength field which would determine what enemy can spawn here - maybe a chance for a elite creature to appear?
public class Room : MonoBehaviour {
public RoomOpenings roomOpenings;
public List<SpawnPoint> SpawnPoints => transform.Find
("Spawn Points").GetComponentsInChildren<SpawnPoint>().ToList();
}With the spawn points functionality added the generation phase looks like this:
Room generation
Corridor generation
Enemy generation - generator decides for each room how many enemies to spawn and on which positions

Look at them!
Slightly refactored level generation from the previous post with enemy generation added looks like this:
public static void GenerateLevel(int roomAmount = 0) {
if (roomAmount == 0) roomAmount = DefaultRoomAmount;
enemiesParent = new GameObject("Enemies").transform;
enemiesParent.SetParent(levelParent);
GenerateRooms(roomAmount);
GenerateCorridors();
GenerateEnemies(0, 4);
GenerationCompleted?.Invoke();
}private static void GenerateEnemies(int minAmount, int maxAmount) {
for (int i = 1; i < rooms.Count; i++) {
int count = Random.Range(minAmount, maxAmount);
List<SpawnPoint> spawnPoints = rooms[i].SpawnPoints;
for (int j = 0; j < count; j++) {
SpawnPoint spawnPoint = spawnPoints.Random();
spawnPoints.Remove(spawnPoint);
GameObject enemyPrefab = ResourcesController.enemyPrefabs.Random();
GameObject enemy = Object.Instantiate(enemyPrefab, spawnPoint.transform.position, Quaternion.identity);
enemy.transform.SetParent(enemiesParent);
}
}
}The Random method that I use with the lists is my extension method that returns an object from a random index in the provided list.
public static T Random<T>(this List<T> list) {
int rand = UnityEngine.Random.Range(0, list.Count);
return list[rand];
}The level generation is getting better and better. Till the next time!



Comments