/**
* `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']);
});
});