Writing Classes

What is writing classes?

Writing classes are a big part of OOP (Object Oriented Programming). A class is an organized, repeatable way to make multiple functions for an object that share similar properties.

Benefits of writing classes?

  • Code reusability - write once, use many times
  • Efficiency - multiple functions in one class
  • Organization - organize related properties and methods together
  • Scalability - easy to manage multiple objects
  • Maintainability - changes in one place affect all instances
// Example class defining a game level with characters
class GameLevelPatrollingGuard {
    constructor(data) {
        this.level = data.level || 1;
        this.background = new Background(data.bg);
        this.player = new Player(data.player);
        this.npc = new PatrollingGuard(data.npc);
        this.gameObjects = [this.background, this.player, this.npc];
    }
    
    configureGameArea(canvas, ctx) {
        // Render all game objects
        this.gameObjects.forEach(obj => obj.draw(ctx));
    }
}

// Usage
const levelData = { level: 1, bg: {...}, player: {...}, npc: {...} };
const gameLevel = new GameLevelPatrollingGuard(levelData);

What does this code do?

  • Defines a GameLevelPatrollingGuard class with a constructor
  • Constructor accepts data and creates game objects
  • Stores background, player, and NPC in gameObjects array
  • configureGameArea() method renders all game objects
  • Shows how a single class organizes multiple related functions
  • Demonstrates constructor patterns and instance creation
// Child class extending parent class
class ExampleEnemy extends Enemy {
    constructor(data, gameEnv) {
        super(data, gameEnv);
        this.enemyType = 'Example';
    }
    
    handleCollisionEvent(projectile) {
        // Override: detect collision with player
        if (this.collidesWith(projectile)) {
            console.log("Enemy hit! Destroying player...");
            projectile.destroy();
        }
    }
}

What does this code do?

  • Extends Enemy parent class to create child class
  • Calls super() to initialize parent properties
  • Adds enemyType property specific to this enemy
  • Overrides handleCollisionEvent() method
  • Shows collision detection and object destruction
  • Demonstrates inheritance and method overriding together

Methods & Parameters

What are methods and parameters?

Methods are reusable blocks of code that perform specific tasks. Parameters are inputs to methods that let you pass data and customize behavior.

Benefits of methods and parameters?

  • Reusability - write code once, call many times
  • Flexibility - customize behavior through parameters
  • Maintainability - easier to update and debug
  • Clarity - descriptive names make code readable
  • Organization - group related functionality
// Class with multiple methods and parameters
class Guard {
    constructor(data, gameEnv) {
        this.x = data.x;
        this.y = data.y;
        this.health = 50;
        this.vx = 2;
        this.vy = 0;
    }
    
    // Method with parameters
    update(canvas) {
        this.stayWithinCanvas(canvas);
    }
    
    // Method to keep guard within canvas boundaries
    stayWithinCanvas(canvas) {
        if (this.x < 0 || this.x + 50 > canvas.width) {
            this.vx *= -1;
        }
        this.x += this.vx;
    }
    
    // Collision detection method
    handleCollisionEvent(projectile) {
        if (this.collidesWith(projectile)) {
            console.log("Guard hit!");
            projectile.destroy();
        }
    }
}

What does this code do?

  • Constructor takes data and gameEnv parameters to customize each instance
  • update() method takes canvas parameter for boundary checking
  • stayWithinCanvas() keeps guard bouncing within bounds
  • handleCollisionEvent() detects collisions with projectiles
  • Shows methods with different parameters and purposes
  • Demonstrates how parameters customize behavior

Instantiation & Objects

What is instantiation?

Instantiation means creating a new instance (copy) of an object from a class blueprint. Each instance has its own properties and data.

Benefits of instantiation?

  • Create multiple objects from one template
  • Reusability - same class for many objects
  • Organization - cleaner code structure
  • Easy management - change instances independently
  • Scalability - quickly add more objects
 const enemyData = {
                id: `waveEnemy_${this.currentWave}_${i}`,
                src: sprite_src,
                SCALE_FACTOR: 5,
                STEP_FACTOR: 0,
                ANIMATION_RATE: 8,
                INIT_POSITION: { x: xPos, y: yPos },
                pixels: { height: 1000, width: 3000 }, // full spritesheet dimensions
                orientation: { rows: 2, columns: 6 },
                left:  { row: 0, start: 0, columns: 6 }, // top row = ghost leaning left
                right: { row: 1, start: 0, columns: 6 }, // bottom row = ghost leaning right
                up:    { row: 0, start: 0, columns: 6 },
                down:  { row: 1, start: 0, columns: 6 },
                hitbox: { widthPercentage: 0.4, heightPercentage: 0.5 },
                healthPoints: 1,
                speed: speed
            };

            const enemy = new WaveEnemy(enemyData, this.gameEnv);
            this.waveEnemies.push(enemy);
            this.gameEnv.gameObjects.push(enemy);
        }
    }

In this code from our game level:

  • We make a class called enemyData
  • We use the keyword new to instantiate the enemy using the enemy class

Inheritence

What is inheritance?

Inheritance allows classes to acquire properties and methods from parent classes. Child classes can reuse parent code and add their own specialized behavior.

