/** * `On fixed update`: 60 steps a second of game time, each with dt 1/60, however the frames fall – * counted by the same rule the physics steps by. */ import { describe, expect, it } from 'vitest'; import { SetAssetPropertyCommand } from '../../src/core/commands/assetCommands'; import { FIXED_STEP, fixedSteps, MAX_FIXED_STEPS } from '../../src/core/time'; import { graphEditor, makeBehavior, makeSpriteScene, place, startWorld } from '../helpers'; describe('fixed steps', () => { it('spends a frame in steps of 1/60 and keeps the rest for the next', () => { expect(fixedSteps(0, FIXED_STEP).steps).toBe(1); expect(fixedSteps(0, 1 / 30).steps).toBe(2); expect(fixedSteps(0, 0).steps).toBe(0); const half = fixedSteps(0, 1 / 120); expect(half.steps).toBe(0); expect(fixedSteps(half.backlog, 1 / 120).steps).toBe(1); }); it('adds up to the time that passed, however uneven the frames', () => { let backlog = 0; let steps = 0; for (let i = 0; i < 20; i++) for (const dt of [0.01, 0.02, 0.005, 0.015]) { const next = fixedSteps(backlog, dt); backlog = next.backlog; steps += next.steps; } expect(steps).toBe(60); }); it('catches up a hitch only so far and forgets the rest', () => { expect(fixedSteps(0, 1)).toEqual({ steps: MAX_FIXED_STEPS, backlog: 0 }); }); }); /** A behavior logging `fixed
` per step and `update` per frame. */ function stepped(runWhilePaused = false) { const f = makeSpriteScene('Box'); const behavior = makeBehavior(f, 'Engine'); if (runWhilePaused) f.stack.execute(new SetAssetPropertyCommand(behavior.id, 'runWhilePaused', true)); const edit = graphEditor(f, behavior); const fixed = edit.addNode('OnFixedUpdate'); const eventDt = edit.addNode('Log'); const nodeDt = edit.addNode('Log'); const update = edit.addNode('OnUpdate'); edit.wire([ [fixed, 'exec', eventDt, 'exec'], [fixed, 'dt', eventDt, 'message'], [eventDt, 'exec', nodeDt, 'exec'], [edit.addNode('Time'), 'dt', nodeDt, 'message'], [update, 'exec', edit.addNode('Log', { message: 'update' }), 'exec'], ]); place(f, behavior.id, { x: 0, y: 0 }); return startWorld(f, { behaviors: true }); } const STEP = String(FIXED_STEP); describe('On fixed update', () => { it('runs once per 1/60 of game time, before On update, and dt inside is the step', () => { const { world, frame } = stepped(); frame(1 / 30); expect(world.log).toEqual([STEP, STEP, STEP, STEP, 'update']); world.log.length = 0; frame(1 / 120); expect(world.log).toEqual(['update']); frame(1 / 120); expect(world.log).toEqual(['update', STEP, STEP, 'update']); }); it('stands still while the game is paused, also for a behavior that runs then', () => { const { world, frame } = stepped(true); world.paused = true; frame(1 / 30); frame(1 / 30); expect(world.log).toEqual(['update', 'update']); }); });