e
This commit is contained in:
parent
f657b6a4a4
commit
d163ceb291
76 changed files with 1385 additions and 524 deletions
|
|
@ -96,15 +96,40 @@
|
|||
return true;
|
||||
}
|
||||
|
||||
function updateCursorCare(worldRef, dt) {
|
||||
const pointer = worldRef?.pointer;
|
||||
if (!pointer?.inside || !global.TarinaiCursorCareSystem?.updateOne) return 0;
|
||||
// Cursor care is a direct pointer interaction and must remain responsive even
|
||||
// when the expensive creature pipeline is cadence-throttled. One spatial
|
||||
// query per frame is cheaper than testing every creature individually.
|
||||
const candidates = worldRef.nearbyTarinai?.(pointer.x || 0, pointer.y || 0, 220, true) || [];
|
||||
let updated = 0;
|
||||
for (const t of candidates) {
|
||||
if (!t || t.dead) continue;
|
||||
if (global.TarinaiCursorCareSystem.updateOne(t, dt)) updated += 1;
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
function updateCreatures(worldRef, dt) {
|
||||
const h = helpers;
|
||||
const end = global.TarinaiPerf.begin("update.tarinai.individual") || null;
|
||||
const visibleRect = h.updateVisibleWorldRect(worldRef, 160);
|
||||
const nearVisibleRect = h.updateVisibleWorldRect(worldRef, 520);
|
||||
const now = worldRef.time || 0;
|
||||
updateCursorCare(worldRef, dt);
|
||||
const pressure = prepareMechanicalCrowdPressure(worldRef, visibleRect);
|
||||
const pressureActive = Boolean(pressure?.active);
|
||||
const visibleFullBudget = pressureActive ? Math.max(1, profileNumber("pressureCreatureFullBudget", 24)) : 1000000000;
|
||||
// Prevent a low-FPS feedback loop: at large populations a long frame makes
|
||||
// nearly every visible creature's full update become due at once, which makes
|
||||
// the next frame even longer. Under normal frame times this cap is disabled;
|
||||
// only slow frames spread expensive full updates across subsequent frames.
|
||||
const creatureCountForBudget = (worldRef.tarinai || []).length;
|
||||
const antiSpiralVisibleBudget = creatureCountForBudget >= 160 && dt > 0.028
|
||||
? Math.max(36, Math.min(96, Math.floor(2.8 / Math.max(0.028, dt))))
|
||||
: 1000000000;
|
||||
const pressureVisibleBudget = pressureActive ? Math.max(1, profileNumber("pressureCreatureFullBudget", 24)) : 1000000000;
|
||||
const visibleFullBudget = Math.min(antiSpiralVisibleBudget, pressureVisibleBudget);
|
||||
const visibleMotionBudget = pressureActive ? Math.max(1, profileNumber("pressureCreatureMotionBudget", 36)) : 1000000000;
|
||||
const collectDiagnostics = global.TarinaiPerf?.diagnosticsEnabled?.() === true;
|
||||
const stats = {
|
||||
|
|
@ -169,6 +194,11 @@
|
|||
if (creatureIndex >= creatureCount) creatureIndex = 0;
|
||||
const t = creatureList[creatureIndex++];
|
||||
if (!t || t.dead) continue;
|
||||
// Reproduction cooldown is simulation time, not AI-think time. Keep it
|
||||
// advancing even when the expensive creature pipeline is cadence/budget
|
||||
// throttled; otherwise large colonies can stay reproductively locked for
|
||||
// many in-game minutes after the early population burst.
|
||||
t.reproductionTimer = (Number(t.reproductionTimer || 0) || 0) - dt;
|
||||
stats.total += 1;
|
||||
const behaviorId = updatePolicy.behaviorId(t);
|
||||
inc(stats.states, behaviorId);
|
||||
|
|
@ -205,8 +235,17 @@
|
|||
const jitter = stableUnit(t.id || t.familyKey || Math.random(), `visible-creature-budget:${Math.floor(now * 5)}`);
|
||||
t._nextLowFreqUpdateAt = now + Math.max(dt, interval * (0.20 + jitter * 0.25));
|
||||
stats.visibleFullSkips += 1;
|
||||
stats.skipped += 1;
|
||||
inc(stats.skippedStates, behaviorId);
|
||||
// Keep movement visually continuous while deferring only the heavy
|
||||
// AI/environment portion of the update.
|
||||
if (cadence.smoothMotion && stats.visibleMotionUsed < stats.visibleMotionBudget
|
||||
&& global.TarinaiCreatureRuntime.updateMotionOnly(t, dt, { context })) {
|
||||
stats.smooth += 1;
|
||||
stats.visibleMotionUsed += 1;
|
||||
inc(stats.smoothStates, behaviorId);
|
||||
} else {
|
||||
stats.skipped += 1;
|
||||
inc(stats.skippedStates, behaviorId);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (usesBackgroundBudget && stats.fullBudgetUsed >= stats.fullBudget) {
|
||||
|
|
@ -225,7 +264,15 @@
|
|||
stats.full += 1;
|
||||
if (cadence.visible) stats.visibleFullUsed += 1;
|
||||
if (usesBackgroundBudget) stats.fullBudgetUsed += 1;
|
||||
global.TarinaiCreatureRuntime.updateOne(t, runDt, { context });
|
||||
// Off-screen creatures used to receive the accumulated AI/timer dt but
|
||||
// only one rendered-frame worth of movement (about 0.016s). At high
|
||||
// zoom this effectively froze creatures after they left the viewport.
|
||||
// Catch up a bounded amount of motion on their scheduled full update;
|
||||
// the movement step uses the light collision path for this catch-up.
|
||||
const runContext = cadence.visible
|
||||
? context
|
||||
: { ...context, motionDt: Math.min(runDt, cadence.near ? 0.45 : 0.75), offscreenCatchup: true };
|
||||
global.TarinaiCreatureRuntime.updateOne(t, runDt, { context: runContext });
|
||||
} else if (cadence.smoothMotion) {
|
||||
if (cadence.visible && stats.visibleMotionUsed >= stats.visibleMotionBudget) {
|
||||
stats.visibleMotionSkips += 1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue