Almost any game code has functions that mustn’t fire every tick. A shoot routine that lets at most one shot through every three frames. A key handler that doesn’t trigger ten times a second just because the key’s been held. A logger that writes once per second instead of 60 times.

My first instinct was to handle this with globals. It works, but every sub drags its little bit of state through the global namespace, and clashes are a matter of time. STATIC is the cleaner answer.

How STATIC behaves

STATIC declares a variable inside a sub or function that keeps its value across calls. On the first call it’s initialised to 0 (or whatever default you set), and from then on it persists between calls, without ever leaking outside the sub.

SUB FireShot()
  STATIC INTEGER cooldown

  IF cooldown > 0 THEN
    INC cooldown, -1
    EXIT SUB
  END IF

  ' ... fire the shot ...

  cooldown = 3
END SUB

Three frames of cooldown, all in one sub, no global echo, no init at program start. The first time the sub runs, cooldown is already 0, and from then on it lives where it belongs.

Key debouncing

Classic case: a key that flips a toggle shouldn’t flip again every frame while you hold it. Same pattern, but the state we track is “was it held last frame”:

SUB CheckMuteKey()
  STATIC INTEGER prev_held

  IF IsKeyPressed(KEY_M) THEN
    IF NOT prev_held THEN
      ToggleMute()
    END IF
    prev_held = 1
  ELSE
    prev_held = 0
  END IF
END SUB

prev_held remembers whether the key was down last frame. The toggle only fires on the edge from “not pressed” to “pressed”. Clean edge detection, no global helper.

Rate-limiting logging or audio

When something should happen once per second instead of 60 times per frame, a frame counter does the job:

SUB LogPerformance()
  STATIC INTEGER tick

  INC tick
  IF tick < 60 THEN EXIT SUB
  tick = 0

  PRINT "FPS: " + STR$(actual_fps)
END SUB

The same shape works for rhythmic audio cues or periodic HUD updates. The narrow scope keeps the whole thing easy to reason about — if something’s off, I know exactly where the state lives.

Three or four subs with their own little cooldowns and the difference from the global version becomes obvious. My code is noticeably tidier since I switched, and each sub carries its own state instead of fishing it out of a shared pool.