Skip to content

States & SubStates

Games in Flixel-Pixi are organized into scenes called States (FlxState). Modal menus, pause screens, and inventories are handled through layered SubStates (FlxSubState).


The FlxState Lifecycle

                      ┌───────────────┐
                      │  Constructor  │
                      └───────┬───────┘


                      ┌───────────────┐
                      │   create()    │ ◄── Initialize objects, groups, cameras
                      └───────┬───────┘

                ┌─────────────▼─────────────┐
                │                           │
                ▼                           ▼
        ┌───────────────┐           ┌───────────────┐
        │update(elapsed)│           │    draw()     │
        └───────┬───────┘           └───────────────┘

                ▼ (On switch state)
        ┌───────────────┐
        │   destroy()   │ ◄── Releases textures, groups, listeners
        └───────────────┘

Creating a State

ts
import { FlxButton, FlxSprite, FlxState, FlxText } from 'flixel-pixi';
import { PlayState } from './PlayState';

export class MenuState extends FlxState {
  override create(): void {
    super.create();

    const title = new FlxText(0, 140, 640, 'MY RETRO ADVENTURE', 28);
    title.alignment = 'center';
    this.add(title);

    const startBtn = new FlxButton(260, 260, 'START GAME', () => {
      this.switchState(new PlayState());
    });
    this.add(startBtn);
  }
}

Switching States

To transition to a new state:

ts
this.switchState(new NextState());

When switching states:

  1. The incoming state is instantiated and its create() method is invoked.
  2. The previous state's destroy() is called automatically, tearing down all children and cleaning up GPU render handles.

A FlxSubState runs on top of the current state. When active, it can optionally pause the parent state's simulation (update) while still drawing it in the background.

ts
import { FlxButton, FlxSubState, FlxText } from 'flixel-pixi';

export class PauseSubState extends FlxSubState {
  override create(): void {
    super.create();

    // Dark semi-transparent background overlay
    this.bgColor = 0x88000000;

    const pauseLabel = new FlxText(0, 180, 640, 'PAUSED', 24);
    pauseLabel.alignment = 'center';
    this.add(pauseLabel);

    const resumeBtn = new FlxButton(260, 260, 'RESUME', () => {
      this.close(); // Close substate and resume gameplay
    });
    this.add(resumeBtn);
  }
}

Opening & Closing a SubState

From your PlayState:

ts
// Open pause substate
if (this.context.input.keyboard.justPressed('ESCAPE')) {
  this.openSubState(new PauseSubState());
}

Best Practices

  • Always allocate game objects inside create(), not the constructor, so the runtime context (this.context) is fully bound.
  • Call super.create(), super.update(elapsed), and super.destroy() to ensure internal group processing and cleanup occur properly.

Released under the MIT License.