10. 【JavaScript】 Structuring an SVG Shooter with FSM

— An Intermediate Design to Keep DOM Games from Breaking

tags: [“JavaScript”, “SVG”, “FSM”, “GameDev”]


🎮 Structuring an SVG Shooter with FSM

— From “It Runs” to “It Can Grow Without Breaking” —

⚠️ This article is NOT about a finished game.

Let’s clarify the position of this article first:

👉
If you just want to play the game, jump straight to 12.
This article is a design note for people who are building the game.


What We Did in 09

In 09, we built a shooting game without Canvas, using only:

We implemented:

👉 We confirmed that the game works.


But Something Feels Wrong When You Keep Adding Features

As you extend the game, the code starts filling up with things like:


⚠️ The Limitations of 09

👉 The cause is very clear:

The game’s “state”
is not explicitly represented in the code.


🧠 Solution: FSM (Finite State Machine)

What we do in this article is simple:

Represent the current game state
with a single variable.

No complicated theory is required.


🧩 State Definition

const State = {
  INIT: "init",         // initialization
  READY: "ready",       // waiting to start
  PLAY: "play",         // playing
  GAME_OVER: "game_over"
};

let currentState = State.INIT;

👉 The current state of the game is always managed here.


🔄 Centralize State Transitions

function changeState(next) {
  console.log(`STATE: ${currentState}${next}`);
  currentState = next;
}

👉 Just looking at the logs tells you how the game flows.


🎮 Restructuring the Main Loop Around FSM

In 09, conditionals were scattered everywhere.
With FSM, the main loop becomes this:

function gameLoop() {
  switch (currentState) {

    case State.INIT:
      initGame();
      changeState(State.READY);
      break;

    case State.READY:
      // waiting for start input
      break;

    case State.PLAY:
      updatePlayer();
      updateEnemies();
      updateBullets();
      checkCollision();
      break;

    case State.GAME_OVER:
      // waiting for restart
      break;
  }

  requestAnimationFrame(gameLoop);
}

✨ You can instantly see what kind of game this is right now
✨ You escape from if-statement hell


⌨️ Input Handling Changes Meaning by State

document.addEventListener("keydown", e => {

  if (currentState === State.READY && e.code === "Space") {
    changeState(State.PLAY);   // start
  }

  if (currentState === State.GAME_OVER && e.code === "KeyR") {
    changeState(State.INIT);   // restart
  }
});

👉 Input becomes a trigger for state transitions
👉 Redundant flag management disappears


🧠 SVG (DOM) and FSM Work Well Together

👉 Easier to explain design than Canvas-based games
👉 Very suitable for teaching and learning


📐 Overall Structure at This Stage

Game FSM
 ├─ INIT
 ├─ READY
 ├─ PLAY
 └─ GAME_OVER
      │
      └─ SVG (DOM)
           ├─ player
           ├─ bullets
           └─ enemies

✨ What You Gain at This Point

But:


🔮 What Comes Next (11)


🎯 Honest Summary

FSM is not the goal.
It is simply a tool for continuing development without breaking the game.