v0.18.0
All notable changes to ECSpresso are documented here. Format loosely follows Keep a Changelog.
ScreenDefinition<Config> now defaults State to Config instead of Record<string, never>.
That means code like this may now fail typecheck:
type Screens = {
playing: ScreenDefinition<{ level: number }>;
};
const playing: Screens['playing'] = {
initialState: () => ({}),
};
Before, omitted State meant empty state. Now omitted State means state mirrors config, so initialState must return { level: number }.
- SystemProcessFn<Cfg, Queries, ResourceKeys?, Singletons?> names the .setProcess(...) callback shape.
- SystemLifecycleFn<Cfg> names the .setOnInitialize(...) / .setOnDetach(...) callback shape.
- AssetConfiguratorFn<Assets, AssetGroups?> names the .withAssets(...) callback shape.
- ScreenConfiguratorFn<Screens> names the .withScreens(...) callback shape.
WorldConfig helper types for plugin requirements: - ComponentsConfig<T>, EventsConfig<T>, ResourcesConfig<T>, AssetsConfig<T>, and ScreensConfig<T> build single-slot configs without spelling empty WorldConfigFrom parameters.
ecspresso/plugins/rendering/sprite-animation for PixiJS atlases: - spritesheetLoader<S>(url) — AssetConfigurator-compatible loader; lazy-loads pixi.js and runtime-shape-checks the resolved value so non-atlas URLs error at load time instead of deep in the play loop.
- clipFromSheet(sheet, name, options?) and animationSetFromSheet(id, sheet, options?) — build clips/sets from a loaded Spritesheet<S>. Animation-name union is inferred when S is declared as an interface ... extends SpritesheetData. Both throw on zero-frame animations.
- clipFromGrid({ source, frameWidth, frameHeight, columns, rows?, count?, indices?, ... }) — slice a grid-arranged image without atlas JSON. Async (lazy-loads pixi) and validates inputs: exactly one of rows/count/indices is required, indices are integer- and bounds-checked when rows is given.
- New exported types: SheetClipOverrides<A>, SheetAnimationKeys<S>.
No breaking changes from 0.16.3.
builder.disableChangeTracking() — opt out of change tracking entirely for worlds with zero changed: filters (e.g. benchmarks). Auto-subscription from plugins still grows the bitmap correctly if a plugin with a changed: filter is added afterwards.changed: declarations during _registerSystem and subscribes only the components something actually consumes. Unsubscribed components skip the mark walk entirely.postUpdate rebuild auto-skips in flat-hierarchy scenes when the fixedUpdate rebuild already ran (2D: −5.7%, 3D: −9.6% ms/frame in the physics bench).EventBus.publish switched from a conditional-tuple rest signature to two named overloads — empty fast-path is ~7× faster (5.7 → 0.8 ns/call); end-to-end bench −7–10% ms/frame.markChanged resolves component indices once and uses a flat Uint32Array for the per-component change generation._changedIdx and _mutatesIdx are pre-resolved at system registration, removing per-frame name→idx Map lookups.getComponent calls with split queries.bench/ecs-physics3D.bench.ts and shared mulberry32 PRNG with the 2D bench.