Object-Oriented Game Development – Whack-a-Mole
Categories: HTML JavaScriptLearn more about Object Oriented Game Devlopment through our game Whack-a-Mole!
Lesson: Object-Oriented Game Development – Whack-a-Mole
In this lesson we will study how Object-Oriented Programming (OOP) principles can be applied to build a fun game: Whack-a-Mole.
We will not just play the game, but learn the design concepts behind it, how the code is organized, and why OOP is powerful for games and larger software projects.
This lesson combines:
- Teaching text to explain programming ideas step by step.
- Code examples in JavaScript.
- FRQ-style practice scattered throughout to simulate AP CSP exam thinking.
- Connections to Big Ideas (abstraction, data, algorithms, impact).
Learning Objectives
By the end of this lesson, you should be able to:
- Define and identify OOP principles (encapsulation, inheritance, polymorphism, composition).
- Explain how OOP helps organize complex programs like games.
- Understand how local storage can persist data beyond runtime.
- Analyze and modify code to meet new requirements (FRQs).
The Whack-a-Mole Concept
In Whack-a-Mole, the computer manages a grid of holes. Randomly, a mole (or sometimes a power-up) appears in a hole. The player must click or tap the mole before it disappears.
Key Rules:
- Moles give points when clicked.
- Power-ups may give bonus points or extra time.
- The game tracks score and saves the high score using local storage.
This simple idea is perfect for studying OOP: each part of the game (holes, moles, power-ups, score) can be represented as an object.
OOP Structure
We will design the game using classes:
Game
→ manages the entire program (composition).Hole
→ represents a place where something can appear.Entity
(abstract base class) → shared logic for anything that appears in a hole.Mole
→ a specific type ofEntity
.PowerUp
→ another type ofEntity
.
👉 This is an example of inheritance: both Mole
and PowerUp
inherit from Entity
.
👉 This is also composition: the Game
is composed of multiple Hole
objects.
UML Diagram (Visualizing OOP)
This diagram will show:
- Inheritance:
Mole
andPowerUp
extendEntity
. - Composition:
Game
has multipleHole
s, each containing at most oneEntity
. - Encapsulation: each class controls its own data and behavior.
Spawning Entities
The game must randomly choose where to put new moles or power-ups. This is handled inside the Game
class.
Here is the method that chooses an empty hole and places something in it:
spawnEntity() {
let emptyHoles = this.holes.filter(h => !h.entity);
if (emptyHoles.length > 0) {
let hole = emptyHoles[Math.floor(Math.random() * emptyHoles.length)];
if (Math.random() < 0.8) {
hole.entity = new Mole(hole, "normal");
} else {
hole.entity = new PowerUp(hole, "bonus");
}
}
}
Teaching Explanation
filter()
→ removes all holes that already have something inside.Math.random()
→ makes the game unpredictable.- If chance < 0.8, spawn a mole (80% chance). Otherwise, spawn a power-up (20% chance).
👉 Notice how the Game class does not directly create pixels on screen. Instead, it uses the object system: Hole
manages its own location, Entity
manages its own lifespan, etc.
📝 FRQ Checkpoint 1
Prompt:
- Explain why it is better for the
Game
class to delegate work to theHole
andEntity
classes instead of doing everything itself. - Modify the logic so that
GoldenMole
appears 5% of the time, worth double points. - Justify how this change demonstrates polymorphism.
Handling Player Input
When a player clicks, the game checks whether the click intersects with a mole or power-up. If so, it calls the object’s onHit()
method.
handleClick(mx, my) {
this.holes.forEach(hole => {
if (hole.entity &&
mx >= hole.x - hole.size/2 &&
mx <= hole.x + hole.size/2 &&
my >= hole.y - hole.size/2 &&
my <= hole.y + hole.size/2) {
hole.entity.onHit(this);
}
});
}
Teaching Explanation
- Each
Entity
object controls what happens when hit (encapsulation). - The
Game
just forwards the event. - Polymorphism:
onHit()
means something different for aMole
(gain points) versus aPowerUp
(bonus effects).
📝 FRQ Checkpoint 2
Prompt:
- Suppose the player clicks slightly outside the hole’s boundary. How does the code prevent counting that as a hit?
- Write pseudocode for a new entity
Bomb
that subtracts points when hit. - Explain how this uses inheritance and overriding methods.
Persisting High Scores with Local Storage
Without local storage, scores vanish when you refresh the page. Local storage lets us keep the high score.
Example implementation:
this.highScore = localStorage.getItem("highScore") || 0;
addScore(points) {
this.score += points;
if (this.score > this.highScore) {
this.highScore = this.score;
localStorage.setItem("highScore", this.highScore);
}
}
Teaching Explanation
localStorage.setItem(key, value)
→ saves data to browser storage.localStorage.getItem(key)
→ retrieves saved data.- This persists across sessions (until the user clears it).
👉 This is an example of data abstraction: we don’t worry about how the browser stores data, we just use a simple API.
📝 FRQ Checkpoint 3
Prompt:
- Explain why local storage is useful in a game but not always appropriate in secure applications.
- Modify the game so it also saves the last score in addition to high score.
- How does this relate to Big Idea 5: Impact of Computing?
Big Ideas Connections
- Big Idea 1: Creative Development → Using OOP to design reusable code.
- Big Idea 2: Data → High score stored in local storage.
- Big Idea 4: Algorithms → Random entity spawning and event handling.
- Big Idea 5: Impact → Ethical considerations of storing persistent data.
This project demonstrates how classroom theory applies to real-world interactive applications.
Reflection Questions
- How does encapsulation make debugging easier?
- How could inheritance reduce duplicated code in larger games?
- Why is polymorphism important when designing new entity types?
- What are some limitations of local storage for more advanced games?
👉 Answer these questions in your notebook to solidify your learning.