|
@@ -26,6 +26,8 @@ import {
|
|
|
CHUNK_REST_ANGULAR,
|
|
CHUNK_REST_ANGULAR,
|
|
|
CHUNK_REST_SPEED,
|
|
CHUNK_REST_SPEED,
|
|
|
CHUNK_REST_TIME,
|
|
CHUNK_REST_TIME,
|
|
|
|
|
+ CHUNK_SLEEP_TIME,
|
|
|
|
|
+ CHUNK_WAKE_MARGIN_PX,
|
|
|
COUGH_INTERVAL_S,
|
|
COUGH_INTERVAL_S,
|
|
|
COUGH_STUMBLE,
|
|
COUGH_STUMBLE,
|
|
|
COYOTE_TIME,
|
|
COYOTE_TIME,
|
|
@@ -408,6 +410,7 @@ export class Game {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
if (changedRects.length > 0) for (const component of this.finder.find(changedRects)) this.detach(component, changedBy[component.region], result);
|
|
if (changedRects.length > 0) for (const component of this.finder.find(changedRects)) this.detach(component, changedBy[component.region], result);
|
|
|
|
|
+ if (changedRects.length > 0) this.wakeChunksNear(changedRects);
|
|
|
this.syncTerrain(result);
|
|
this.syncTerrain(result);
|
|
|
|
|
|
|
|
this.crushedThisStep.clear();
|
|
this.crushedThisStep.clear();
|
|
@@ -535,11 +538,27 @@ export class Game {
|
|
|
}
|
|
}
|
|
|
const resting = chunk.speed() < CHUNK_REST_SPEED && Math.abs(chunk.body.getAngularVelocity()) < CHUNK_REST_ANGULAR;
|
|
const resting = chunk.speed() < CHUNK_REST_SPEED && Math.abs(chunk.body.getAngularVelocity()) < CHUNK_REST_ANGULAR;
|
|
|
chunk.restTime = resting ? chunk.restTime + TICK_DT : 0;
|
|
chunk.restTime = resting ? chunk.restTime + TICK_DT : 0;
|
|
|
|
|
+ // A chunk settling on a rough floor touches dozens of little edges, and planck's continuous
|
|
|
|
|
+ // collision makes each tick very costly (100+ ms for a whole tier). Put it to sleep while it
|
|
|
|
|
+ // waits to merge; anything touching it, or the terrain changing nearby, wakes it up.
|
|
|
|
|
+ if (chunk.restTime >= CHUNK_SLEEP_TIME && chunk.body.isAwake()) chunk.body.setAwake(false);
|
|
|
chunk.retryIn = Math.max(0, chunk.retryIn - TICK_DT);
|
|
chunk.retryIn = Math.max(0, chunk.retryIn - TICK_DT);
|
|
|
if (chunk.restTime >= CHUNK_REST_TIME && chunk.retryIn === 0) this.tryMerge(chunk, result);
|
|
if (chunk.restTime >= CHUNK_REST_TIME && chunk.retryIn === 0) this.tryMerge(chunk, result);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /** Wakes the sleeping chunks close to changed terrain, so they fall if their floor went away. */
|
|
|
|
|
+ private wakeChunksNear(rects: Rect[]): void {
|
|
|
|
|
+ for (const chunk of this.chunks.values()) {
|
|
|
|
|
+ if (chunk.body.isAwake()) continue;
|
|
|
|
|
+ const c = chunk.body.getWorldCenter();
|
|
|
|
|
+ const reach = Math.hypot(chunk.w, chunk.h) / 2 + CHUNK_WAKE_MARGIN_PX;
|
|
|
|
|
+ const x = c.x * PPM;
|
|
|
|
|
+ const y = c.y * PPM;
|
|
|
|
|
+ if (rects.some((r) => x + reach >= r.x0 && x - reach <= r.x1 && y + reach >= r.y0 && y - reach <= r.y1)) chunk.body.setAwake(true);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/** Rasterizes a resting chunk back into the bitmap, if the result is anchored. */
|
|
/** Rasterizes a resting chunk back into the bitmap, if the result is anchored. */
|
|
|
private tryMerge(chunk: FallingChunk, result: StepResult): void {
|
|
private tryMerge(chunk: FallingChunk, result: StepResult): void {
|
|
|
const s = chunk.state();
|
|
const s = chunk.state();
|
|
@@ -562,6 +581,9 @@ export class Game {
|
|
|
}
|
|
}
|
|
|
this.terrain.markDirty(rect);
|
|
this.terrain.markDirty(rect);
|
|
|
chunk.retryIn = CHUNK_MERGE_RETRY;
|
|
chunk.retryIn = CHUNK_MERGE_RETRY;
|
|
|
|
|
+ // Not held by anything anchored: let it move again (it may be resting on a player).
|
|
|
|
|
+ chunk.body.setAwake(true);
|
|
|
|
|
+ chunk.restTime = 0;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
// Rotated rasterization leaves a few pixels touching only diagonally: drop them from the patch.
|
|
// Rotated rasterization leaves a few pixels touching only diagonally: drop them from the patch.
|