Benefits of inheritance?

  • Reuse code - write once in parent, use in all children
  • Create specialized versions - customize child behavior
  • Reduce duplication - avoid repeating code
  • Organize hierarchies - create logical class relationships
  • Share functionality - common methods in one place
// Parent class with common properties and methods
class Character {
    constructor(name, health) {
        this.name = name;
        this.health = health;
        this.isDead = false;
    }
    
    takeDamage(damage) {
        this.health -= damage;
        if (this.health <= 0) {
            this.isDead = true;
            console.log(this.name + " is dead!");
        }
    }
}

// Child class extends parent
class Guard extends Character {
    constructor(name, health) {
        super(name, health);
        this.patrolRadius = 100;
    }
    
    patrol() {
        console.log(this.name + " is patrolling...");
    }
}

// Usage
const guard = new Guard("Guard1", 50);
guard.takeDamage(10); // Uses parent method
guard.patrol(); // Uses child method

What does this code do?

  • Character parent class defines common properties (name, health)
  • takeDamage() is shared by all children
  • Guard child class calls super() to initialize parent
  • Guard adds specialized property patrolRadius
  • Guard adds specialized method patrol()
  • Shows how child reuses parent code while adding own features
// Another inheritance example
class Entity {
    constructor(x, y) {
        this.x = x;
        this.y = y;
        this.vx = 0;
        this.vy = 0;
    }
    
    move() {
        this.x += this.vx;
        this.y += this.vy;
    }
}

// Player adds score tracking to Entity
class Player extends Entity {
    constructor(x, y) {
        super(x, y);
        this.score = 0;
    }
    
    addScore(points) {
        this.score += points;
    }
}

// Usage
const player = new Player(100, 300);
player.move(); // From parent
player.addScore(10); // From child

What does this code do?

  • Entity parent class provides position and movement
  • Player child class calls super(x, y) for position
  • Player adds score-tracking functionality
  • Child inherits move() from parent
  • Child adds addScore() method
  • Shows inheritance creating specialized versions of common class

Method Overriding

What is method overriding?

Method overriding means creating a new version of a parent class’s method in a child class. Instead of using the parent’s method, the child uses its own customized version.

Benefits of method overriding?

  • Customization - each child class behaves differently
  • Polymorphism - call method on any child, correct behavior runs
  • Flexibility - change behavior without changing parent
  • Code reuse with differences - keep common code, change specifics
  • Cleaner code - no need for conditional logic
// Parent class with methods
class Enemy {
    constructor(data, gameEnv) {
        this.x = data.x;
        this.y = data.y;
        this.health = 100;
        this.vx = 2;
    }
    
    handleCollisionEvent(projectile) {
        console.log("Generic enemy hit");
    }
    
    stayWithinCanvas(canvas) {
        if (this.x < 0 || this.x + 50 > canvas.width) {
            this.vx *= -1;
        }
    }
}

// Guard child overrides both methods
class Guard extends Enemy {
    handleCollisionEvent(projectile) {
        // Override: destroy player instead
        console.log("Guard hit! Destroying player...");
        projectile.destroy();
        this.health -= 10;
    }
    
    stayWithinCanvas(canvas) {
        // Override: bounce at edges
        if (this.x < 0 || this.x + 50 > canvas.width) {
            this.vx *= -1;
            this.x += this.vx;
        }
    }
}

// ExampleEnemy overrides only collision
class ExampleEnemy extends Enemy {
    handleCollisionEvent(projectile) {
        // Override collision behavior
        console.log("Example enemy explodes!");
        projectile.destroy();
    }
    
    // Keeps parent stayWithinCanvas
}

What does this code do?

  • Parent Enemy class defines default methods
  • Guard child overrides both handleCollisionEvent() and stayWithinCanvas()
  • ExampleEnemy child only overrides handleCollisionEvent()
  • Each child can have completely different behavior
  • Shows flexibility of method overriding
  • Demonstrates partial override - some methods from parent, some custom

Constructor Chaining

What is constructor chaining?

Constructor chaining means one constructor calls another constructor so you can reuse setup code. This happens in classes with extends and super().

Benefits of constructor chaining?

  • Avoid repeating initialization code
  • Parent code runs first, then child code
  • Clean initialization of inheritance hierarchies
  • Automatic parent property setup
  • Maintainable inheritance relationships
// Parent class with constructor
class Enemy {
    constructor(data, gameEnv) {
        this.name = data.name;
        this.x = data.x;
        this.y = data.y;
        this.health = data.health || 50;
        this.vx = 0;
        this.vy = 0;
        console.log(this.name + " created");
    }
}

// Child class calls parent constructor with super()
class Guard extends Enemy {
    constructor(data, gameEnv) {
        super(data, gameEnv); // Calls parent constructor first
        
        // Child-specific initialization
        this.velocity = 2;
        this.patrolRadius = 100;
        console.log(this.name + " is guarding with velocity " + this.velocity);
    }
}

// Usage
const guardData = { name: "Guard1", x: 100, y: 300, health: 75 };
const guard = new Guard(guardData, gameEnv);

What does this code do?

  • super(data, gameEnv) calls parent constructor first
  • Parent constructor initializes name, x, y, health, velocities
  • After parent setup complete, child code runs
  • Child adds Guard-specific properties (velocity, patrolRadius)
  • Console shows parent initialization first, then child
  • Demonstrates proper constructor chaining pattern