Appearance
@carrot/engine-framework โ
ts
Entity-component game framework with scene management, pipeline execution, and decorator-driven configuration.
Installation โ
bash
npm install @carrot/engine-frameworkArchitecture / How It Works โ
The framework follows an entity-component pattern where Entity objects live in Scene containers and Behaviour subclasses provide all functionality.
Core Loop โ
Game owns the main loop. Each frame:
- Game systems tick (
GameSystem._update) - Pipeline stages dispatch in order:
Earliest->PreUpdate->Update->PostUpdate->PreRender->Render->PostRender->Final - The active scene's entities tick between
PreUpdateandUpdate
Entity-Behaviour Model โ
- Entity has a transform (
Transform2DorTransform3D), a list of behaviours, child entities, and tags. - Behaviour is the component. Lifecycle:
initialize(engine)(async, load assets) ->start()(first frame) ->update(delta)(every frame) ->dispose()(cleanup). - RenderBehaviour extends Behaviour for anything that submits draw calls (must expose
mesh,materialInstance,visible).
Scenes โ
Scene manages entity lifecycle. SceneManager handles switching - deactivating the old scene and activating the new one. Scenes support a preload() hook for asset loading before entity initialization.
Pipeline โ
GamePipeline provides ordered execution across named stages. Entries declare before/after dependencies for topological sorting within each stage.
Templates โ
EntityTemplate, BehaviourTemplate, and SceneTemplate are serializable descriptions for prefabs, save/load, and runtime spawning. BehaviourRegistry maps type-name strings to factory functions to instantiate behaviours from templates.
Decorators โ
@Requires(...types)- declares sibling behaviour dependencies@ExecutionOrder({ priority, before, after })- ordering constraints@Pipeline({ stage, before, after })- pipeline stage registration
Decorator metadata is stored in a WeakMap-based store (setMeta/getMeta).
Dependencies โ
| Package | Used For |
|---|---|
@carrot/engine | Core engine, canvas, render loop |
@carrot/signals | Event signals on Game, Entity, Scene, SceneManager, GameViewport |
@carrot/collections | (transitive) |
@carrot/maths-geometry | Transform2D, Transform3D, AspectRatio, Size2 |
@carrot/assets | AssetImage, AssetRegistry for asset resolution |
@carrot/logging | Optional Log interface |
@carrot/engine-renderer | (transitive - renderer access via Engine) |
@carrot/engine-renderer-graph | (transitive) |
@carrot/engine-materials | MaterialInstance used in RenderBehaviour |
@carrot/engine-meshes | Mesh used in RenderBehaviour |
Build โ
bash
npm run build # runs tscOutput goes to dist/. Package is ESM ("type": "module").
Using @carrot/engine-framework โ
Entity-component game framework with scene management, pipeline execution, and decorator-driven configuration.
Import โ
ts
import {
Game, GameViewport, Entity, Behaviour, Scene, SceneManager,
GameSystem, GamePipeline, PipelineStage, RenderBehaviour,
BehaviourRegistry, Requires, ExecutionOrder, Pipeline,
} from '@carrot/engine-framework';Common Patterns โ
1. Bootstrap a game โ
ts
const game = new Game({
canvas: document.getElementById('game') as HTMLCanvasElement,
assetBasePath: '/assets/',
});
const scene = new Scene('Menu');
await game.start(scene);2. Create entities with behaviours โ
ts
class PlayerController extends Behaviour {
protected override update(delta: number): void {
const input = this.engine!.input;
if (input.keyboard.getButton('KeyW')) {
this.entity.transform.localPosition.y += 100 * delta;
}
}
}
const player = Entity.create2d('Player');
player.addBehaviour(new PlayerController());
scene.addEntity(player);3. Scene switching โ
ts
class MenuScene extends Scene {
constructor() { super('Menu'); }
protected override onEnter(): void {
// set up menu entities
}
protected override onExit(): void {
// clean up
}
}
class GameScene extends Scene {
constructor() { super('Game'); }
protected override async preload(): Promise<void> {
// preload level assets
}
}
// Switch scenes
await game.scenes.switchTo(new GameScene());
// Listen for switches
game.scenes.onSwitch.add(({ previous, current }) => {
console.log(`Switched from ${previous?.name} to ${current.name}`);
});4. Game systems (scene-independent) โ
ts
class AudioManager extends GameSystem {
protected override initialize(engine: Engine): void {
// set up audio context
}
protected override update(delta: number): void {
// tick audio
}
}
game.addSystem(new AudioManager());
// Retrieve later
const audio = game.getSystem(AudioManager);5. Pipeline registration โ
ts
game.pipeline.register({
fn: (delta) => physics.update(delta),
stage: PipelineStage.Update,
name: 'physics',
before: ['rendering'],
});
game.pipeline.register({
fn: (delta) => renderer.draw(delta),
stage: PipelineStage.Render,
name: 'rendering',
after: ['physics'],
});6. Decorators โ
ts
@Requires(SpriteRenderer)
@ExecutionOrder({ after: [InputSystem] })
@Pipeline({ stage: PipelineStage.Update })
class PlayerController extends Behaviour {
protected override start(): void {
const renderer = this.requireSibling(SpriteRenderer); // guaranteed by @Requires
}
}7. Entity hierarchy and tags โ
ts
const parent = Entity.create2d('Ship');
const turret = Entity.create2d('Turret');
parent.addChild(turret);
parent.addTag('player');
const players = scene.findEntitiesByTag('player');8. Behaviour registry and templates โ
ts
game.behaviours.register('playerController', () => new PlayerController());
const prefab: EntityTemplate = {
name: 'Bullet',
transform: { type: '2d', x: 0, y: 0 },
behaviours: [
{ type: 'sprite', properties: { image: 'bullet.png' } },
{ type: 'playerController', properties: {} },
],
};9. Viewport, aspect ratio, and display info โ
ts
import { AspectRatio } from '@carrot/maths-geometry';
const game = new Game({
canvas,
aspectRatio: new AspectRatio(16, 9),
});
// The viewport produces a DisplayInfo (not just a Size2)
const display = game.viewport.display;
console.log(`Logical: ${display.logicalSize.width}x${display.logicalSize.height}`);
console.log(`Physical: ${display.physicalSize.width}x${display.physicalSize.height}`);
// viewport.size still works - delegates to display.logicalSize
const size = game.viewport.size;
// Resize events carry previous and current DisplayInfo
game.viewport.onResize.add(({ previous, current }) => {
console.log(`Resized: ${current.logicalSize.width}x${current.logicalSize.height}`);
});Package Contents โ
@carrot/engine-framework-sprites โ
Import: import { ... } from '@carrot/engine-framework-sprites';
Classes โ
| Class | Extends | Description |
|---|---|---|
Sprite | Behaviour | Loads image, creates texture + material + quad mesh |
SpriteAnimated | Sprite | Frame-based animation support (stub) |
SpritePacked | SpriteAnimated | Texture atlas/packing support (stub) |
SpriteRenderer | RenderBehaviour | Reads mesh/material from a Sprite sibling, exposes to render pipeline |
Functions โ
| Function | Description |
|---|---|
registerSpriteBehaviours(registry) | Registers all sprite behaviour factories on a BehaviourRegistry |
Sprite Members โ
| Member | Type | Description |
|---|---|---|
image | AssetImage | undefined | Loaded image asset |
texture | Texture2D | undefined | GPU texture |
material | Material2DSprite | undefined | Sprite material |
mesh | Mesh | undefined | Quad mesh sized to image (or overridden size) |
tint | ColorRgbLike | Tint colour (get/set) - delegates to material instance |
mainTexture | Texture2D | Main texture (get/set) - delegates to material instance |
spriteSize | Size2 | Current sprite dimensions (getter) |
materialInstance | MaterialInstance<Material2DSprite> | Typed material instance (getter) |
onImageLoaded | Signal<AssetImage> | Fires when image loads successfully |
onImageFailed | Signal<Error> | Fires on image load failure |
Constructor: new Sprite(source: string | AssetImage, size?: Size2Like) - optional size overrides the mesh dimensions instead of using the image's natural size.
SpriteRenderer Members โ
| Member | Type | Description |
|---|---|---|
mesh | Mesh | undefined | Mesh from sibling Sprite |
materialInstance | MaterialInstance | undefined | Material instance from sibling Sprite |
visible | boolean | True when both mesh and material are available |
submit() | method | Submit a DrawCall to the renderer's draw list |
draw(camera) | method | Immediate-mode rendering - binds material, submits, and draws in one call |
Registered Behaviour Types โ
'sprite' ยท 'spriteRenderer' ยท 'spriteAnimated' ยท 'spritePacked'
@carrot/engine-framework-physics โ
Import: import { ... } from '@carrot/engine-framework-physics';
Classes โ
| Class | Description |
|---|---|
NewtonSystem | Pooled particle physics GameSystem with gravity and air friction |
NewtonParticle | Individual particle - position, velocity, rotation, lifetime. Implements PoolItem |
NewtonParticle Properties โ
position (Vector2) ยท velocity (Vector2) ยท rotation (number) ยท angularVelocity (number) ยท lifetime (number) ยท age (number) ยท maxAge (number) ยท isAlive (boolean)
NewtonSystem Members โ
| Member | Type | Description |
|---|---|---|
gravity | number | Y-axis gravity (default: -0.981) |
airFriction | number | Velocity damping per frame (default: 0.98) |
particles | NewtonParticle[] | Current particle list (snapshot) |
emit(count) | method | Spawn particles from the pool |
@carrot/engine-framework-flexui โ
Import: import { ... } from '@carrot/engine-framework-flexui';
This package is currently a stub. No exports are defined yet.
Planned Exports โ
The following are planned but not yet implemented:
- FlexNode tree (layout nodes with flex properties)
- Layout engine (measure, arrange, render)
- Text rendering (SDF-based)
- UI event system (hit testing, focus, input routing)
- Style system (CSS-like properties, cascading)
- UIOverlayPass integration via RenderFeature
@carrot/engine-framework-cameras โ
Import: import { ... } from '@carrot/engine-framework-cameras';
Classes โ
| Class | Extends | Description |
|---|---|---|
CameraBehaviour | Behaviour | Wraps a low-level Camera with projection factories, entity transform sync, and auto-resize |
Types โ
| Type | Description |
|---|---|
CameraProjectionMode | 'ortho2d' ยท 'orthographic' ยท 'perspective' ยท 'custom' |
OrthographicBounds | { left, right, bottom, top, near, far } |
PerspectiveConfig | { fov, aspect, near, far } |
Functions โ
| Function | Description |
|---|---|
registerCameraBehaviours(registry) | Registers camera behaviour factories on a BehaviourRegistry |
CameraBehaviour Static Factories โ
| Factory | Description |
|---|---|
CameraBehaviour.ortho2d() | 2D orthographic camera - auto-rebuilds projection on engine resize |
CameraBehaviour.perspective(config) | Perspective camera from a PerspectiveConfig |
CameraBehaviour.orthographic(bounds) | Orthographic camera from an OrthographicBounds |
CameraBehaviour.custom(matrix) | Camera with a manually provided projection matrix |
CameraBehaviour Members โ
| Member | Type | Description |
|---|---|---|
camera | Camera | The underlying low-level Camera (getter) |
projectionMode | CameraProjectionMode | Current projection mode (getter) |
clearFlags | ClearFlags | What to clear before rendering (get/set) |
clearColor | ColorRgbLike | Clear colour (get/set) |
renderOrder | number | Render order - lower renders first (get/set) |
layerMask | number | Layer bitmask filter (get/set) |
target | RenderTexture | null | Render target - null = default framebuffer (get/set) |
CameraBehaviour Runtime Setters โ
| Method | Description |
|---|---|
setOrtho2d() | Switch to auto-resizing 2D orthographic projection |
setOrthographic(bounds) | Switch to orthographic with explicit bounds |
setPerspective(config) | Switch to perspective projection |
setCustomProjection(matrix) | Switch to a manually provided projection matrix |