Hack #1: Apply Your Own Game Idea

Create new code cells to implement some of the sprite interactions or features you have ideated for your game. This exercise is crucial if you plan to have interactions with a Non-Player Character (NPC).

Challenge: Use the concepts of 2D arrays and nested loops to create and display interactions or features for your game. Think about how you can organize and manage different elements, such as NPC dialog, questions, and receiving answers.

%%html
<div id="questionsAnswers"></div>

<script>
var test = [
    ['math',[
        {q: "What is 1 + 1", a: 2},
        {q: "What is 1 * 1", a: 1},
        {q: "What is 5 cubed", a : 125},
        {q: "What is the sqr root of 25", a: 25},
        {q: "what is 123 squared", a: 15129}
    ]
    ],[
        'loops',[
            {q: "does this loop work?[if (num 1 = num 2){}  ](y/n)", a: "n"},
            {q: "does this loop work?[if (num 1 == num 2){}  ](y/n)", a: "y"},
            {q: "does this loop work?[for (let i = 1; i < 10, i++){}  ](y/n)", a: "y"},
        ]
    ]
]


for (let category of test){
    const h2 = document.createElement("h2")
    h2.innerHTML = category[0]
    document.getElementById('questionsAnswers').appendChild(h2);
    for (let qa of category[1]) {  // index 1 is the array of questions and answers
            // Create a p tag for each question and answer
            const p = document.createElement('p');
            p.innerHTML = `<strong>Q:</strong> ${qa.q} <br> <strong>A:</strong> ${qa.a}`;
            document.getElementById('questionsAnswers').appendChild(p);
        }
}
</script>

Hack #2: Display Individual Sprites

Create new code cell(s) to display individual sprites from a sprite sheet. This sprite sheet will potentially be used in your game.

Challenge: Use the concepts of 2D arrays, nested loops, and sprite metadata to extract and display individual sprites. Think about how you can manage and display different frames or animations for your game characters or objects.

%%html

<style>
    gameCanvasUnique {
        border: 4px solid rgb(4, 102, 33); /* Green border for the canvas */
    }
</style>

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

<script>
// Outer function 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: 'man',
            src: "./man.png",
            orientation: {
                rows: 8,
                columns: 13,
                header: 16,
                pad: 4,
                jagged: [13, 8, 10, 10, 10, 6, 4, 7]
            },
        };

        return spriteMetaData;
    }
    /**
     * Function to define the sprite metadata for Tux the penguin
     * @returns {Object} spriteMetaData - The metadata for the Tux sprite
     */
     function MonkeySpriteMetaData() {
        // NPC sprite data (Tux the penguin)
        const isLocal =  window.location.protocol === 'vscode-webview:' | false;
        const baseUrl = isLocal ? '.' : '/william_2025';
        const spriteMetaData = {
            name: 'monkey',
            src: "./monkey.png",
            orientation: {
                rows: 10,
                columns: 16,
                jagged: [16, 16, 16, 15, 15, 16, 15, 15, 0 ,16 ]
            },
            scale: {
                x: 0.99,
                y: 0.99
            }
        };
        return spriteMetaData;
    }

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

        // Method to draw each sprite individually
        draw() {
            // This is the size of the sprite file, calculated from the PNG file 
            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 the 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 monkey = new CanvasData(MonkeySpriteMetaData());
}

defineAndDrawSprite();
</script>