Box2D physics engine implementation for NovaECS physics core. Provides deterministic 2D physics simulation using the industry-standard Box2D engine.
NovaECS物理核心的Box2D物理引擎实现。使用行业标准的Box2D引擎提供确定性2D物理模拟。
npm install @esengine/nova-ecs-physics-box2d
Required dependencies: 必需的依赖项:
npm install @esengine/nova-ecs @esengine/nova-ecs-math @esengine/nova-ecs-physics-core
For complete API documentation, visit: https://esengine.github.io/nove-ecs-physics-box2d/
完整的API文档请访问:https://esengine.github.io/nove-ecs-physics-box2d/
import { World } from '@esengine/nova-ecs';
import { FixedVector2, Fixed } from '@esengine/nova-ecs-math';
import {
RigidBodyComponent,
ColliderComponent,
PhysicsTransformComponent,
RigidBodyType,
ColliderType
} from '@esengine/nova-ecs-physics-core';
import { Box2DPhysicsPlugin } from '@esengine/nova-ecs-physics-box2d';
// Create world and install Box2D physics plugin
const world = new World();
const physicsPlugin = new Box2DPhysicsPlugin({
worldConfig: {
gravity: new FixedVector2(0, -9.81),
allowSleep: true,
velocityIterations: 8,
positionIterations: 3
},
fixedTimeStep: 1/60,
enableDebugRender: false
});
await world.plugins.install(physicsPlugin);
// Create a falling box
const box = world.createEntity();
box.addComponent(new PhysicsTransformComponent(
new FixedVector2(0, 10), // position
0, // rotation
new FixedVector2(1, 1) // scale
));
box.addComponent(new RigidBodyComponent(
RigidBodyType.Dynamic, // dynamic body
0.1, // linear damping
0.1, // angular damping
1.0 // gravity scale
));
box.addComponent(new ColliderComponent({
type: ColliderType.Box,
halfWidth: new Fixed(1),
halfHeight: new Fixed(1)
}, {
friction: new Fixed(0.3),
restitution: new Fixed(0.5),
density: new Fixed(1.0)
}));
// Create static ground
const ground = world.createEntity();
ground.addComponent(new PhysicsTransformComponent(
new FixedVector2(0, -5)
));
ground.addComponent(new RigidBodyComponent(RigidBodyType.Static));
ground.addComponent(new ColliderComponent({
type: ColliderType.Box,
halfWidth: new Fixed(10),
halfHeight: new Fixed(1)
}));
// Game loop
function gameLoop(deltaTime: number) {
world.update(deltaTime);
// Get box position for rendering
const transform = box.getComponent(PhysicsTransformComponent)!;
console.log(`Box position: ${transform.position.x}, ${transform.position.y}`);
}
setInterval(() => gameLoop(16), 16);
import { PhysicsMaterial } from '@esengine/nova-ecs-physics-core';
// Create bouncy material
const bouncyMaterial: PhysicsMaterial = {
friction: new Fixed(0.1),
restitution: new Fixed(0.9), // Very bouncy
density: new Fixed(0.5)
};
// Create ice material
const iceMaterial: PhysicsMaterial = {
friction: new Fixed(0.02), // Very slippery
restitution: new Fixed(0.1),
density: new Fixed(0.8)
};
entity.addComponent(new ColliderComponent(
{ type: ColliderType.Circle, radius: new Fixed(1) },
bouncyMaterial
));
import { CollisionEventComponent } from '@esengine/nova-ecs-physics-core';
const entity = world.createEntity();
// ... add other components
const collisionEvents = new CollisionEventComponent();
// Handle collision begin
collisionEvents.addCollisionBeginCallback((other) => {
console.log('Collision started with:', other);
});
// Handle collision end
collisionEvents.addCollisionEndCallback((other) => {
console.log('Collision ended with:', other);
});
entity.addComponent(collisionEvents);
import { RaycastInput } from '@esengine/nova-ecs-physics-core';
const physicsWorld = physicsPlugin.getWorldSystem()?.getPhysicsWorld();
if (physicsWorld) {
const raycast: RaycastInput = {
origin: new FixedVector2(0, 10),
direction: new FixedVector2(0, -1), // Downward
maxDistance: new Fixed(20)
};
const results = physicsWorld.raycast(raycast);
for (const result of results) {
if (result.hit) {
console.log(`Hit at: ${result.point?.x}, ${result.point?.y}`);
console.log(`Distance: ${result.distance?.toNumber()}`);
}
}
}
// Apply continuous force (like wind)
const rigidBody = entity.getComponent(RigidBodyComponent)!;
rigidBody.applyForce(new FixedVector2(10, 0)); // Rightward force
// Apply instant impulse (like explosion)
rigidBody.applyImpulse(new FixedVector2(0, 50)); // Upward impulse
// Apply force at specific point (creates rotation)
rigidBody.applyForceAtPoint(
new FixedVector2(10, 0), // force
new FixedVector2(0, 1) // point offset from center
);
import { CollisionFilter } from '@esengine/nova-ecs-physics-core';
// Define collision categories
const CATEGORY_PLAYER = 0x0001;
const CATEGORY_ENEMY = 0x0002;
const CATEGORY_WALL = 0x0004;
const CATEGORY_PICKUP = 0x0008;
// Player collides with enemies, walls, and pickups
const playerFilter: CollisionFilter = {
categoryBits: CATEGORY_PLAYER,
maskBits: CATEGORY_ENEMY | CATEGORY_WALL | CATEGORY_PICKUP
};
// Enemy collides with player and walls only
const enemyFilter: CollisionFilter = {
categoryBits: CATEGORY_ENEMY,
maskBits: CATEGORY_PLAYER | CATEGORY_WALL
};
entity.addComponent(new ColliderComponent(
{ type: ColliderType.Box, halfWidth: new Fixed(1), halfHeight: new Fixed(1) },
undefined, // Use default material
playerFilter
));
const physicsPlugin = new Box2DPhysicsPlugin({
worldConfig: {
gravity: new FixedVector2(0, -9.81),
allowSleep: true,
velocityIterations: 8, // Higher = more accurate but slower
positionIterations: 3, // Higher = more accurate but slower
timeStep: new Fixed(1/60)
},
fixedTimeStep: 1/60, // Physics time step
maxSubSteps: 10, // Maximum physics sub-steps per frame
enableDebugRender: false, // Enable debug visualization
autoCreateSystems: true, // Automatically create physics systems
enableCCD: true, // Continuous collision detection
enableWarmStarting: true, // Warm starting for better performance
enableSubStepping: false // Sub-stepping for better stability
});
Use appropriate iteration counts | 使用适当的迭代次数
Enable sleeping | 启用休眠
Use collision filtering | 使用碰撞过滤
Optimize collider shapes | 优化碰撞器形状
MIT