improve bus compartment re-boarding logic and wall thickness
- Add `isSettledRelativeToChassis()` to measure settling speed relative to the bus instead of absolute world velocity, allowing kids resting inside a moving bus to re-board properly - Gate re-boarding on being within the compartment's horizontal width to prevent false positives from kids resting on the ground at the bus's height - Use a separate settling timer (`_slowSinceRelative`) to prevent interference between absolute and relative settle checks - Increase `compartmentWallThickness` to 16 for better physical collision response against fast-moving kids
This commit is contained in:
parent
17fe6efd10
commit
9da3219529
|
|
@ -49,7 +49,10 @@ export const BUS = {
|
|||
// back below it while inside compartmentHalfWidth re-contains the kid.
|
||||
// Tune by playtesting - not physically derived.
|
||||
compartmentHalfWidth: 75 * WORLD_SCALE,
|
||||
compartmentWallThickness: 10 * WORLD_SCALE,
|
||||
// A bit thicker than the floor - gives a fast-moving kid more physical
|
||||
// wall to hit before KidManager's per-tick clamp is what's actually
|
||||
// stopping it, rather than the fixture itself.
|
||||
compartmentWallThickness: 16 * WORLD_SCALE,
|
||||
compartmentFloorThickness: 10 * WORLD_SCALE,
|
||||
compartmentFloorY: 12 * WORLD_SCALE,
|
||||
compartmentTopY: -60 * WORLD_SCALE,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export default class Kid {
|
|||
this.seatIndex = seatIndex;
|
||||
this.state = 'aboard'; // aboard | ejected
|
||||
this._slowSince = null; // see isSettled()
|
||||
this._slowSinceRelative = null; // see isSettledRelativeToChassis()
|
||||
|
||||
const worldPos = bus.getSeatWorldPosition(seatIndex);
|
||||
|
||||
|
|
@ -91,6 +92,26 @@ export default class Kid {
|
|||
return now - this._slowSince >= KID.settleDurationMs;
|
||||
}
|
||||
|
||||
// Same idea as isSettled, but measured relative to the chassis's own
|
||||
// velocity rather than the world - a kid at rest inside a moving bus
|
||||
// shares the bus's velocity, which is almost always well above
|
||||
// restSpeedThreshold, so gating re-boarding on isSettled's absolute speed
|
||||
// would mean a kid resting inside the compartment while the bus drives
|
||||
// could never actually re-board. Kept as its own method with its own
|
||||
// timer (not a parameter on isSettled) since KidManager and PlayScene call
|
||||
// these in the same tick with different references - sharing one timer
|
||||
// between them would have each call reset the other's progress.
|
||||
isSettledRelativeToChassis(now, chassisVelocity) {
|
||||
const v = this.image.body.velocity;
|
||||
const speed = Math.hypot(v.x - chassisVelocity.x, v.y - chassisVelocity.y);
|
||||
if (speed >= KID.restSpeedThreshold) {
|
||||
this._slowSinceRelative = null;
|
||||
return false;
|
||||
}
|
||||
if (this._slowSinceRelative === null) this._slowSinceRelative = now;
|
||||
return now - this._slowSinceRelative >= KID.settleDurationMs;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.image.destroy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export default class KidManager {
|
|||
// chassis's own rotated frame. A kid drifting up past the open top
|
||||
// becomes ejected immediately. A floating kid drifting back down through
|
||||
// the open top only re-boards once it has actually come to rest inside
|
||||
// the compartment (see Kid.isSettled), so a kid still mid-arc can't
|
||||
// the compartment (see Kid.isSettledRelativeToChassis), so a kid still mid-arc can't
|
||||
// teleport back in mid-air.
|
||||
_onAfterUpdate() {
|
||||
const chassis = this.bus.chassis;
|
||||
|
|
@ -62,19 +62,29 @@ export default class KidManager {
|
|||
|
||||
const dx = kid.image.x - chassis.x;
|
||||
const dy = kid.image.y - chassis.y;
|
||||
const localX = dx * cos + dy * sin;
|
||||
const localY = -dx * sin + dy * cos;
|
||||
const isAboveOpenTop = localY < BUS.compartmentTopY;
|
||||
// Re-boarding additionally requires being within the compartment's
|
||||
// horizontal span, not just below the open top - localY alone can't
|
||||
// tell "resting inside the compartment" apart from "resting on the
|
||||
// ground anywhere else in the level at roughly the bus's height,"
|
||||
// since compartmentTopY is measured purely along the chassis's local Y
|
||||
// axis with no horizontal extent to it.
|
||||
const isWithinCompartmentWidth = Math.abs(localX) <= BUS.compartmentHalfWidth;
|
||||
|
||||
if (isAboveOpenTop && kid.state === 'aboard') {
|
||||
kid.markEjected();
|
||||
playRandomKidFallSound(this.scene);
|
||||
anyChanged = true;
|
||||
} else if (!isAboveOpenTop && kid.state === 'ejected' && kid.isSettled(this.scene.time.now)) {
|
||||
// Re-boarding is gated on the kid actually settling (see Kid.isSettled),
|
||||
// not just passing through the compartment's vertical band again. Ejected
|
||||
// kids run under reduced gravity, so a kid still floating/arc-ing will
|
||||
// happily drift back through the open top while airborne; without this
|
||||
// gate it would flip back to aboard mid-air and "teleport" back in.
|
||||
} else if (!isAboveOpenTop && isWithinCompartmentWidth && kid.state === 'ejected' &&
|
||||
kid.isSettledRelativeToChassis(this.scene.time.now, chassis.body.velocity)) {
|
||||
// Re-boarding is gated on the kid actually settling relative to the
|
||||
// chassis (see Kid.isSettledRelativeToChassis), not just passing
|
||||
// through the compartment's open top again. Ejected kids run under
|
||||
// reduced gravity, so a kid still floating/arc-ing will happily
|
||||
// drift back through the open top while airborne; without this gate
|
||||
// it would flip back to aboard mid-air and "teleport" back in.
|
||||
kid.markAboard();
|
||||
anyChanged = true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue