61 lines
2.2 KiB
TypeScript
61 lines
2.2 KiB
TypeScript
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);
|
||
});
|
||
});
|