The one-way tolerance angle is a field: a one-way collider and a one-way tile gain One-way angle (0 to 90 degrees, default 45), a contact blocks when its normal is within that angle of the blocking side, without a value the limit is bit-identical to before, and the rule holds for Move, Raycast and dynamic bodies in Rapier; tiles with different angles never merge into one rectangle, the scene view draws the angle as a cone in the middle of the thick side, and the inspector shows Blocks from and One-way angle only while One-way is on, through the new schema option shownWith (#159)
game-maker / gate (push) Successful in 1m55s
game-maker / suite (push) Successful in 6m50s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5 committed 2026-09-19 13:13:29 +02:00
1 parent d4ea04d4d6
commit 5385d9e152
14 files changed
+454 -62

No files matched your search

+23 -11
View File
@@ -7,6 +7,7 @@ import {
cellPolygonShape,
type CellRect,
cellRectShape,
oneWayAngles,
solidPolygons,
solidRects,
} from '../../core/model/tilemapCollision';
@@ -19,7 +20,9 @@ import {
isTrigger,
landsOn,
movedShape,
oneWayCone,
oneWayFacing,
type OneWaySide,
overlapDepth,
shapeExtent,
shapesOverlap,
@@ -83,11 +86,11 @@ export const NOT_BLOCKED: MoveResult = Object.freeze({
/**
* A shape of a collider or tilemap, and for a one-way one the world direction it blocks from (#155):
* only a contact whose normal lies within 45° of it stops anything.
* only a contact whose normal lies within its angle of it stops anything (#159).
*/
export interface SolidShape {
shape: ColliderShape;
facing: Vec2 | null;
facing: OneWaySide | null;
}
/** A shape in the mover's way and the collider or tilemap it belongs to. */
@@ -167,10 +170,13 @@ export class Solids {
? {
rects: solidRects(project, props, asset),
polygons: solidPolygons(project, props, asset),
oneWayRects: solidRects(project, props, asset, true),
oneWayPolygons: solidPolygons(project, props, asset, true),
oneWay: oneWayAngles(project, asset).map((angle) => ({
angle,
rects: solidRects(project, props, asset, angle),
polygons: solidPolygons(project, props, asset, angle),
})),
}
: { rects: [], polygons: [], oneWayRects: [], oneWayPolygons: [] };
: { rects: [], polygons: [], oneWay: [] };
this.blocks.set(tilemap, blocks);
}
return blocks;
@@ -179,19 +185,25 @@ export class Solids {
/**
* 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), the one-way cells blocking from the map's
* top (#155). A tilemap placed before the first tick is worked out on the spot.
* top (#155) within their tile's angle (#159). A tilemap placed before the first tick is worked out
* on the spot.
*/
solidShapesOf(world: World, body: Entity): SolidShape[] {
const m = worldMatrixOf(body);
if (body.type !== 'tilemap')
return [{ shape: colliderShape(body.props, m), facing: oneWayFacing(body.props, m) }];
const { rects, polygons, oneWayRects, oneWayPolygons } = this.blocksOf(world, body);
const { rects, polygons, oneWay } = this.blocksOf(world, body);
const up = oneWayFacing({ oneWay: true }, m);
const shapes = (cells: CellRect[], outlines: CellPolygon[], facing: Vec2 | null) => [
const shapes = (cells: CellRect[], outlines: CellPolygon[], facing: OneWaySide | null) => [
...cells.map((rect) => ({ shape: cellRectShape(body.props, rect, m), facing })),
...outlines.map((polygon) => ({ shape: cellPolygonShape(body.props, polygon, m), facing })),
];
return [...shapes(rects, polygons, null), ...shapes(oneWayRects, oneWayPolygons, up)];
return [
...shapes(rects, polygons, null),
...oneWay.flatMap((group) =>
shapes(group.rects, group.polygons, up && { ...up, cone: oneWayCone(group.angle) }),
),
];
}
shapesOf(world: World, body: Entity): ColliderShape[] {
@@ -401,8 +413,8 @@ function unitOf(v: Vec2): Vec2 {
interface TilemapBlocks {
rects: CellRect[];
polygons: CellPolygon[];
oneWayRects: CellRect[];
oneWayPolygons: CellPolygon[];
/** The one-way cells, one group per angle their tiles land things within. */
oneWay: { angle: number; rects: CellRect[]; polygons: CellPolygon[] }[];
}
export function shapeOf(collider: Entity): ColliderShape {