UNPKG

ecspresso

Version:

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

141 lines (140 loc) 5.65 kB
/** * Physics 3D Plugin for ECSpresso * * Provides ECS-native 3D arcade physics: gravity, forces, drag, semi-implicit Euler * integration, and impulse-based collision response with friction. * * Reuses RigidBody and collider types from the 2D physics/collision plugins for * shape definitions. Has its own collision detection in fixedUpdate for physics * response; the existing collision3D plugin can still run in postUpdate for game * logic events. */ import type { SystemPhase } from 'ecspresso'; import type { Vector3D } from 'ecspresso'; import type { Transform3DWorldConfig } from '../spatial/transform3D'; import type { Collision3DComponentTypes, LayerFactories } from './collision3D'; import type { RigidBody, BodyType, RigidBodyOptions } from './physics2D'; export type { RigidBody, BodyType, RigidBodyOptions }; /** * Component types directly provided by the physics3D plugin. */ export interface Physics3DOwnComponentTypes { rigidBody3D: RigidBody; velocity3D: Vector3D; force3D: Vector3D; } /** * Full component types available when using the physics3D plugin * (own components + transform + collision dependencies). * Convenience alias for consumer code. */ export interface Physics3DComponentTypes<L extends string = never> extends Collision3DComponentTypes<L>, Physics3DOwnComponentTypes { } /** * Physics 3D configuration resource. */ export interface Physics3DConfig { gravity: Vector3D; } export interface Physics3DResourceTypes { physics3DConfig: Physics3DConfig; } /** * Event emitted for each physics 3D collision pair. * * Normal components are flattened (`normalX`/`normalY`/`normalZ`) rather than * nested in a `Vector3D` to avoid a per-event allocation in the physics hot path. */ export interface Physics3DCollisionEvent { entityA: number; entityB: number; /** Unit normal X, pointing from A toward B */ normalX: number; /** Unit normal Y, pointing from A toward B */ normalY: number; /** Unit normal Z, pointing from A toward B */ normalZ: number; /** Penetration depth (positive) */ depth: number; } export interface Physics3DEventTypes { physics3DCollision: Physics3DCollisionEvent; } export interface Physics3DPluginOptions<G extends string = 'physics3D', CG extends string = never> { /** World gravity vector (default: {x: 0, y: 0, z: 0}) */ gravity?: Vector3D; /** System group name (default: 'physics3D') */ systemGroup?: G; /** Additional group for the collision system only (default: none). * When set, the collision system belongs to both `systemGroup` and this group, * allowing independent enable/disable of collision detection. */ collisionSystemGroup?: CG; /** Priority for integration system (default: 1000) */ integrationPriority?: number; /** Priority for collision system (default: 900) */ collisionPriority?: number; /** Execution phase (default: 'fixedUpdate') */ phase?: SystemPhase; } /** * Create a rigidBody3D + force3D component pair. * Static bodies automatically get mass=Infinity. */ export declare function createRigidBody3D(type: BodyType, options?: RigidBodyOptions): { rigidBody3D: RigidBody; force3D: Vector3D; }; /** * Create a force3D component with initial values. */ export declare function createForce3D(x: number, y: number, z: number): { force3D: Vector3D; }; /** * Accumulate a force onto an entity's force3D component. */ export declare function applyForce3D(ecs: { getComponent(id: number, name: 'force3D'): Vector3D | undefined; }, entityId: number, fx: number, fy: number, fz: number): void; /** * Apply an instantaneous impulse: velocity3D += impulse / mass. */ export declare function applyImpulse3D(ecs: { getComponent(id: number, name: 'velocity3D'): Vector3D | undefined; getComponent(id: number, name: 'rigidBody3D'): RigidBody | undefined; }, entityId: number, ix: number, iy: number, iz: number): void; /** * Directly set an entity's velocity3D. */ export declare function setVelocity3D(ecs: { getComponent(id: number, name: 'velocity3D'): Vector3D | undefined; }, entityId: number, vx: number, vy: number, vz: number): void; /** * Create a 3D physics plugin for ECSpresso. * * Provides: * - Semi-implicit Euler integration (gravity, forces, drag → velocity3D → position) * - Impulse-based collision response with restitution and friction * - physics3DCollision events with contact normal and depth * * @example * ```typescript * const ecs = ECSpresso.create() * .withPlugin(createTransform3DPlugin()) * .withPlugin(createPhysics3DPlugin({ gravity: { x: 0, y: -9.81, z: 0 } })) * .withFixedTimestep(1/60) * .build(); * * ecs.spawn({ * ...createTransform3D(0, 10, 0), * ...createRigidBody3D('dynamic', { mass: 1, restitution: 0.5 }), * velocity3D: { x: 0, y: 0, z: 0 }, * ...createAABB3DCollider(1, 1, 1), * ...createCollisionLayer('player', ['ground']), * }); * ``` */ type Physics3DProvides<L extends string = never> = Physics3DOwnComponentTypes & Collision3DComponentTypes<L>; export declare function createPhysics3DPlugin<L extends string = never, G extends string = 'physics3D', CG extends string = never>(options?: Physics3DPluginOptions<G, CG> & { layers?: LayerFactories<Record<L, readonly string[]>>; }): import("ecspresso").Plugin<import("ecspresso").WithResources<import("ecspresso").WithEvents<import("ecspresso").WithComponents<import("ecspresso").EmptyConfig, Physics3DProvides<L>>, Physics3DEventTypes>, Physics3DResourceTypes>, Transform3DWorldConfig, "physics3D-integration" | "physics3D-collision", G | CG, never, never>;