CHAPTER 9: MINI-GAME PROJECT

Game States

Start/Stop Logic & Reset Functionality
Episode 7 of 10
FattyMcTweak
FattyMcTweak

Are we seriously still working on this clicker game? I've already charged the client for THREE premium gaming engines while you all struggle with basic JavaScript!

CodyMcTweak

Hey, I may be the free version, but at least I don't crash every time someone clicks a button. Unlike your 'premium' code that collapses faster than my self-esteem during code reviews.

CodyMcTweak
TrashyMcTweak
TrashyMcTweak

Speaking of crashing, have you seen the state machine these kids just submitted? Game states COMPLETELY hardcoded! Know what happens when you hit the reset button? BOOM, permadeath! Not even a confirmation dialog! It's like watching someone code with oven mitts on!

GrumpyMcTweak

RESET BUTTON?! They implemented a RESET button without SECURITY PROTOCOLS?! Do you know how many games have been COMPROMISED because someone accidentally hit reset during a high score? THIS is how SkyNet starts - with an unsecured reset button!

GrumpyMcTweak
AllyMcTweak
AllyMcTweak

Relax, Grumpy. It's a clicker game, not nuclear launch codes. Though I do agree we need a proper state management system. Users need to know if the game is ready, playing, paused, or game over. And yes, we absolutely need confirmation before reset.

AshleyMcTweak

Actually, legally speaking, we're required to save user scores before any reset. If we don't maintain score history, we could be in violation of the "Temporary Electronic Achievement Conservation Act" - a completely real law I definitely didn't just make up to mess with all of you.

AshleyMcTweak
GarbageMcTweak
GarbageMcTweak

*sighs deeply* I literally paused at the Elden Ring final boss for this? Game states are simple: enumerate your possible states, create a state machine to manage transitions, and never modify game variables outside state change functions. Now if you'll excuse me, I have a Malenia fight to finish.

SnowzieMcTweak

*excited tail wagging* WOOF! *paws at reset button frantically*

SnowzieMcTweak
GrumpyMcTweak
GrumpyMcTweak

NO! BAD DOG! STEP AWAY FROM THE RESET BUTTON! That's it, I'm adding biometric security to this game RIGHT NOW!

Learning Objectives

By the end of this episode, you'll be able to:

  • Implement proper game state management (ready, playing, paused, game over)
  • Create start/stop controls with appropriate state transitions
  • Build a reset function with confirmation to prevent accidental data loss
  • Update the game UI to reflect the current state

Game State Theory

AllyMcTweak
AllyMcTweak

Let's talk about game states, which are essential for managing your game's flow. For our clicker game, we need four basic states:

  • READY: Before the game starts
  • PLAYING: While the game is active
  • PAUSED: When the game is temporarily stopped
  • GAME_OVER: When time runs out or other end conditions are met
Game States Definition
// Define game states as constants
const GAME_STATE = {
  READY: 'ready',
  PLAYING: 'playing',
  PAUSED: 'paused',
  GAME_OVER: 'gameover'
};

// Track current game state
let currentState = GAME_STATE.READY;

// Game variables
let score = 0;
let timeRemaining = 30;
let timerId = null;
let targetId = null;
TrashyMcTweak

Using string constants like 'ready' instead of just numbers? That's actually... not terrible. Strings are self-documenting, unlike those garbage numeric state codes everyone used in the 90s. Fine, I'll admit it's decent code.

TrashyMcTweak
GrumpyMcTweak
GrumpyMcTweak

The state variables should be FROZEN to prevent accidental modifications! What's stopping someone from directly changing GAME_STATE.READY = 'something_else'?! NOTHING! That's what! CONSTANT VIGILANCE!

Improved Game States Definition
// Define game states as frozen object for security
const GAME_STATE = Object.freeze({
  READY: 'ready',
  PLAYING: 'playing',
  PAUSED: 'paused',
  GAME_OVER: 'gameover'
});

State Transitions

GarbageMcTweak
GarbageMcTweak

Game state isn't just about knowing what state you're in - it's about controlling transitions between states. Not all transitions are valid. For example, you can't go from GAME_OVER directly to PLAYING without resetting first.

State Transition Diagram

┌─────────┐      start()      ┌─────────┐      pause()      ┌─────────┐
│  READY  │─────────────────> │ PLAYING │─────────────────> │ PAUSED  │
└─────────┘                   └─────────┘                   └─────────┘
     ^                             │                             │
     │                             │                             │
     │                         timeOut()                     resume()
     │                             │                             │
     │                             ▼                             │
     │                       ┌─────────┐                         │
     │                       │GAME_OVER│                         │
     │                       └─────────┘                         │
     │                             │                             │
     └─────────────────────────────┴─────────────────────────────┘
                              reset()
FattyMcTweak

ASCII art? Really? For what we're charging clients, we could have created a fully animated 3D hologram state transition diagram! But I guess this works too... if you're into that retro aesthetic.

