Row of contents

Fun thing to note most of the rubric rules require you too review the code of the game, I believe most if not all code requirements have been met so to make it easier for you to read them and for me to double check I’m gonna list up each code requirement and connect it to a section of my game’s code with a neat little description as well, so here’s a table of links across this page that make it easy to go to any wanted rubric requirement instantaneously:

Object-Oriented Programming

  • Writing Classes, Create minimum 2 custom character classes extending base classes Code review: Player.js, NPC.js, Enemy.js,
  • Methods & Parameters, Implement methods with parameters (e.g., collisionHandler(other, direction)) Code review: Method signatures with 2+ parameters
  • Instantiation & Objects, Instantiate game objects in GameLevel configuration Code review: GameLevel setup objects
  • Inheritance (Basic), Create class hierarchy with 2+ levels (e.g., GameObject → Character → Player) Code review: extends keyword, inheritance chain
  • Method Overriding, Override parent methods (update(), draw(), handleCollision()) Code review: Polymorphic implementations
  • Constructor Chaining, Use super() to chain constructors Code review: super(data, gameEnv) calls

Lastly, the rubric objectives will be separated across pages depending on their category so here on the first of these pages that link to each other, the first category we will cover is … OBJECT-ORIENTED PROGRAMMING!!!

Writing Classes

Writing classes means litterally just making a class, the was litterally seen in, bullet.js and here **,

and in many more places, I’d be suprised if you made a game with 1 or no classes at all failing this objective spectacularly! Overall classes don’t just cover the constructor, they cover 90% of these .js files look how little there is after the end of the bullet.js class that isn’t class code (it’s what’s there after the yellow })


class Bullet {
    constructor(data) {
        this.x = data.x;
        this.y = data.y;
        this.velocity = data.velocity || { x: 0, y: 0 };
        this.gameEnv = data.gameEnv;
        this.shooter = data.shooter;
        this.direction = data.direction || 'down'; // down, left, right, up
        this.width = 40;
        this.height = 40;
        this.lifetime = 10000; // 10 seconds
        this.creationTime = Date.now();
        this.destroyed = false;
        this.isVisible = true;
        this.frameIndex = 0;
        this.frameCounter = 0;
        this.animationRate = 3;
    }

    update() {
        // Move the bullet continuously in its direction
        this.x += this.velocity.x;
        this.y += this.velocity.y;

        // Check lifetime (10 seconds)
        if (Date.now() - this.creationTime > this.lifetime) {
            this.destroy();
            return;
        }
    }

    draw() {
        if (this.destroyed) return;
        
        // Always show yellow cube
        this.gameEnv.ctx.fillStyle = 'yellow';
        this.gameEnv.ctx.fillRect(this.x, this.y, this.width, this.height);
    }

    checkCollision(target) {
        if (this.destroyed || !target) return false;

        return this.x < target.x + target.width &&
               this.x + this.width > target.x &&
               this.y < target.y + target.height &&
               this.y + this.height > target.y;
    }

    destroy() {
        if (!this.destroyed) {
            this.destroyed = true;
            // Don't remove from gameObjects - just mark as destroyed
            // This allows bullets to be cleaned up naturally by the game
        }
    }
}

export default Bullet;

what this code has

Class Declaration

class Bullet

means this creates a blueprint for “bullet”. If this were a player class, it could be used to make player objects

Constructor

    constructor(data) {

The constructor runs automatically when a new object is created.

It sets up the bullet’s starting data.

“this”

        this.x = data.x;
        this.y = data.y;
        this.velocity = data.velocity || { x: 0, y: 0 };
        this.gameEnv = data.gameEnv;
        this.shooter = data.shooter;
        this.direction = data.direction || 'down'; // down, left, right, up
        this.width = 40;
        this.height = 40;
        this.lifetime = 10000; // 10 seconds
        this.creationTime = Date.now();
        this.destroyed = false;
        this.isVisible = true;
        this.frameIndex = 0;
        this.frameCounter = 0;
        this.animationRate = 3;
    }

this stores values inside the current object.

