fix(ground): synchronize act-scene renderStarted with simulation clock domain and guard bounce height
This commit is contained in:
parent
1568fcbd10
commit
cef09dbd81
|
|
@ -148,12 +148,11 @@ export function resolveGroundItemMetadata(item: any, specifiedAmount?: number):
|
||||||
/** Calculate parabolic bounce height h(t) at timestamp now. */
|
/** Calculate parabolic bounce height h(t) at timestamp now. */
|
||||||
export function calculateBounceHeight(bounceState: GroundItemBounceState, now: number): number {
|
export function calculateBounceHeight(bounceState: GroundItemBounceState, now: number): number {
|
||||||
const elapsed = now - bounceState.startTime
|
const elapsed = now - bounceState.startTime
|
||||||
if (elapsed <= 0) return 0
|
if (elapsed <= 0 || elapsed >= bounceState.durationMs) return 0
|
||||||
if (elapsed >= bounceState.durationMs) return 0
|
|
||||||
|
|
||||||
const p = elapsed / bounceState.durationMs
|
const p = elapsed / bounceState.durationMs
|
||||||
// Parabolic equation: h(p) = 4 * H * p * (1 - p)
|
// Parabolic equation: h(p) = 4 * H * p * (1 - p)
|
||||||
return 4 * bounceState.peakHeightPx * p * (1 - p)
|
return Math.max(0, 4 * bounceState.peakHeightPx * p * (1 - p))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -6604,9 +6604,10 @@ if (typeof window !== 'undefined') {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const groundSimTime = (engine.world.tick + ((engine.world as any).alpha ?? 0)) * 40
|
||||||
for (const gItem of engine.groundItems.all) {
|
for (const gItem of engine.groundItems.all) {
|
||||||
pushEntity(gItem.x, gItem.y, () => {
|
pushEntity(gItem.x, gItem.y, () => {
|
||||||
drawGroundItem(renderer, gItem, renderStarted, itemsAtlasHandle)
|
drawGroundItem(renderer, gItem, groundSimTime, itemsAtlasHandle)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -438,6 +438,24 @@ describe('Milestone M11.3 (Issue #406) — Ground Item & Gold Spawning, Isometri
|
||||||
expect(entity.bounceState).toBeNull()
|
expect(entity.bounceState).toBeNull()
|
||||||
expect(manager.getRenderOffset(entity, 600)).toEqual({ x: 0, y: 0 })
|
expect(manager.getRenderOffset(entity, 600)).toEqual({ x: 0, y: 0 })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('defensively guards calculateBounceHeight against non-positive or expired elapsed times', () => {
|
||||||
|
const bounce = {
|
||||||
|
startTime: 1000,
|
||||||
|
durationMs: 400,
|
||||||
|
peakHeightPx: 24,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negative elapsed (sub-frame jitter / out of order): returns 0, never negative
|
||||||
|
expect(calculateBounceHeight(bounce, 900)).toBe(0)
|
||||||
|
expect(calculateBounceHeight(bounce, 999.9)).toBe(0)
|
||||||
|
// Exactly at start (t = 1000): returns 0
|
||||||
|
expect(calculateBounceHeight(bounce, 1000)).toBe(0)
|
||||||
|
// At duration boundary (t = 1400): returns 0
|
||||||
|
expect(calculateBounceHeight(bounce, 1400)).toBe(0)
|
||||||
|
// Far in future: returns 0
|
||||||
|
expect(calculateBounceHeight(bounce, 2000)).toBe(0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('4. 2:1 Isometric Diamond Grid Scatter Algorithm Parity', () => {
|
describe('4. 2:1 Isometric Diamond Grid Scatter Algorithm Parity', () => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue