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