Each bullet object gets its own: x (data), y (data), velocity (x,y axis), gameEnv, shooter, direction, width, height, lifetime, creationTime, destroyed (t/f statement), isVisible (t/f statement), frameIndex, frameCounter, animation Rate.

shoot function in shooterplayer.js

    shoot() {
        const currentTime = Date.now();
        if (currentTime - this.lastShotTime < this.shootCooldown) return;

        this.lastShotTime = currentTime;

        // Create bullet data based on facing direction
        let velocity = { x: 0, y: 0 };
        switch (this.facing) {
            case 'up': velocity.y = -6; break;
            case 'down': velocity.y = 6; break;
            case 'left': velocity.x = -6; break;
            case 'right': velocity.x = 6; break;
        }

        const bulletData = {
            x: this.position.x + this.width / 2 - 20,
            y: this.position.y + this.height / 2 - 20,
            velocity: velocity,
            gameEnv: this.gameEnv,
            shooter: this,
            direction: this.facing
        };

        const bullet = new Bullet(bulletData);
        this.bullets.push(bullet);
        this.gameEnv.gameObjects.push(bullet);
        console.log('Bullet spawned at', this.position.x, this.position.y, 'facing', this.facing);
    }

Attributes / Properties Stores object state/data

Example of purely attributes (it can cover any subject though):

        this.width = 40;
        this.height = 40;

the “extends” phrase covers the subject of inheritance (from character code)

class Character extends GameObject {

this means Character inherits data from GameObject

super is used in a child class to access things from the parent class.

constructor(data = null, gameEnv = null) {
    super(data, gameEnv);
}

Creating an Object From the Class

        const bullet = new Bullet(bulletData);

this creates a new bullet object

Using the Object

Input:

        this.bullets.forEach(bullet => bullet.destroy());

Output:

        // Clean up bullets when player is destroyed

Input:

