Physics stage 2: colliders gain the shapes capsule and polygon, a polygon keeps its corners in the property points, turns, scales and mirrors with its object and is edited in the scene view (drag a corner, click an edge to insert one, double-click to delete, at least three stay, each step one undo step), a concave polygon is split into convex parts, a self-intersecting one is drawn red and collides with nothing, Rapier gets a capsule and one convex collider per part on the same body, and Move and the editor run use our own f64 geometry for the new shapes, where on a tie between two normals the one that blocks the move least wins, so a box no longer stops at the foot of a slope (#151); first version of a collision outline per texture frame, edited in the texture tab with the new Collision toggle, which a plain tile blocks with instead of its rectangle (#141, to be reworked)
game-maker / gate (push) Successful in 1m52s
game-maker / suite (push) Successful in 6m41s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5 committed 2026-09-18 22:56:25 +02:00
1 parent 1b5e386b60
commit 32057a96fc
25 files changed
+2341 -122

No files matched your search

+33 -16
View File
@@ -2,7 +2,14 @@ import { isHandleEnabled, worldMatrixOf } from '../../core/graph';
import { applyVector, determinant, IDENTITY, invert, linearOf, type Mat } from '../../core/model/matrix';
import type { Vec2 } from '../../core/model/Properties';
import { findAssetOf } from '../../core/model/Project';
import { type CellRect, cellRectShape, solidRects } from '../../core/model/tilemapCollision';
import {
type CellPolygon,
cellPolygonShape,
type CellRect,
cellRectShape,
solidPolygons,
solidRects,
} from '../../core/model/tilemapCollision';
import { parentChain } from '../../core/model/walk';
import {
type ColliderShape,
@@ -97,35 +104,45 @@ export interface CollisionSource {
*/
export class Solids {
/**
* The blocking rectangles of each tilemap, in cells. Worked out once per tick – painting, or a
* behavior changing the map, is picked up by the next one – and turned into world shapes only
* when asked, since the map may move in between.
* The blocking rectangles and outlined cells of each tilemap, in cells. Worked out once per tick –
* painting, or a behavior changing the map, is picked up by the next one – and turned into world
* shapes only when asked, since the map may move in between.
*/
private rects = new Map<Entity, CellRect[]>();
private blocks = new Map<Entity, { rects: CellRect[]; polygons: CellPolygon[] }>();
/** Forgets the rectangles, so the next tick works them out afresh. */
/** Forgets the cells, so the next tick works them out afresh. */
reset(): void {
this.rects = new Map();
this.blocks = new Map();
}
rectsOf(world: World, tilemap: Entity): CellRect[] {
let rects = this.rects.get(tilemap);
if (!rects) {
private blocksOf(world: World, tilemap: Entity): { rects: CellRect[]; polygons: CellPolygon[] } {
let blocks = this.blocks.get(tilemap);
if (!blocks) {
const asset = findAssetOf(world.project, tilemap.assetId, 'tilemap');
rects = asset ? solidRects(world.project, tilemap.props, asset) : [];
this.rects.set(tilemap, rects);
blocks = asset
? {
rects: solidRects(world.project, tilemap.props, asset),
polygons: solidPolygons(world.project, tilemap.props, asset),
}
: { rects: [], polygons: [] };
this.blocks.set(tilemap, blocks);
}
return rects;
return blocks;
}
/**
* The shapes a body occupies in the world: one for a collider, one box per merged rectangle
* for a tilemap. A tilemap placed before the first tick is worked out on the spot.
* The shapes a body occupies in the world: one for a collider; for a tilemap one box per merged
* rectangle and one polygon per outlined cell (#141). A tilemap placed before the first tick is
* worked out on the spot.
*/
shapesOf(world: World, body: Entity): ColliderShape[] {
if (body.type !== 'tilemap') return [shapeOf(body)];
const m = worldMatrixOf(body);
return this.rectsOf(world, body).map((rect) => cellRectShape(body.props, rect, m));
const { rects, polygons } = this.blocksOf(world, body);
return [
...rects.map((rect) => cellRectShape(body.props, rect, m)),
...polygons.map((polygon) => cellPolygonShape(body.props, polygon, m)),
];
}
/**