Conditionals

Conditionals are a way to control the flow of code and act as decision making.

Below is an example of some conditionals

If statements

The most common type of conditionals are if statements. if statements run the code inside of them when the specified condition is met.

%% js

// This is a function from the GameControl of adventureGame
// In this function, if GameState.quizCompleted is true, then it runs the handleLevelEnd function.

checkQuizCompletion: function() {
        if (GameState.quizCompleted) {
            this.handleLevelEnd();
        }
    }
%% js

// This is an example from GameControl.js in adventureGame. This is the gameLoop that runs the game.

gameLoop: function() {
        // Base case: leave the game loop 
        if (!GameEnv.continueLevel || GameState.quizCompleted) {
            if (GameState.quizCompleted) {
                this.routeToMapLevel();
            } else {
                this.handleLevelEnd();  
            }
            return;
        }
        // Nominal case: update the game objects 
        GameEnv.clear();
        for (let object of GameEnv.gameObjects) {
            object.update();  // Update the game objects
        }
        //console.log('Current transitionNPCS before checkTransitions:', this.transitionNPCS);
        this.checkTransitions();
        //console.log('Current transitionNPCS after checkTransitions:', this.transitionNPCS);
        this.checkQuizCompletion();
        this.handleLevelStart();
        // Recursively call this function at animation frame rate
        requestAnimationFrame(this.gameLoop.bind(this));
    }

If else statements

There are also if statements with another else component. If the condition is met, the code inside the curly braces for the if statement will run. If the condition is not met, it will run the else code. Here is an example below.

%% js

// This is the draw function in Background.js in adventureGame

draw() {
    const ctx = GameEnv.ctx;
    const width = GameEnv.innerWidth;
    const height = GameEnv.innerHeight;

    // This is the conditional
    // If there is an image, then it draws it according to the canvas dimensions.
    if (this.image) {
        // Draw the background image scaled to the canvas size
        ctx.drawImage(this.image, 0, 0, width, height);
    // If there is not an image, then the canvas is filled with the color represented by the hex code to 
    // the canvas dimensions.
    } else {
        // Fill the canvas with fillstyle color if no image is provided
        ctx.fillStyle = '#87CEEB';
        ctx.fillRect(0, 0, width, height);
    }
}

Switch statements

Another type of conditional are switch statements. Here is an example below.

%% js

// This is part of the code related to the movement of the player in adventureGame from Player.js.
// In this function, handleKeyUp, the velocity of the player is set to 0 when a key is no longer being pressed, 
// stopping their movement.

handleKeyUp({ keyCode }) {
    switch (keyCode) {
        case this.keypress.up: // case clause
            this.velocity.y = 0;
            break; // break statement
        case this.keypress.left: // case clause
            this.velocity.x = 0;
            break; // break statement
        case this.keypress.down: // case clause
            this.velocity.y = 0;
            break; // break statement
        case this.keypress.right: // case clause
            this.velocity.x = 0;
            break; // break statement
    }
}

In a switch statement the code in the parenthesis are evaluated and then compared to the values of the different case clauses. If it matches the value of one of the cases, then the code in that case clause is run. The break statements stop the code from running the next case if another was already met and run.

Iterations

Iterations are the repitions of code usually through loops. Here are examples of some of the different loops below.

for Loops

The most common loop is the for Loop. Inside the parenthesis of the for loop is where it is set how many times it will run.

%% js

// This is a function from GameControl.js in adventureGame

handleLevelEnd: function() {
    console.log('%c[GameControl] handleLevelEnd() called', 'color: purple; font-weight: bold;');
    // More levels to play 
    if (this.currentLevelIndex < this.levelClasses.length - 1) {
        alert("Level ended.");
    } else { // All levels completed
        alert("Game over. All levels completed.");
    }
    // Tear down the game environment
    // This is the for loop. In this case it will keep running until the index is less than or equal to 0
    for (let index = GameEnv.gameObjects.length - 1; index >= 0; index--) {
        const obj = GameEnv.gameObjects[index];
        if (obj && typeof obj.destroy === 'function') {
        obj.destroy();
        console.log(`%c[GameControl] Destroyed object: ${obj.id}`, 'color: red; font-weight: bold;');
        } else {
        console.warn('Object does not have a destroy method:', obj);
        }    
    }
}

// Each part of the iteration set up

let index = GameEnv.gameObjects.length - 1 // sets index to the last element of the GameEnv.gameObjects array
index >= 0 // sets the loop to keep running until index = 0
index-- // decreases index by 1, moves counter to the previous object in the array

In this case, the number of iterations is not known as it’s dependent on how many elements are in the gameObjects array. In some other cases the starting number would be set. The counter for this loop is the variable index. Index is set to start with the last element of the GameEnv.gameObjects array. Then the code inside the for loop will be run. After running this code index is decremented, lowering it’s value by one. So then the loop is started again with the next element at the end of the gameObjects array. This continues until index reaches 0 and the array is empty.

While loops

Another type of loop is a while loop. A while loop keeps running until the condition is no longer true.

%% js

let i = 0;

// Loop keeps running until i is 5
while (i < 5) {
    console.log("Iteration:", i);
    i++; // Increments i, increasing it by 1
}

Other Loops

Two other loop types I ended up adding when changing the game.

map function

This is an array method that allows you to transform each element and create a new array from those new values.

Here is the syntax

array.map(callback(currentValue, index, array), thisArg)

callback is a function that is performed for each element in the array. It can take three arguments.

  • currentValue: the current element being processed in the array
  • index (optional): index of current element being processed in the array
  • array (optional): the array that map was called upon thisArg (optional) is the value to use as this when executing the callback function

Here is the example of it that I put into the game below.

%% javascript

// This is from GameLevelMap.js in adventureGame

// the array
this.transitionNPCS = [
    { class: Npc, data: sprite_data_tux },
    { class: Npc, data: sprite_data_nomad },
    { class: Npc, data: sprite_data_octocat },
    { class: Npc, data: sprite_data_robot },
  ].map(npcData => {
    const npcDataFormatted = {
      ...npcData.data, // copies properties
      targetLevel: npcData.data.targetLevel,
      INIT_POSITION: npcData.data.INIT_POSITION,
      hitbox: npcData.data.hitbox,
      pixels: npcData.data.pixels
    };

this.transitinoNPCS is the array. npcData is the callback. For each npcData object, a new npcDataFormatted object is created.

forEach

This is also an array method like map. Similar to map, it performs a function once on every element in the array. Unlike map it doesn’t create a new array. It’s mainly used for things like logging or modifying elements.

Syntax is the same as map except for forEach

array.forEach(callback(currentValue, index, array), thisArg)
%% javascript

// This is the checkTransitions function from adventureGame. It has a forEach function in it.

checkTransitions: function() {
    const player = GameEnv.gameObjects.find(obj => obj instanceof Player); // Adjust as needed
    
    if (player) {
        //console.log('Player position:', player.position);
        //console.log('transitionNPCS:', this.transitionNPCS);

        // forEach method
        // For each npc there are collision checks.
        this.transitionNPCS.forEach(npc => {
            //console.log('Checking transition for NPC:', npc);
            if (
                player.position.x < npc.position.x + npc.hitbox.width &&
                player.position.x + player.hitbox.width > npc.position.x &&
                player.position.y < npc.position.y + npc.hitbox.height &&
                player.position.y + player.hitbox.height > npc.position.y &&
                npc.targetLevel
            ) {
                console.log(`Transitioning to ${npc.targetLevel}`);
                this.handleNPCTransition(npc.targetLevel);
            }
        });    
    }
},