        this.bullets = this.bullets.filter(bullet => {

Output:

        // Remove destroyed bullets

Input:

        const bullet = new Bullet(bulletData);
        this.bullets.push(bullet);
        this.gameEnv.gameObjects.push(bullet);

Output:

        console.log('Bullet spawned at', this.position.x, this.position.y, 'facing', this.facing);

Methods & Parameters

Methods

Methods are actions the object can perform.

This class has: update(), draw(), destroy(), checkCollision(target).

Parameters

    checkCollision(target) {

“target” is a parameter passed into the method.

Methods and Parameters covers stuff like handlers too, and although there are uses of “handle”’s in level3.js, the only 2 times “handler” appears in my custom code (it may also be in player.js) is in the variant version I made of Npc.js called enpeecee.js which has slightly different styles of functions while keeping the original Npc.js available for other use, here they are, one for he destroy function, and the other for clean up work!

destroy and update are the methods here

more examples of methods and parameters

   update() {
        if (this.gameOver) return;

        // Get player from game objects (created by GameLevel system)
        const player = this.gameEnv.gameObjects.find(obj => obj instanceof ShooterPlayer);
        if (!player) return;

        // Spawn enemies every 3 seconds
        const currentTime = Date.now();
        if (currentTime - this.lastSpawnTime > this.enemySpawnRate) {
            this.spawnEnemy();
            this.lastSpawnTime = currentTime;
        }

        // Update enemies and remove expired ones
        this.enemies.forEach((enemy, index) => {
            enemy.update();
            // Remove enemy after 5 seconds if not shot
            if (currentTime - enemy.spawnTime > this.enemyLifetime) {
                enemy.destroy();
                this.enemies.splice(index, 1);
            }
        });

        // Check bullet collisions with enemies
        player.bullets.forEach(bullet => {
            this.enemies.forEach((enemy, enemyIndex) => {
                if (bullet.checkCollision(enemy)) {
                    // Create hit marker at enemy position
                    const hitMarker = new HitMarker(
                        enemy.x + enemy.width / 2, // Center of enemy
                        enemy.y, // Top of enemy
                        this.gameEnv
                    );
                    this.gameEnv.gameObjects.push(hitMarker);

                    // Create explosion at enemy position
                    const explosion = new Explosion(
                        enemy.x + enemy.width / 2,
                        enemy.y + enemy.height / 2,
                        this.gameEnv
                    );
                    this.gameEnv.gameObjects.push(explosion);

                    // Enemy defeated!
                    bullet.destroy();
                    enemy.destroy();
                    this.enemies.splice(enemyIndex, 1);
                    this.score++;
                    this.scoreDisplay.textContent = `Wolves Eliminated: ${this.score}`;
                }
            });
        });
    update() {
        this.draw();
        this.collisionChecks();
        this.move();
    }

2 update methods ``` js draw() { // Clear the canvas before drawing this.clearCanvas();

    if (this.spriteSheet) {
        // Draw the sprite sheet frame
        this.drawSprite();
        // Update the frame index for animation
        this.updateAnimationFrame();
    } else {
        // Draw default red square
        this.drawDefaultSquare();
    }

    // Set up the canvas dimensions and styles
    this.setupCanvas();
} ``` > draw method  > {} usually represents an object or a block of code in JavaScript. > && means AND in code (pretty obvious use cases) <a id="instant"></a>

Instantiation & Objects

Difference Between create() and Instantiation Concept Meaning Instantiation Creating individual objects create() Setting up larger systems or environments Example const player = new Player();

Instantation uses the phrase “new”

Instantiation: creates ONE object.

GameEnv.create();

Setup method: initializes the entire environment.

            // Load the sprite sheet
            this.spriteSheet = new Image();

For this segment Instantiation and objects covers how gamelevel’s in the game engine set up objects in the game like characters, background areas, collisions, coins, bullets, etc. which for example can be seen here: in the class of Game Object constructor it sets up creates and instances which are used and applied in other parts of game object throughout all “Game Object”’s.

Inheritance (basic)

the “extends” phrase covers the subject of inheritance (from character code)

class Character extends GameObject {

this means Character inherits data from GameObject

Files in the same folder (as in links in base url in sub folders and code) can inherit things from one another using objects (key words) and IMPORTING data from other files to use in something new like EXTENDING from an object to a character to a player or an enemy.

Player class:

class Player extends Character

inherits from Character.

This one’s a classic, the heirarchy system of classes using phrases like “extends” it’s when a file of code is reused specified, and improved across multiple new creations of code like character.js being extend to make player.js or enemy.js, and those files extend to become shooterplayer.js and wolf.js, making grand new creations while baseing code off previous more general work, even character.js is an expansion of GameObject.js, increaseing efficiency and customization, without starting fresh every time and to produce checkpoints or landmarks of to take notes of or as an acheivement. this can be seen below:

Method Overriding

super is used in a child class to access things from the parent class.

constructor(data = null, gameEnv = null) {
    super(data, gameEnv);
}

Method overriding happens when a child class replaces a method from a parent class with its own custom version.

This is important in games because different objects may need different behaviors even if they share the same base structure.

Example: Character.js:

update() {
    super.update();

    if(!this.moved){
        if (this.gravity) {
            this.time += 1;
            this.velocity.y += 0.5 + this.acceleration * this.time;
        }
    }
    else{
        this.time = 0;
    }
}

Player.js

update() {
    this.draw();
    this.collisionChecks();
    this.move();
}

Not completely replacing the parent behavior.

I also use:

super.update();

This means:

“Run the original Character update first, THEN add custom Player behavior.”

That is advanced OOP usage and very good rubric evidence.

This proves polymprphic implementation, meaning Code outside doesn’t need to know the exact subclass It can call object.update() or object.handleCollision(…) The correct subclass version runs at runtime.

Constructor Chaining

basically using super() to chain constructors like:

Super notes

super is used in a child class to access things from the parent class.

constructor(data = null, gameEnv = null) {
    super(data, gameEnv);
}

++ means increase the value by 1.

forEach is a method used to go through every item in an array one at a time.

catch is used for handling errors in code. It works with try. Basic structure:

try {
    // code that might fail
} catch (error) {
    // code that runs if there is an error
}