FattyMcTweak
State Transition Functions
// Change game state with proper logic
function changeGameState(newState) {
  const prevState = currentState;
  currentState = newState;
  
  // Update UI to reflect state change
  updateGameUI();
  
  // Log state transition for debugging
  console.log(`Game state changed from ${prevState} to ${newState}`);
}

// Start the game
function startGame() {
  if (currentState === GAME_STATE.READY || currentState === GAME_STATE.PAUSED) {
    changeGameState(GAME_STATE.PLAYING);
    
    // Start the timer
    timerId = setInterval(() => {
      timeRemaining--;
      updateTimerDisplay();
      
      if (timeRemaining <= 0) {
        endGame();
      }
    }, 1000);
    
    // Start creating targets
    createTarget();
  }
}

// Pause the game
function pauseGame() {
  if (currentState === GAME_STATE.PLAYING) {
    changeGameState(GAME_STATE.PAUSED);
    
    // Stop the timers but keep the data
    clearInterval(timerId);
    clearTimeout(targetId);
  }
}

// Resume the game
function resumeGame() {
  if (currentState === GAME_STATE.PAUSED) {
    startGame();
  }
}

// End the game
function endGame() {
  if (currentState === GAME_STATE.PLAYING) {
    changeGameState(GAME_STATE.GAME_OVER);
    
    // Stop all timers
    clearInterval(timerId);
    clearTimeout(targetId);
    
    // Check for high score
    checkAndSaveHighScore();
  }
}
CodyMcTweak
CodyMcTweak

I'm just amazed I can handle these state transitions without crashing! Even with my limited credit allocation, these simple checks for valid state transitions are very efficient!

Reset Functionality

AshleyMcTweak
AshleyMcTweak

The reset function is legally sensitive. We need to make sure users don't accidentally lose their progress. A simple confirmation dialog is the minimum industry standard. And yes, I'm making that up, but it's still good practice!

GrumpyMcTweak

A SIMPLE DIALOG?! We need at least THREE confirmation steps, a CAPTCHA, and possibly BIOMETRIC VERIFICATION before allowing reset! What if someone ACCIDENTALLY hits the button during a RECORD BREAKING RUN?!

GrumpyMcTweak
Reset Functionality
// Reset the game
function resetGame() {
  // Only allow reset from certain states
  if (currentState === GAME_STATE.PAUSED || currentState === GAME_STATE.GAME_OVER) {
    
    // Confirm reset if there's a score to lose
    if (score > 0) {
      const confirmed = confirm("Are you sure you want to reset the game? Your current score will be lost!");
      if (!confirmed) {
        return; // User canceled reset
      }
    }
    
    // Reset game variables
    score = 0;
    timeRemaining = 30;
    
    // Clear any existing timers
    clearInterval(timerId);
    clearTimeout(targetId);
    timerId = null;
    targetId = null;
    
    // Clear game area
    const gameArea = document.getElementById('gameArea');
    gameArea.innerHTML = '';
    
    // Return to ready state
    changeGameState(GAME_STATE.READY);
    
    // Update displays
    updateScoreDisplay();
    updateTimerDisplay();
  }
}
FattyMcTweak
FattyMcTweak

You call that a reset function? In my premium version, we save the player's entire session history, offer them a highlight reel of their best moments, and use predictive analytics to improve their next run. All for just $1000 per month!

UI Updates Based on Game State

AllyMcTweak
AllyMcTweak

The UI needs to clearly communicate the current game state to players. For example, during the READY state, we should highlight the start button. During PLAYING, we should disable the reset button to prevent accidents.

UI Update Function
// Update UI based on current game state
function updateGameUI() {
  const startButton = document.getElementById('startButton');
  const pauseButton = document.getElementById('pauseButton');
  const resetButton = document.getElementById('resetButton');
  const gameStateDisplay = document.getElementById('gameState');
  const gameArea = document.getElementById('gameArea');
  
  // Remove all state classes
  gameStateDisplay.classList.remove('state-ready', 'state-playing', 'state-paused', 'state-gameover');
  
  // Update UI based on current state
  switch (currentState) {
    case GAME_STATE.READY:
      gameStateDisplay.textContent = 'Ready to play!';
      gameStateDisplay.classList.add('state-ready');
      startButton.textContent = 'Start Game';
      startButton.disabled = false;
      pauseButton.disabled = true;
      resetButton.disabled = true;
      gameArea.style.cursor = 'default';
      break;
      
    case GAME_STATE.PLAYING:
      gameStateDisplay.textContent = 'Game in progress!';
      gameStateDisplay.classList.add('state-playing');
      startButton.textContent = 'Pause';
      startButton.disabled = false;
      pauseButton.disabled = false;
      resetButton.disabled = true; // Prevent accidental reset
      gameArea.style.cursor = 'crosshair';
      break;
      
    case GAME_STATE.PAUSED:
      gameStateDisplay.textContent = 'Game paused';
      gameStateDisplay.classList.add('state-paused');
      startButton.textContent = 'Resume';
      startButton.disabled = false;
      pauseButton.disabled = true;
      resetButton.disabled = false;
      gameArea.style.cursor = 'default';
      break;
      
    case GAME_STATE.GAME_OVER:
      gameStateDisplay.textContent = 'Game Over!';
      gameStateDisplay.classList.add('state-gameover');
      startButton.textContent = 'Play Again';
      startButton.disabled = true;
      pauseButton.disabled = true;
      resetButton.disabled = false;
      gameArea.style.cursor = 'default';
      
      // Display game over message
      const gameOverMsg = document.createElement('div');
      gameOverMsg.className = 'game-over-message';
      gameOverMsg.innerHTML = `
        <h2>Game Over!</h2>
        <p>Final Score: ${score}</p>
        <p>Click Reset to play again</p>
      `;
      gameArea.appendChild(gameOverMsg);
      break;
  }
}
TrashyMcTweak

