Concept
I’m in the process of creating a haunted-house-themed game where the player has purchased a dilapidated property, mainly because it was so cheap—no one else wanted it. The catch is that it’s infested with ghosts. My overarching goal is for the player to banish these ghosts, wave by wave, ultimately revealing the house’s true beauty once it’s free of any supernatural presence. I want to capture a sense of gradual transformation: at first, the environment is dim and unnerving, but it transitions to a bright, welcoming home as the player defeats all the ghosts.
Progress So Far
So far, I have built out a state machine that includes an intro screen, a main “PLAY” state, and placeholders for the final “WIN” and “END” screens. In the intro, the game briefly explains the story—that the house was bought cheaply because of the hauntings—then moves to the main gameplay once the user clicks the start button. The basic logic for wave progression is in place: after the initial wave of slower ghosts, I plan to introduce a second wave of faster ghosts, and ultimately a boss ghost that requires multiple hits to defeat. Each ghost’s code has been thoroughly tested in small increments, ensuring that the transition between waves feels smooth. I also integrated a rudimentary health system; each time a ghost vanishes without being clicked, the player’s health decreases, raising the stakes as they progress.
Class Implementation
A key aspect of my current setup is the Ghost class, which I designed to handle movement, timing, and click interaction. By encapsulating these behaviors, I’ve managed to keep my main draw() loop more organized and make it simpler to test the game’s logic wave by wave. Here is a condensed version of the Ghost class:
class Ghost { constructor(x, y) { this.x = x; this.y = y; this.size = 60; this.visible = true; this.wasClicked = false; this.vx = random(-2, 2); this.vy = random(-2, 2); this.spawnTime = millis(); this.lifespan = 5000; // 5 seconds } move() { if (!this.visible) return; this.x += this.vx; this.y += this.vy; if (this.x < 0 || this.x + this.size > width) { this.vx *= -1; } if (this.y < 0 || this.y + this.size > height) { this.vy *= -1; } if (millis() - this.spawnTime > this.lifespan) { this.visible = false; } } display() { if (this.visible) { image(ghostImg, this.x, this.y, this.size, this.size); } } isClicked(mx, my) { return ( this.visible && mx > this.x && mx < this.x + this.size && my > this.y && my < this.y + this.size ); } vanish() { this.visible = false; this.wasClicked = true; } }
Using this as a foundation, I have also been working on subclasses like FastGhost and BossGhost to provide unique behaviors—faster speed, shorter lifespans, or requiring multiple hits to defeat. This object-oriented structure ensures I can easily add or modify ghost types without complicating the main game flow.
Frightening / Challenging Aspects
One of the biggest challenges I’m facing is tuning the difficulty so it feels suspenseful without being overly punishing. In particular, I need to strike a balance between ghost speed, lifespan, and the number of ghosts per wave. If ghosts vanish too slowly, it’s too easy; if they move or time out too quickly, it becomes frustrating. Another tricky part is creating a strong contrast between the haunting atmosphere at the start and the serene, beautiful environment at the end—this requires careful coordination of art assets, lighting (or color usage), and the timing of transitions so players truly feel like they’ve “rescued” the house from a dark fate.
Risk Prevention
I have set up the game flow using distinct states—“INTRO,” “PLAY,” “WIN,” and “END”—to keep code separated and avoid any messy overlaps. Testing each state individually helps isolate potential bugs early. I also made sure that all ghost interaction, movement, and collision logic lives within their respective classes, so if there’s an issue with a particular ghost type, I know exactly where to look for a fix. By incrementally adding waves and testing them (rather than coding all three at once), I can ensure that each wave behaves as intended and that wave transitions don’t break the health or scoring systems. This structured approach reduces the chance of large-scale errors going unnoticed until late in development.
Next Steps
I still need to fully integrate the final boss ghost, which will serve as the game’s climactic encounter. Once the boss is in place, I will refine the wave progression so that defeating the boss triggers a major visual shift—the unveiling of the house’s hidden beauty. Finally, I’ll spend time polishing transitions between states, adjusting ghost behaviors, and ensuring that the health system and scoring remain engaging but fair. My aim is for players to feel a real sense of accomplishment when they see the haunting gloom replaced by a warm, inviting dwelling, thereby completing the game’s central promise of transforming a scary, cheap purchase into a comfortable new home.