perf: smoother interpolation
This commit is contained in:
parent
b912fee2e7
commit
3b88ab8969
|
@ -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'],
|
||||
});
|
||||
authoritative = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.location = 0;
|
||||
}
|
||||
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,60 +44,65 @@ export default class Interpolator {
|
|||
);
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: 'Tick',
|
||||
payload: {
|
||||
ecs: interpolated,
|
||||
elapsed,
|
||||
frame: this.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;
|
||||
}
|
||||
}
|
||||
|
||||
onmessage = async (event) => {
|
||||
interpolator.accept(event.data);
|
||||
switch (event.data.type) {
|
||||
case 'EcsChange': {
|
||||
if (handle) {
|
||||
cancelAnimationFrame(handle)
|
||||
handle = null;
|
||||
}
|
||||
interpolator.latest = null;
|
||||
break;
|
||||
}
|
||||
case 'Tick': {
|
||||
if (interpolator.penultimate) {
|
||||
postMessage({
|
||||
type: 'Tick',
|
||||
payload: {
|
||||
ecs: interpolator.penultimate.payload.ecs,
|
||||
elapsed: last ? (performance.now() - last) / 1000 : 0,
|
||||
frame: interpolator.penultimate.payload.frame,
|
||||
...penultimate.payload,
|
||||
ecs: interpolated,
|
||||
elapsed,
|
||||
frame: penultimate.payload.frame + fraction,
|
||||
},
|
||||
});
|
||||
}
|
||||
requestAnimationFrame(interpolate);
|
||||
}
|
||||
requestAnimationFrame(interpolate);
|
||||
|
||||
onmessage = async (event) => {
|
||||
const packet = event.data;
|
||||
switch (packet.type) {
|
||||
case 'EcsChange': {
|
||||
authoritative = [];
|
||||
latest = null;
|
||||
tracking = [];
|
||||
postMessage(packet);
|
||||
break;
|
||||
}
|
||||
case 'Tick': {
|
||||
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: {
|
||||
...penultimate.payload,
|
||||
elapsed: last ? (performance.now() - last) / 1000 : 0,
|
||||
},
|
||||
});
|
||||
if (!handle) {
|
||||
last = performance.now();
|
||||
handle = requestAnimationFrame(interpolate);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
postMessage(packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user