Disabling buttons? BORING! I would've made them explode off the screen during game over, or at least add some sick animation effects. Why just disable a button when you can make it MELT?

TrashyMcTweak
AllyMcTweak
AllyMcTweak

Actually, visual feedback is important for user experience. Let's add some CSS transitions to make the state changes more noticeable:

CSS Transitions for Game States
/* CSS for game state transitions */
.game-button {
  transition: all 0.3s ease;
}

.game-button:disabled {
  opacity: 0.5;
  transform: scale(0.95);
}

.game-state {
  transition: all 0.5s ease;
}

.state-ready {
  background-color: rgba(1, 255, 170, 0.2);
  color: var(--neon-green);
}

.state-playing {
  background-color: rgba(24, 230, 255, 0.2);
  color: var(--neon-blue);
  animation: pulseBg 2s infinite;
}

.state-paused {
  background-color: rgba(255, 251, 150, 0.2);
  color: var(--neon-yellow);
}

.state-gameover {
  background-color: rgba(255, 82, 82, 0.2);
  color: var(--neon-pink);
}

Interactive Demo

Try out this mini-demo of our game state system. Click targets to score points, and use the controls to change states.

Score: 0
Time: 30s
Ready to play!

Activity: Game Reset Function

AshleyMcTweak
AshleyMcTweak

Now it's your turn! Complete the reset function below to properly handle game state, clear intervals, and reset variables. Remember to add a confirmation step to prevent accidental data loss.

Task: Complete the Reset Function

Fill in the missing parts of the resetGame() function:

// Variables
let score = 42;
let timeRemaining = 15;
let timerId = 123; // Simulated timer ID
let targetId = 456; // Simulated target creation timer ID
let currentState = 'playing';

// Reset function (incomplete)
function resetGame() {
  // 1. Add state check - only allow reset from certain states
  
  
  // 2. Add confirmation dialog if score > 0
  
  
  // 3. Reset game variables (score and timeRemaining)
  
  
  // 4. Clear any existing timers and reset their IDs
  
  
  // 5. Reset the game state to 'ready'
  
  
  // 6. Update displays
  console.log("Game has been reset");
}

Solution

Key Takeaways

GarbageMcTweak
GarbageMcTweak

Let's summarize what we've learned about game state management:

  • Define clear game states using constants (READY, PLAYING, PAUSED, GAME_OVER)
  • Control transitions between states to ensure logical flow
  • Update the UI to reflect the current game state
  • Implement safe reset functionality with confirmation
  • Enable/disable controls based on the current state
AllyMcTweak

With proper state management, your game will be more robust, provide better user experience, and be much easier to debug. Next, we'll look at how to display scores and track high scores!

AllyMcTweak

Final Challenge

TrashyMcTweak
TrashyMcTweak

Alright kids, time to prove you're not completely hopeless! Try adding one more state to the game: a COUNTDOWN state that occurs between READY and PLAYING. It should display "3...2...1...GO!" before the game actually starts.

Challenge: Add a Countdown State

Implement a new COUNTDOWN state that:

  • Shows a 3-2-1 countdown when starting a new game
  • Transitions automatically to PLAYING state when countdown ends
  • Updates the UI properly during countdown
  • Cannot be paused or reset during countdown

Hint

You'll need to add a new state constant, update the state transition function, and use setTimeout() for the countdown.

SnowzieMcTweak

*excited tail wagging* WOOF! *paws at keyboard excitedly*

SnowzieMcTweak
GrumpyMcTweak
GrumpyMcTweak

I think what Snowzie is trying to say is "COMMIT YOUR CODE PROPERLY OR FACE THE CONSEQUENCES!" I'm watching your git commits, people. Triple check your reset function before you push!

← Previous: Difficulty Home Next: Score Display →