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

+10 -8
View File
@@ -16,6 +16,7 @@ import {
contactBetween,
isTrigger,
landsOn,
type OneWaySide,
movedShape,
piecesOf,
shapeArea,
@@ -100,7 +101,7 @@ interface Body {
interface Part {
entity: Entity;
shape: ColliderShape;
facing: Vec2 | null;
facing: OneWaySide | null;
owner: Body;
}
@@ -497,7 +498,7 @@ export class PhysicsSystem implements System, CollisionSource {
local.map(({ entity, pieces, facing }) => [
entity.id,
pieces.map(pieceKey),
facing && [round(facing.x), round(facing.y)],
facing && [round(facing.x), round(facing.y), facing.cone],
entity.type === 'collider' ? [isTrigger(entity.props), colliderMaterial(entity.props)] : 0,
]),
) + JSON.stringify(want.settings?.mass ?? 0);
@@ -599,10 +600,11 @@ export class PhysicsSystem implements System, CollisionSource {
/**
* Whether each dynamic body near a one-way collider passes through it in the coming step (#155).
* It lands when it is on the blocking side (or sunk in no deeper than a landing sinks), the contact
* faces that side within 45° and it is not moving away from it; a body that landed is held as long
* as the two overlap, however deep a fast fall took it in one step. Otherwise it passes – from
* below, from the side, from inside – and so does a body told to ignore one-way colliders. One that
* is to pass is woken: a resting body sleeps, and a new answer alone would not move it.
* faces that side within the collider's angle (#159) and it is not moving away from it; a body
* that landed is held as long as the two overlap, however deep a fast fall took it in one step.
* Otherwise it passes – from below, from the side, from inside – and so does a body told to ignore
* one-way colliders. One that is to pass is woken: a resting body sleeps, and a new answer alone
* would not move it.
*/
private decideOneWay(world: World): void {
const platforms: [number, Part][] = [];
@@ -657,13 +659,13 @@ export class PhysicsSystem implements System, CollisionSource {
return movedShape(part.shape, move, turn);
}
private facingNow(part: Part): Vec2 {
private facingNow(part: Part): OneWaySide {
const facing = part.facing!;
if (!part.owner.body.isDynamic()) return facing;
const turn = part.owner.body.rotation() - part.owner.placed.angle;
const cos = Math.cos(turn);
const sin = Math.sin(turn);
return { x: facing.x * cos - facing.y * sin, y: facing.x * sin + facing.y * cos };
return { x: facing.x * cos - facing.y * sin, y: facing.x * sin + facing.y * cos, cone: facing.cone };
}
/**