Variables

Variables are storage containers for data values used within a programming language. Variables can be useful when changing preferences, selecting a background, creating animations, and changing assets in game programming.

Variable Inputs

To obtain data from user and save into a JavaScript variable requires the use of HTML.

The <input> HTML element is used to create interactive controls for web-based forms in order to receive data from the user.

  • Geeks for Geeks Referece on input tag
  • Remember in Jupyter Notebooks and in GitHub Pages we do not use <head> and <body> tags.
  • Additionally, we prefer using SASS for <style> in GitHub Pages.

Variable Outputs

To output a variable in JavaScript it works in combination with HTML as well. To output the variable game_speed or any errors it requires that outputs to be placed in the Document Object Model (DOM).

Additionally, it is common practice in debugging a program to output to console.log.

To view DOM and Developer Outputs

  • Open Help -> Toggle Developer Tools
  • Go to Console menu, you can clear console to view outputs from console comands in this cell
  • To see DOM go to Elements tab and use selector and click on output in this window.
%%html 

<!-- Input definitions -->
<div>
  <label for="speed">Adjust Speed (1-5):</label>
  <input type="number" id="speed" name="speed" min="1" max="5" value="2">
  <button onclick="submitSpeed()">Submit</button>
</div>

<!-- Document Object Model (DOM) output locations -->
<div id="output"></div>
<div id="error"></div>

<script>
  // Function to validate and output the game speed value
  function submitSpeed() {
    let gameSpeed = document.getElementById('speed').value;
    
    // Ensure the value is within the allowed range
    if (gameSpeed < 1 || gameSpeed > 5) {
      // Set/clear output messages when there is an error
      document.getElementById('output').innerHTML = "";
      // Output an error message to the console
      console.error("Invalid game speed value:", gameSpeed);
      // Output an error message to the HTML page
      document.getElementById('error').innerHTML = "Invalid game speed value: " + gameSpeed;
    } else {
      // Set/clear error messages when the value is valid
      document.getElementById('error').innerHTML = "";
      // Output the game speed value to the console
      console.log("Game speed set to:", gameSpeed);
      // Output the game speed value to the HTML page
      document.getElementById('output').innerHTML = "Game speed set to: " + gameSpeed;
    }
  }
</script>

Variable Naming Conventions

  • camelCase In camel case, the first word is lowercase, and the first letter of the remaining words are uppercase: gameSpeed
  • snake_case In snake casse, the words in variables are all lowercase and are separated by a underbar: game_speed
  • PascalCase In PascalCase, the first letter in the word is capitalized: GameSpeed

In JavaScript, there will be usage of all three types in the RPG project.

Variables containing Variables (Reference Types)

There are variables that contain other variables. These are called reference types.

  • JSON Objects: These variables contain key names and associated values (key-value pairs). The key is used for reference and the value contains the data.
    • Key: This is similar to a variable name. The key describes the value and is followed by a colon.
    • Value: The value is associated with the key.
    • Sample Definition: let gameAttribute = {"Speed": 3, "Avatar": "Mario"}
    • Sample Reference: gameAttribute["Speed"]
    • Sample Output from Reference: 3
  • JSON Arrays: These variables contain a sequence of values.
    • Sample Definition: let scoreHistory = [50, 75, 66, 80, 100, 85]
    • Sample Reference: scoreHistory[2]
    • Sample Output from Reference: 66
%%js

// Reference Type example
// Open VSCode Help -> Toggle Developer Tools -> Console

// JSON Object
let gameAttribute = {
    "Speed": 3,
    "Avatar": "Mario"
};

// Accessing JSON Object values
// Accessing JSON Object values using dot notation
console.log("Speed-Dot:", gameAttribute.Speed); // Output: Speed: 3
console.log("Avatar-Dot:", gameAttribute.Avatar); // Output: Avatar: Mario

// Accessing JSON Object values using bracket notation
console.log("Speed-Bracket:", gameAttribute["Speed"]); // Output: Speed: 3
console.log("Avatar-Bracket:", gameAttribute["Avatar"]); // Output: Avatar: Mario

// JSON Array
let scoreHistory = [50, 75, 66, 80, 100, 85];

