# `@combos-fun/plugin-matterjs` — Agent notes

2D physics for Combos Fun. Wraps `matter-js` ^0.20 and exposes a `PhysicsSystem` plus a `Physics` Component that auto-syncs body position / rotation to a sibling display object's `Transform` each frame.

## When to read

Read for any 2D physics task: gravity, collisions, kinematic / static bodies, mouse constraints, rigid-body alignment with sprites.

## Public API

```ts
import {
  PhysicsSystem,
  Physics,
  PhysicsType,
  type PhysicsSystemParams,
} from '@combos-fun/plugin-matterjs';
```

### `PhysicsType`

`RECTANGLE`, `CIRCLE`, `POLYGON`.

### `Physics` Component params

| Field | Type | Notes |
|-------|------|-------|
| `type` | `PhysicsType?` | Body shape |
| `bodyOptions` | `object?` | Matter Body options: `isStatic`, `restitution`, `frictionAir`, `density`, `friction`, ... |
| `position` | `{x, y}?` | Initial offset |
| `sides` / `radius` | `number?` | Polygon / circle |
| `stopRotation` | `boolean?` | Lock rotation sync to render transform |

Runtime fields:

- `body: Matter.Body` — set by `PhysicsSystem` after the GameObject enters the scene.

Events on the `Physics` instance:

- `'collisionStart'` — `(otherGameObject, selfGameObject) => void`
- `'collisionActive'` — same signature
- `'collisionEnd'` — same signature

### `PhysicsSystemParams`

| Field | Type | Notes |
|-------|------|-------|
| `world` | `DeepPartial<IWorldDefinition>` | **Required**. Must include `gravity: { x, y, scale }` (e.g. `{ x: 0, y: 1, scale: 0.001 }`) |
| `resolution` | `number?` | **Must match `RendererSystem.resolution`** to keep bodies aligned with sprites |
| `fps` | `number?` | Step rate |
| `isTest` | `boolean?` | Adds a Pixi debug overlay drawn from Matter renderer |
| `mouse` | `{ open, constraint? }?` | Optional Matter mouse constraint |
| `element` / `canvas` | optional | Debug renderer mount points |

## Required setup

- Add `RendererSystem` (from `plugin-renderer`) **before** `PhysicsSystem`.
- Match `resolution` between renderer and physics.
- Use `Transform.origin: { x: 0.5, y: 0.5 }` so the body's center aligns
  with the sprite center.
- Bodies are created lazily when the `GameObject` is added to a scene.

## Runtime behaviour

- Each frame, `PhysicsSystem` steps the Matter `Engine` and then writes the
  body's `position` / `rotation` back into the sibling `Transform`.
- Pause / resume: `PhysicsSystem` automatically stops / starts the Matter
  runner when `Game.pause()` / `Game.resume()` is called.

## Common pitfalls

| Symptom | Fix |
|---------|-----|
| `gravity` complaint at runtime | Provide `world: { gravity: { x: 0, y: 1, scale: 0.001 } }` (`scale` is required) |
| Bodies misaligned with sprites | `PhysicsSystem.resolution` must match `RendererSystem.resolution` |
| Rotation drift | Use `stopRotation: true` on `Physics` if the visual must stay axis-aligned |
| Bodies pile up at origin | Set `position: { x, y }` on `Physics` constructor params |
| Cannot tap through bodies | Disable mouse constraint or `pointer-events: none` on the canvas overlay |

## Minimal example

```ts
import { Game, GameObject } from '@combos-fun/engine';
import { RendererSystem } from '@combos-fun/plugin-renderer';
import { GraphicsSystem, Graphics } from '@combos-fun/plugin-renderer-graphics';
import { PhysicsSystem, Physics, PhysicsType } from '@combos-fun/plugin-matterjs';

const game = new Game({
  systems: [
    new RendererSystem({ canvas, width: 750, height: 1334 }),
    new GraphicsSystem(),
    new PhysicsSystem({ world: { gravity: { x: 0, y: 1, scale: 0.001 } } }),
  ],
});

const ball = new GameObject('ball', { position: { x: 375, y: 100 }, origin: { x: 0.5, y: 0.5 } });
ball.addComponent(new Graphics(/* draw a circle */));
ball.addComponent(new Physics({ type: PhysicsType.CIRCLE, radius: 25 }));
```

## Verification

- `pnpm --filter @combos-fun/plugin-matterjs run build`
- Run a 2D example with physics; bodies should fall under gravity, collide,
  and stay aligned with sprites.
