Classes
Classes are blueprints for creating objects. They encapsulate data for the object and methods to manipulate that data. In JavaScript, classes are defined using the class
keyword.
Example:
Here I wrote a new class for an interactive object which are Trees which were used in my jungle level:
class Tree extends Character {
constructor(data) {
super(data);
// Basic properties
this.isChopped = false;
this.frameIndex = 0;
this.frameCounter = 0;
this.spriteData = data;
// Create two separate canvases - one for each animation
this.idleCanvas = this.canvas;
this.chopCanvas = document.createElement('canvas');
this.chopCtx = this.chopCanvas.getContext('2d');
// Load sprite sheet
this.spriteSheet = new Image();
this.spriteSheet.src = data.src;
// Set up canvases
this.spriteSheet.onload = () => {
const frameWidth = this.spriteData.pixels.width / this.spriteData.orientation.columns;
const frameHeight = this.spriteData.pixels.height / this.spriteData.orientation.rows;
// Setup idle canvas
this.idleCanvas.width = frameWidth;
this.idleCanvas.height = frameHeight;
this.idleCanvas.style.width = `${this.width}px`;
this.idleCanvas.style.height = `${this.height}px`;
this.idleCanvas.style.position = 'absolute';
this.idleCanvas.style.left = `${this.position.x}px`;
this.idleCanvas.style.top = `${GameEnv.top + this.position.y}px`;
// Setup chop canvas with same dimensions but initially hidden
this.chopCanvas.width = frameWidth;
this.chopCanvas.height = frameHeight;
this.chopCanvas.style.width = `${this.width}px`;
this.chopCanvas.style.height = `${this.height}px`;
this.chopCanvas.style.position = 'absolute';
this.chopCanvas.style.left = `${this.position.x}px`;
this.chopCanvas.style.top = `${GameEnv.top + this.position.y}px`;
this.chopCanvas.style.display = 'none';
// Add chop canvas to the game container
const container = this.idleCanvas.parentNode;
if (container) {
container.appendChild(this.chopCanvas);
}
// Add to game objects
if (!GameEnv.gameObjects.includes(this)) {
GameEnv.gameObjects.push(this);
}
};
}
// Handle tree chopping
chopTree() {
if (!this.isChopped) {
this.isChopped = true;
this.frameIndex = 0;
this.frameCounter = 0;
// Hide idle canvas and show chop canvas
this.idleCanvas.style.display = 'none';
this.chopCanvas.style.display = 'block';
}
}
// Draw the tree
draw() {
if (this.spriteSheet && this.spriteSheet.complete) {
const frameWidth = this.spriteData.pixels.width / this.spriteData.orientation.columns;
const frameHeight = this.spriteData.pixels.height / this.spriteData.orientation.rows;
// Get animation data
const animationData = this.isChopped ? this.spriteData.chop : this.spriteData.idle;
// Calculate position in sprite sheet
const frameX = (animationData.start + this.frameIndex) * frameWidth;
const frameY = animationData.row * frameHeight;
// Clear and draw on appropriate canvas
const currentCanvas = this.isChopped ? this.chopCanvas : this.idleCanvas;
const currentCtx = this.isChopped ? this.chopCtx : this.ctx;
currentCtx.clearRect(0, 0, currentCanvas.width, currentCanvas.height);
currentCtx.drawImage(
this.spriteSheet,
frameX, frameY, frameWidth, frameHeight,
0, 0, currentCanvas.width, currentCanvas.height
);
// Update animation frame based on animation rate from sprite data
this.frameCounter++;
if (this.frameCounter >= this.spriteData.ANIMATION_RATE) {
if (this.isChopped) {
this.frameIndex = Math.min(this.frameIndex + 1, animationData.columns - 1);
} else {
this.frameIndex = (this.frameIndex + 1) % animationData.columns;
}
this.frameCounter = 0;
}
}
}
update() {
this.draw();
}
}
export default Tree;
Methods
Methods are functions defined within a class. They describe the behaviors of the objects created from the class. Methods can manipulate the object’s data and perform actions.
Example:
Here I create a method to handle the player’s idle animation when a key is not pressed
handleIdleAnimation() {
// Update the idle frame index for animation at a slower rate
this.idleFrameCounter++;
if (this.idleFrameCounter % (this.animationRate * 1) === 0) { // Adjust the rate as needed
this.idleFrameIndex = (this.idleFrameIndex + 1) % this.spriteData.idle.columns;
}
// Draw the idle frame
const frameWidth = this.spriteData.pixels.width / this.spriteData.orientation.columns;
const frameHeight = this.spriteData.pixels.height / this.spriteData.orientation.rows;
const frameX = this.idleFrameIndex * frameWidth;
const frameY = this.spriteData.idle.row * frameHeight;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.drawImage(
this.spriteSheet,
frameX, frameY, frameWidth, frameHeight, // Source rectangle
0, 0, this.canvas.width, this.canvas.height // Destination rectangle
);
}
Instantiate Objects
To create an instance of a class, you use the new
keyword followed by the class name and parentheses. This process is called instantiation.
Example:
Here I create an array that stores instances of different gameobjects which is an demonstration of instantiating objects
this.objects = [
{ class: Background, data: image_data_jungle },
{ class: Player, data: sprite_data_explorer },
{ class: Npc, data: sprite_data_lumberjack },
{ class: Tree, data: sprite_data_tree }
];
Use Objects to Interact with Data/Methods
Once you have instantiated an object, you can interact with its data and methods using dot notation. This allows you to access and modify the object’s properties and call its methods.
Example:
Below you can see two snippets, one is the data being stored in “sprite_data_lumberjack” , and it’s properties being stored under a class (same snippest from above)
const sprite_src_lumberjack = path + "/images/gamify/lumberjack.png";
const LUMBERJACK_SCALE_FACTOR = 2.8;
const sprite_data_lumberjack = {
id: 'Lumberjack',
greeting: "Hi I am Lumberjack, I love chopping wood and exploring the jungle!",
src: sprite_src_lumberjack,
SCALE_FACTOR: LUMBERJACK_SCALE_FACTOR,
STEP_FACTOR: 1000,
ANIMATION_RATE: 25,
INIT_POSITION: { x: (width / 2) - (348 / LUMBERJACK_SCALE_FACTOR), y: height - (height / LUMBERJACK_SCALE_FACTOR) },
pixels: { height: 348, width: 348 },
orientation: { rows: 6, columns: 6 },
idle: { row: 0, start: 0, columns: 4 },
down: { row: 0, start: 0, columns: 3 },
left: { row: 2, start: 0, columns: 6 },
right: { row: 2, start: 0, columns: 6 },
up: { row: 4, start: 0, columns: 6 },
hitbox: { widthPercentage: 0.45, heightPercentage: 0.2 },
keypress: { up: 87, left: 65, down: 83, right: 68 }, // W, A, S, D
quiz: {
title: "What should I do?",
questions: [
"Should I chop this tree?\n1. Yes\n2. No",
]
}
this.objects = [
{ class: Background, data: image_data_jungle },
{ class: Player, data: sprite_data_explorer },
{ class: Npc, data: sprite_data_lumberjack },
{ class: Tree, data: sprite_data_tree }
];
Call Methods with Parameters and Handle Return Values
Methods can accept parameters and return values. Parameters allow you to pass data into the method, and return values allow the method to send data back to the caller.
Example:
Here can you see the “handleLumberjackQuiz” method is called with a paramter which si the quiz question. The prompt function returns the player’s input which is stored in the “answer” variable which is used to determine the next action
const answer = prompt(`${lumberjack.data.quiz.title}\n${question}`);
if (answer === '1') {
// If the player answers "1", chop the tree
alert("The tree was chopped down!");
tree.chopTree(); // Call chopTree on the actual tree instance
} else if (answer === '2') {
// If the player answers "2", do not chop the tree
alert("The tree was not chopped down.");
} else {
// If the player provides an invalid response, show an error message
alert("Invalid response. Please answer with 1 or 2.");
}
Implement Basic Inheritance for Code Reuse
Inheritance allows you to create a new class based on an existing class. The new class inherits the properties and methods of the existing class, allowing for code reuse and extension. In JavaScript, inheritance is implemented using the extends
keyword.
Example:
An example of inheritance being used is my Tree class, which inherits properties from an existing class (Character) to reuse it’s code which simplifies the Tree.js file, making it easier to work with. Below is a snippet of the Tree class inheriting the Character class’s property, and also a link to the a .drawio file displaying inheritance being used in the adventuregame
class Tree extends Character