Skip to content

LevelLock on LevelSelectScreen

aadityayadav17 edited this page Oct 17, 2023 · 2 revisions

The LevelSelectScreen class in the game is responsible for implementation of level locking mechanism. Level locking mechanism restricts players from accessing levels they haven't reached or unlocked yet.


As you can see here, DESERT and LAVA levels are locked initially, symbolised by their black and white texture, and hence can't be accessed by players as soon as they enter the Game.

Screenshot 2023-10-17 at 3 19 08 PM


currentLevel

The value of currentLevel is passed as a parameter when an instance of LevelSelectScreen is created. This happens in the constructor:

public LevelSelectScreen(GdxGame game, int currentLevel) {
    this.currentLevel = currentLevel;
    // ... rest of the constructor code ...
}

When creating an instance of LevelSelectScreen, the caller provides the value of currentLevel, and it's stored in the instance variable currentLevel for that specific LevelSelectScreen object. There is no default value set for currentLevel within the LevelSelectScreen class.


Numbering of Planets

In the game, each level (planet) has a level. But as the game follows an unconventional numbering, i.e. ICE (1) -> DESERT (default i.e. =0) -> LAVA (2). To solve this issue, and make the logic of code easier to understand and implement, the LevelSelectScreen uses a mapToConventional() method.

public int mapToConventional(int unconventionalNumber) {
        switch (unconventionalNumber) {
            case -1:
                return -1;
            case 1:
                return 0;
            case 0:
                return 1;
            case 2:
                return 2;
            default:
                throw new IllegalArgumentException("Invalid planet number");
        }
    }

The logic for this method is:

  • When unconventional level of planets is passed onto LevelSelectScreen, it modifies them to make them more along the lines of 0 -> 1 -> 2. This is a more conventional numbering that makes it easy to apply logic.
  • For -1, the LevelSelectScreen is initialised to -1, whenever it is accessed from StoryScreen.

Level-locking mechanism is mainly achieved through the use of two main methods: spawnPlanets and spawnPlanetBorders.

spawnPlanet()

The spawnPlanets method is responsible for rendering planets on the level selection screen. Here's how the mechanism works:

  • The method checks the current game state using the currentLevel variable. This currentLevel is then set as highestLevelReached in the method.
  • As the game follows an unconventional approach of numbering each level, the method uses mapToConventional() to convert it into a more conventional numbering to make it easy for loading correct images.
  • The spawnPlanet method is invoked for each planet type (ICE, DESERT, LAVA) with specific parameters including their positions, visuals, and associated level numbers.
  • Inside the spawnPlanet method, the code decides which image to use for each planet based on the currentLevel and the planet's level.
  • If a player has reached or surpassed a planet's level requirement, the planet is rendered in its colorful, unlocked state.
  • If a player hasn't reached a planet's level requirement, the planet is rendered in a black & white state, indicating it's locked.
private void spawnPlanet(int width, int height, int posx, int posy, String planetName, int version, int frame, int levelNumber) {
        int highestLevelReached = currentLevel;

        levelNumber = mapToConventional(levelNumber);
        if (levelNumber == 0 && highestLevelReached >= -1)  { // ICE planet, which is always unlocked initially
            planet = new Texture(String.format("planets/%s/%d/%d.png", planetName, version, frame));
        } else if (levelNumber == 1 && highestLevelReached >= 0) { // DESERT planet, unlocked only when highestLevelReached is 0
            planet = new Texture(String.format("planets/%s/%d/%d.png", planetName, version, frame));
        } else if (levelNumber == 2 && highestLevelReached >= 1) { // LAVA planet, unlocked after DESERT
            planet = new Texture(String.format("planets/%s/%d/%d.png", planetName, version, frame));
        } else {
            // Display the planet in b&w if it's locked
            planet = new Texture(String.format("planets/%s_bw/%d/%d.png", planetName, version, frame));
        }

        // other code
    }

spawnPlanetBorders()

The spawnPlanetsBorders method is responsible for rendering borders around the planets, and checking if the level being hovered over is unlocked. Here's how the mechanism works:

  • If the mouse is hovering over a planet, a border is drawn around the planet.
  • The method checks if the planet's level is unlocked based on the currentLevel value.
  • If a player clicks on an unlocked planet, the game proceeds to that level. However, clicking on a locked planet generates a log statement indicating an attempt to load a locked level.
private void spawnPlanetBorders() {
        int highestLevelReached = currentLevel;

        ....
        for (int[] planet : Planets.PLANETS) {
            Rectangle planetRect = new Rectangle(planet[0], planet[1], planet[2], planet[3]);
            if (planetRect.contains(mousePos.x, (float) Gdx.graphics.getHeight() - mousePos.y)) {
                // If the mouse is over a planet, draw the planet border
                Sprite planetBorder = new Sprite(new Texture("planets/planetBorder.png"));
                batch.draw(planetBorder, planet[0] - 2.0f, planet[1] - 2.0f, planet[2] + 3.0f, planet[3] + 3.0f);

                int conventionalPlanetLevel = mapToConventional(planet[4]);
                // Check if planet level is unlocked before allowing click
                ...

                if (Gdx.input.justTouched()) {
                    if (conventionalPlanetLevel == 0 && highestLevelReached >= -1) { // ICE planet, which is always unlocked initially
                        loadPlanetLevel(planet);
                    } else if (conventionalPlanetLevel == 1 && highestLevelReached >= 0) { // DESERT planet, unlocked only when highestLevelReached is 0
                        loadPlanetLevel(planet);
                    } else if (conventionalPlanetLevel == 2 && highestLevelReached >= 1) { // LAVA planet, unlocked after DESERT
                        loadPlanetLevel(planet);
                    } else {
                        logger.info("Attempted to load locked level {}", planet[4]);
                        // Add feedback for the player here if necessary
                    }
                }
            }
        }

UML Sequence Diagram

This sequence diagram tells how level locking mechanism is designed to work work with different screens across the game. UML Seq LevelLock


Level Lock Mechanism Testing

Since, unit testing of the mechanism was nearly impossible. I undertook a visual approach. To save time, this was done by modifying the win & lose condition in MainGameScreen, to trigger one of these conditions, and therefore trigger one of WinningScreen, NextLevelScreen and LoosingScreen, as soon as the level was clicked.

Clone this wiki locally