ecspresso
Version:
A minimal Entity-Component-System library for typescript and javascript.
128 lines (127 loc) • 3.85 kB
TypeScript
/**
* Camera / Viewport Plugin for ECSpresso
*
* Provides a declarative camera with world/screen coordinate conversion, smooth follow,
* trauma-based shake, bounds clamping, cursor-centered zoom, and logical viewport dimensions.
*
* This plugin is renderer-agnostic. PixiJS or other renderer integration (applying
* cameraState to a container/stage transform) is the consumer's responsibility.
*
* Camera uses its own x/y/zoom/rotation rather than localTransform/worldTransform.
* It reads the target entity's worldTransform for follow, but doesn't participate
* in the transform hierarchy itself.
*/
import type { SystemPhase } from 'ecspresso';
import type { TransformWorldConfig } from './transform';
export interface Camera {
x: number;
y: number;
zoom: number;
rotation: number;
}
export interface CameraFollow {
target: number;
smoothing: number;
deadzoneX: number;
deadzoneY: number;
offsetX: number;
offsetY: number;
}
export interface CameraShake {
trauma: number;
traumaDecay: number;
maxOffsetX: number;
maxOffsetY: number;
maxRotation: number;
}
export interface CameraBounds {
minX: number;
minY: number;
maxX: number;
maxY: number;
}
export interface CameraComponentTypes {
camera: Camera;
cameraFollow: CameraFollow;
cameraShake: CameraShake;
cameraBounds: CameraBounds;
}
export interface FollowOptions {
smoothing?: number;
deadzoneX?: number;
deadzoneY?: number;
offsetX?: number;
offsetY?: number;
}
export type EntityHandle = {
id: number;
};
export interface CameraState {
x: number;
y: number;
zoom: number;
rotation: number;
shakeOffsetX: number;
shakeOffsetY: number;
shakeRotation: number;
viewportWidth: number;
viewportHeight: number;
entityId: number;
follow(target: number | EntityHandle, options?: FollowOptions): void;
unfollow(): void;
setPosition(x: number, y: number): void;
setZoom(zoom: number): void;
setRotation(rotation: number): void;
setBounds(minX: number, minY: number, maxX: number, maxY: number): void;
clearBounds(): void;
addTrauma(amount: number): void;
}
export interface CameraResourceTypes {
cameraState: CameraState;
}
export interface CameraPluginOptions<G extends string = 'camera'> {
viewportWidth?: number;
viewportHeight?: number;
initial?: {
x?: number;
y?: number;
zoom?: number;
rotation?: number;
};
follow?: FollowOptions;
shake?: boolean | Partial<Omit<CameraShake, 'trauma'>>;
bounds?: {
minX: number;
minY: number;
maxX: number;
maxY: number;
} | [number, number, number, number];
zoom?: {
zoomStep?: number;
minZoom?: number;
maxZoom?: number;
};
pan?: {
speed: number;
actions?: {
up?: string;
down?: string;
left?: string;
right?: string;
};
};
systemGroup?: G;
phase?: SystemPhase;
randomFn?: () => number;
}
export declare function worldToScreen(worldX: number, worldY: number, state: CameraState): {
x: number;
y: number;
};
export declare function screenToWorld(screenX: number, screenY: number, state: CameraState): {
x: number;
y: number;
};
type CameraLabels = 'camera-init' | 'camera-follow' | 'camera-shake-update' | 'camera-bounds' | 'camera-state-sync' | 'camera-zoom' | 'camera-pan';
export declare function createCameraPlugin<G extends string = 'camera'>(options?: CameraPluginOptions<G>): import("ecspresso").Plugin<import("ecspresso").WithResources<import("ecspresso").WithComponents<import("ecspresso").EmptyConfig, CameraComponentTypes>, CameraResourceTypes>, TransformWorldConfig, CameraLabels, G, never, never>;
export {};