From 3b88ab89695624f6bd39ce5d7b18b41c2621bac8 Mon Sep 17 00:00:00 2001 From: cha0s Date: Fri, 6 Sep 2024 19:21:58 -0500 Subject: [PATCH] perf: smoother interpolation --- app/client/interpolator.js | 135 +++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 75 deletions(-) diff --git a/app/client/interpolator.js b/app/client/interpolator.js index 0b29b7a..fb32346 100644 --- a/app/client/interpolator.js +++ b/app/client/interpolator.js @@ -1,47 +1,27 @@ -export default class Interpolator { - duration = 0; - latest = null; - location = 0; - penultimate; - tracking = []; - accept(state) { - const packet = state; - if ('Tick' !== packet.type) { +let authoritative = []; +let duration = 0; +let last = performance.now(); +let latest = null; +let location = 0; +let penultimate; +let tracking = []; + +const interpolate = () => { + const now = performance.now(); + const elapsed = (now - last) / 1000; + last = now; + if (authoritative.length > 0) { + for (const packet of authoritative) { postMessage(packet); - return; } - this.penultimate = this.latest; - this.latest = packet; - this.tracking = []; - if (this.penultimate) { - this.duration = this.penultimate.payload.elapsed; - const [from, to] = [this.penultimate.payload.ecs, this.latest.payload.ecs]; - for (const entityId in from) { - for (const componentName in from[entityId]) { - if ( - ['Camera', 'Position'].includes(componentName) - && to[entityId]?.[componentName] - ) { - this.tracking.push({ - entityId, - componentName, - properties: ['x', 'y'], - }); - } - } - } - } - this.location = 0; + authoritative = []; } - interpolate(elapsed) { - if (0 === this.tracking.length) { - return undefined; - } - this.location += elapsed; - const fraction = Math.min(1, this.location / this.duration); - const [from, to] = [this.penultimate.payload.ecs, this.latest.payload.ecs]; + if (tracking.length > 0) { + location += elapsed; + const fraction = location / duration; + const [from, to] = [penultimate.payload.ecs, latest.payload.ecs]; const interpolated = {}; - for (const {entityId, componentName, properties} of this.tracking) { + for (const {entityId, componentName, properties} of tracking) { if (!interpolated[entityId]) { interpolated[entityId] = {}; } @@ -64,61 +44,66 @@ export default class Interpolator { ); } } - return { + postMessage({ type: 'Tick', payload: { + ...penultimate.payload, ecs: interpolated, elapsed, - frame: this.penultimate.payload.frame + fraction, + frame: penultimate.payload.frame + fraction, }, - }; - } -} - -let handle; -const interpolator = new Interpolator(); -let last; - -const interpolate = (now) => { - const elapsed = (now - last) / 1000; - last = now; - const interpolated = interpolator.interpolate(elapsed); - if (interpolated) { - handle = requestAnimationFrame(interpolate); - postMessage(interpolated); - } - else { - handle = null; + }); } + requestAnimationFrame(interpolate); } +requestAnimationFrame(interpolate); onmessage = async (event) => { - interpolator.accept(event.data); - switch (event.data.type) { + const packet = event.data; + switch (packet.type) { case 'EcsChange': { - if (handle) { - cancelAnimationFrame(handle) - handle = null; - } - interpolator.latest = null; + authoritative = []; + latest = null; + tracking = []; + postMessage(packet); break; } case 'Tick': { - if (interpolator.penultimate) { - postMessage({ + penultimate = latest; + latest = packet; + if (penultimate) { + duration = penultimate.payload.elapsed; + location = 0; + tracking = []; + const [from, to] = [penultimate.payload.ecs, latest.payload.ecs]; + for (const entityId in from) { + for (const componentName in from[entityId]) { + if ( + ['Camera', 'Position'].includes(componentName) + && to[entityId]?.[componentName] + ) { + tracking.push({ + entityId, + componentName, + properties: ['x', 'y'], + }); + } + } + } + authoritative.push({ type: 'Tick', payload: { - ecs: interpolator.penultimate.payload.ecs, + ...penultimate.payload, elapsed: last ? (performance.now() - last) / 1000 : 0, - frame: interpolator.penultimate.payload.frame, }, }); - if (!handle) { - last = performance.now(); - handle = requestAnimationFrame(interpolate); - } + last = performance.now(); } break; } + default: { + postMessage(packet); + break; + } } };