UNPKG

ecspresso

Version:

A minimal Entity-Component-System library for typescript and javascript.

128 lines (127 loc) 3.85 kB
/** * 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 {};