ecspresso
    Preparing search index...

    Screen Management

    Manage game states/screens with transitions and overlay support:

    import type { ScreenDefinition } from 'ecspresso';

    type Screens = {
    menu: ScreenDefinition<
    Record<string, never>, // Config (passed when entering)
    { selectedOption: number } // State (mutable during screen)
    >;
    gameplay: ScreenDefinition<
    { difficulty: string; level: number },
    { score: number; isPaused: boolean }
    >;
    pause: ScreenDefinition<Record<string, never>, Record<string, never>>;
    };

    const game = ECSpresso.create()
    .withComponentTypes<Components>()
    .withEventTypes<Events>()
    .withResourceTypes<Resources>()
    .withScreens(screens => screens
    .add('menu', {
    initialState: () => ({ selectedOption: 0 }),
    onEnter: () => console.log('Entered menu'),
    onExit: () => console.log('Left menu'),
    })
    .add('gameplay', {
    initialState: () => ({ score: 0, isPaused: false }),
    onEnter: (config) => console.log(`Starting level ${config.level}`),
    onExit: () => console.log('Gameplay ended'),
    requiredAssetGroups: ['level1'],
    })
    .add('pause', {
    initialState: () => ({}),
    })
    )
    .build();

    await game.initialize();
    await game.setScreen('menu', {}); // Set initial screen
    await game.setScreen('gameplay', { difficulty: 'hard', level: 1 }); // Transition
    await game.pushScreen('pause', {}); // Push overlay
    await game.popScreen(); // Pop overlay

    const current = game.getCurrentScreen(); // 'gameplay'
    const config = game.getScreenConfig(); // { difficulty: 'hard', level: 1 }
    const state = game.getScreenState(); // { score: 0, isPaused: false }
    game.updateScreenState({ score: 100 });

    Use ScreenConfiguratorFn when a .withScreens(...) callback is extracted into a named helper:

    import type { ScreenConfiguratorFn, ScreenDefinition } from 'ecspresso';

    type AppScreens = {
    playing: ScreenDefinition<{ level: number }>;
    pause: ScreenDefinition;
    };

    export const configureScreens: ScreenConfiguratorFn<AppScreens> = function configureScreens(screens) {
    return screens
    .add('playing', { initialState: config => config })
    .add('pause', { initialState: () => ({}) });
    };

    const game = ECSpresso.create()
    .withScreens(configureScreens)
    .build();

    Subscribe to a specific screen entering or exiting without writing inline screenEnter / screenExit event guards. Multiple handlers can be registered for the same screen and fire in registration order. Each returns a disposer.

    const offEnter = game.onScreenEnter('gameplay', ({ config, ecs }) => {
    // Fires on setScreen('gameplay', ...) and pushScreen('gameplay', ...)
    console.log(`Starting level ${config.level}`);
    ecs.spawn({ player: true });
    });

    const offExit = game.onScreenExit('gameplay', ({ ecs }) => {
    // Fires when leaving 'gameplay' via setScreen away or popScreen
    console.log('Gameplay ended');
    });

    // Later, if needed:
    offEnter();
    offExit();

    Pass { scope: screenName } to spawn or spawnChild to have the entity automatically removed when that screen exits. This replaces hand-maintained per-component teardown lists.

    await game.setScreen('gameplay', { level: 1 });

    // Removed automatically when 'gameplay' exits
    game.spawn({ enemy: { hp: 10 } }, { scope: 'gameplay' });
    game.spawnChild(parentId, { projectile: { speed: 5 } }, { scope: 'gameplay' });

    The screen name is type-checked against your declared screens.

    game.addSystem('menuUI')
    .inScreens(['menu']) // Only runs in 'menu'
    .setProcess(({ ecs }) => {
    renderMenu(ecs.getScreenState().selectedOption);
    });

    game.addSystem('animations')
    .excludeScreens(['pause']) // Runs in all screens except 'pause'
    .setProcess(() => { /* ... */ });

    Access screen state through the $screen resource:

    game.addSystem('ui')
    .setProcess(({ ecs }) => {
    const screen = ecs.getResource('$screen');
    screen.current; // Current screen name
    screen.config; // Current screen config
    screen.state; // Current screen state (mutable)
    screen.isOverlay; // true if screen was pushed
    screen.stackDepth; // Number of screens in stack
    screen.isCurrent('gameplay'); // Check current screen
    screen.isActive('menu'); // true if in current or stack
    });