// Accessing JSON Array values
console.log("Score at index 2:", scoreHistory[2]); // Output: Score at index 2: 66

Variable Naming Convention Hack

Search up naming convention for JavaScript. Conventions are important to easily recognize purpose of definitions.

  1. Usage of camelCase. What is standard?
    • A var or let variables in JavaScript should use camelCase.
    • ChatGPT Explanation: camelCase is a common convention for naming variables and functions in JavaScript. It starts with a lowercase letter and capitalizes the first letter of each subsequent word (e.g., myVariableName)
  2. Usage of UPPER_CASE snake_case. What is standard?
    • A const variable should use SNAKE_CASE.
    • ChatGPT Explanation: While not universally enforced, it is a common convention to use UPPER_CASE with underscores (SNAKE_CASE) for constants to indicate that their values should not change (e.g., SCALE_FACTOR).
  3. Usage of PascalCase. What is standard?
    • A class definitions in JavaScript follow PascalCase naming.
    • ChatGPT Explanation: PascalCase is used for class names in JavaScript. It capitalizes the first letter of each word, including the first word (e.g., GameControl).

RPG Project Hack

Identify naming convention and data type used in this code. Which items would you change according to your research on naming conventions? What are the data types? Change names and provide comments to to describe data types and name convention changes.

  1. Make a new code cell to make it easy to compare before and after.
  2. Figure out naming convention for Key-Values items. Source and share your reference.
import GameControl from '/john_2025/assets/js/rpg/GameControl.js';

// Background data
const image_src = "/john_2025/images/rpg/water.png";
const image_data = {
    pixels: {height: 580, width: 1038}
};
const image = {src: image_src, data: image_data};

// Sprite data
const sprite_src = "/john_2025/images/rpg/turtle.png";
const sprite_data = {
    SCALE_FACTOR: 10,
    STEP_FACTOR: 1000,
    ANIMATION_RATE: 50,
    pixels: {height: 280, width: 256},
    orientation: {rows: 4, columns: 3 },
    down: {row: 0, start: 0, columns: 3 },
    left: {row: 1, start: 0, columns: 3 },
    right: {row: 2, start: 0, columns: 3 },
    up: {row: 3, start: 0, columns: 3 },
};
const sprite = {src: sprite_src, data: sprite_data};

// Assets for game
const assets = {image: image, sprite: sprite}

// Start game engine
GameControl.start(assets);
%%js

// Background data
const imageSrc = "/john_2025/images/rpg/water.png";
const imageData = {
    pixels: {height: 580, width: 1038}
};
const image = {src: imageSrc, data: imageData};

// Sprite data
const spriteSrc = "/john_2025/images/rpg/turtle.png"; // String: URL for the sprite image
const spriteMetaData = {
    SCALE_FACTOR: 10, // Number: Determines the size of the sprite relative to the canvas. A smaller number makes the sprite larger.
    STEP_FACTOR: 1000, // Number: Divides the canvas into 1000 steps for smooth and consistent movement of the sprite.
    ANIMATION_RATE: 50, // Number: Controls how fast the sprite's animation plays. A smaller number makes the animation faster.    pixels: {height: 280, width: 256}, // Object: Dimensions of the sprite
    orientation: {rows: 4, columns: 3}, // Object: Orientation of the sprite
    down: {row: 0, start: 0, columns: 3}, // Object: Data for the down direction
    left: {row: 1, start: 0, columns: 3}, // Object: Data for the left direction
    right: {row: 2, start: 0, columns: 3}, // Object: Data for the right direction
    up: {row: 3, start: 0, columns: 3}, // Object: Data for the up direction
};
const sprite = {src: spriteSrc, data: spriteMetaData}; // Object: Sprite data

// Assets for game
const assets = {image: image, sprite: sprite}; // Object: Game assets

console.log("Game Assets", assets);

Code Block Hack

Make a new code block for some game elements that you have in your “Ideation”.

%%html

<style>
    #gameCanvas {
        border: 4px solid rgb(102, 4, 4);
    }
</style>

<canvas id="gameCanvas" width="521" height="967"></canvas>

