Files
feuerwerk/tests/core/physics-engine.test.ts
T

61 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest';
import {
loadPhysics,
PIXELS_PER_METRE,
physicsStatus,
type Rapier,
toMetres,
toPixels,
} from '../../src/runtime/physics/engine';
const STEP = 1 / 60;
const GRAVITY = 980;
/** A 400 × 20 px floor whose top is at y = 290, and a 32 px crate dropped from y = 0. */
function crateOverFloor(rapier: Rapier) {
const world = new rapier.World({ x: 0, y: toMetres(GRAVITY) });
world.timestep = STEP;
const floor = world.createRigidBody(rapier.RigidBodyDesc.fixed().setTranslation(0, toMetres(300)));
world.createCollider(rapier.ColliderDesc.cuboid(toMetres(200), toMetres(10)), floor);
const crate = world.createRigidBody(rapier.RigidBodyDesc.dynamic().setTranslation(0, 0));
world.createCollider(rapier.ColliderDesc.cuboid(toMetres(16), toMetres(16)), crate);
const run = (steps: number) => {
for (let i = 0; i < steps; i++) world.step();
};
const y = () => toPixels(crate.translation().y);
return { world, crate, run, y };
}
describe('physics engine', () => {
it('loads once and reports the Rapier version', async () => {
const first = loadPhysics();
expect(loadPhysics()).toBe(first);
const rapier = await first;
expect(rapier.version()).toBe('0.20.0');
expect(physicsStatus()).toMatchObject({ state: 'ready', version: '0.20.0' });
});
it('converts at 100 px to the metre', () => {
expect(PIXELS_PER_METRE).toBe(100);
expect(toMetres(980)).toBe(9.8);
expect(toPixels(toMetres(123.5))).toBeCloseTo(123.5, 10);
});
it('lets a crate fall freely in pixels per second squared', async () => {
const { run, y, crate } = crateOverFloor(await loadPhysics());
run(30);
// ½ g t² = 122.5 px after half a second; the integrator lands about a pixel past it.
expect(Math.abs(y() - 0.5 * GRAVITY * 0.25)).toBeLessThan(1.5);
expect(toPixels(crate.linvel().y)).toBeCloseTo(GRAVITY * 0.5, 3);
});
it('keeps a crate resting on the floor', async () => {
const { run, y, crate } = crateOverFloor(await loadPhysics());
run(180);
expect(Math.abs(y() - 274)).toBeLessThan(0.5);
expect(Math.abs(toPixels(crate.linvel().y))).toBeLessThan(0.5);
run(120);
expect(Math.abs(y() - 274)).toBeLessThan(0.5);
});
});