Roguelike Monthly Jam #1: Imperfect Control

This was the first game jam I've taken part in, but I'm pleased to say that I'm cautiously happy with the results. There were a lot of things I'd like to have added, but as is to be expected I ran out of time for them. On the other hand, I was pleased that I managed to create the core elements of gameplay and random generation, with at least some scene setting involved, even if it was through my kinda crappy paint drawings. Talking of paint, here's the (not great) splash screen I hurriedly made:

Imperfect Control splash screen

You can play Imperfect Control at https://reddragonmakesgames.itch.io/imperfect-control. I might talk about Imperfect Control in a couple of posts, because there's quite a bit to talk about. In this post, I'm going to talk about the random generation.

Random map generation:

"Rougelike" refers to games with procedurally generated levels, so this was one of the first things I looked at when creating this game. The first script I wrote had the following function in it:

    void PlaceWalls()
    {
        int[] zPos = {10, 0, -10, 0};
        int[] xPos = {0, 10, 0, -10};

        for (int i = 0; i < 4; i ++)
        {
            //Randomly chose a wall to use
            GameObject wall;
            if (Random.Range(0,2) == 0)
                wall = WallA;
            else
                wall = WallB;

            GameObject newWall = Instantiate 
                (wall, new Vector3(xPos[i], 0, zPos[i]), Quaternion.identity);
            newWall.transform.Rotate(Vector3.up, 90 * i);        
        }
    }

Wall prefabs
With two different wall prefab game objects (wall prefabs shown to the right) attached to the script, the result was a nice looking box. When I wrote this code, I was imagining that I would spawn rooms with this, and then I'd work out the spawning of enemies and objects based on this afterwards. However, the issue of doors between the rooms made me change tack. Instead, I decided to do spawning the doors and rooms together. I did this by creating a map of room locations. When I wanted to spawn a new room, I searched the map for a valid location to spawn a new wall, and built up a list of valid locations. At the same time, I created a list of possible door locations, for if that spot was chosen. After I'd created the lists, all I had to do was randomly pick an element, and I could get the new spot for the room and door. The code for this is below:

        for (int i = 0; i < numberOfRooms; i++)
        {
            map.Add(FindOpenLocation());
        }


    Vector2Int FindOpenLocation()
    {
        List<Vector2Int> validLocations = new();
        List<Vector2> doorLocations = new();
        //Build up a list of places where there isn't a cell, where there could be
        foreach (Vector2Int location in map)
        {
            if (!map.Contains(new Vector2Int(location.x + 1, location.y)))
            {
                validLocations.Add(new Vector2Int(location.x + 1, location.y));
                doorLocations.Add(new Vector2(location.x + 0.5f, location.y));
            }
            if (!map.Contains(new Vector2Int(location.x - 1, location.y)))
            {
                validLocations.Add(new Vector2Int(location.x - 1, location.y));
                doorLocations.Add(new Vector2(location.x - 0.5f, location.y));
            }
            if (!map.Contains(new Vector2Int(location.x, location.y + 1)))
            {
                validLocations.Add(new Vector2Int(location.x, location.y + 1));
                doorLocations.Add(new Vector2(location.x, location.y + 0.5f));
            }
            if (!map.Contains(new Vector2Int(location.x, location.y - 1)))
            {
                validLocations.Add(new Vector2Int(location.x, location.y - 1));
                doorLocations.Add(new Vector2(location.x, location.y - 0.5f));
            }
        }

        int randomLocation = Random.Range(0, validLocations.Count);
        doors.Add(doorLocations[randomLocation]);
        return validLocations[randomLocation];
    } 

 Note that I'm using the random function over the range of elements this time, unlike my original script. This is a neat way of removing magic numbers, so when I re-wrote the spawning part for walls I could write it like this:

    //Randomly chose a wall to use
    GameObject wall;
    wall = walls[Random.Range(0, walls.Count)];

This means that you can just add prefabs to the script in the unity editor, and the code won't have to change. I did the same thing for spawning enemies and other room elements later. 


The finished script produced a map of locations, which I actually checked in a spreadsheet (by highlighting the relevant cells) for a couple of instances to see if they were valid. After satisfying myself that they were, I added in code to spawn the walls and doors, similar to before, and the result was a fully generated map. I exposed numberOfRooms to the unity editor, allowing me to set the size easily. The results are below.

Map generation, with 20 rooms on the right and 200 on the left (Techincally 2 more rooms on each, one for the start room and one for the boss room). Note that you can't return to the start if you don't cover the same room twice; there are no door loops

(A discussion of gameplay for this game is here)

 

Comments

Popular posts from this blog

Imperfect Control: Gameplay

Armageddump (Boss Rush Jam 2023)

Hair of the Wolf (GameOff 2022)