<script>
    function drawTuxSprite() {
        // NPC sprite data (Tux the penguin)
        const spriteMetaData = {
            name: 'tux',
            src: "./tux.png",
        };

        // Create the canvas and context
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');

        // Load the sprite image
        const spriteImage = new Image();
        spriteImage.src = spriteMetaData.src;

        // Function to draw the sprite on the canvas
        function drawSprite() {
            const width = spriteImage.width; 
            const height = spriteImage.height;

            ctx.drawImage(spriteImage, 0, 0, width, height);
        }

        // Draw the sprite once the image is loaded
        spriteImage.onload = drawSprite;
    }

    // Call the function to draw the sprite
    drawTuxSprite();
</script>
%%html

<style>
    #gameCanvasUnique {
        border: 4px solid rgb(4, 102, 33);
    }
</style>

<canvas id="gameCanvasUnique" width="521" height="967"></canvas>

<script>
// outer fucntion is required by Jupyter Notebook to avoid conflicts
function defineAndDrawSprite() {

    /**
     * Function to define the sprite metadata for Tux the penguin
     * @returns {Object} spriteMetaData - The metadata for the Tux sprite
     */
    function TuxSpriteMetaData() {
        // NPC sprite data (Tux the penguin)
        const spriteMetaData = {
            name: 'tux',
            src:  "./tux.png",
            orientation: {rows: 10, columns: 8, 
                    header: 16, pad: 2, 
                    jagged: [1, 2, 1, 1, 1, 1, 1, 7, 8, 4] 
                },
        };

        return spriteMetaData;
    }

    /**
     * Class to handle the canvas data and drawing of the sprite
     */
    class CanvasData {
        constructor(spriteMetaData) {
            this.INIT_POSITION = { x: 0, y: 0};
            this.spriteMetaData = spriteMetaData;
            this.canvas = document.getElementById('gameCanvasUnique');
            this.ctx = this.canvas.getContext('2d');
            this.spriteImage = new Image();
            this.spriteImage.src = spriteMetaData.src
        }

        // Method to draw each sprite individually
        draw() {
            // This is the size of the sprite, calculated from the sprite sheet
            const sheetWidth = this.spriteImage.width; 
            const sheetHeight = this.spriteImage.height;
            // This meta data describes the sprite sheet
            const rows = this.spriteMetaData.orientation.rows;
            const cols = this.spriteMetaData.orientation.columns;
            const jagged = this.spriteMetaData.orientation.jagged || null;
            const header = this.spriteMetaData.orientation.header || 0;
            const pad = this.spriteMetaData.orientation.pad || 0;
            // This is initial output position on the canvas
            const x = this.INIT_POSITION.x;
            const y = this.INIT_POSITION.y;

            // Calculate the dimensions of each individual sprite
            const spriteWidth = sheetWidth / cols;
            const spriteHeight = (sheetHeight - header * rows) / rows;

            console.log(`Sprite Sheet Dimensions: ${sheetWidth}x${sheetHeight}`);
            console.log(`Individual Sprite Dimensions: ${spriteWidth}x${spriteHeight}`);
            console.log(`Rows: ${rows}, Columns: ${cols}`);

            // Nested for loop to draw 2 dimensional sprite sheet
            for (let row = 0; row <  rows; row++) {
                const columnsInRow = jagged ? jagged[row] || cols : cols;
                for (let col = 0; col < columnsInRow; col++) {
                    const srcX = col * spriteWidth;
                    const srcY = row * (spriteHeight + header ) - (pad * row);
                    const destX = x + col * spriteWidth;
                    const destY = y + row * spriteHeight;
                    const destWidth = spriteWidth;
                    const destHeight = spriteHeight;

                    console.log(`Drawing row: ${row}, column: ${col}`);
                    console.log(`Source: (${srcX}, ${srcY}, ${spriteWidth}, ${spriteHeight})`);
                    console.log(`Destination: (${destX}, ${destY}, ${destWidth}, ${destHeight})`);

                    this.ctx.drawImage(
                        this.spriteImage,
                        srcX, srcY + header, spriteWidth, spriteHeight, // Source rectangle
                        destX, destY, destWidth, destHeight // Destination rectangle
                    );
                }
            }
        }

    }

    // Setup to Tux sprite
    const tux = new CanvasData(TuxSpriteMetaData());
    tux.draw();
}

defineAndDrawSprite();
</script>