Added network interpolation and fix send rate for pointer

This commit is contained in:
Mitchell McCaffrey 2020-07-31 11:03:12 +10:00
parent c6f9b29cc3
commit eed47e644c
3 changed files with 104 additions and 14 deletions

View File

@ -23,9 +23,6 @@ function MapPointer({
);
const mapStageRef = useContext(MapStageContext);
// const [isBrushDown, setIsBrushDown] = useState(false);
// const [brushPosition, setBrushPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
if (!active) {
return;
@ -47,7 +44,7 @@ function MapPointer({
}
function handleBrushUp() {
onPointerMove && onPointerUp({ x: 0, y: 0 });
onPointerMove && onPointerUp(getBrushPosition());
}
interactionEmitter.on("dragStart", handleBrushDown);

View File

@ -1,4 +1,8 @@
import { toRadians, roundTo as roundToNumber } from "./shared";
import {
toRadians,
roundTo as roundToNumber,
lerp as lerpNumber,
} from "./shared";
export function lengthSquared(p) {
return p.x * p.x + p.y * p.y;
@ -238,3 +242,7 @@ export function distance(a, b, type) {
return length(subtract(a, b));
}
}
export function lerp(a, b, alpha) {
return { x: lerpNumber(a.x, b.x, alpha), y: lerpNumber(a.y, b.y, alpha) };
}

View File

@ -1,14 +1,18 @@
import React, { useState, useContext, useEffect } from "react";
import React, { useState, useContext, useEffect, useRef } from "react";
import { Group } from "react-konva";
import AuthContext from "../contexts/AuthContext";
import MapPointer from "../components/map/MapPointer";
import { isEmpty } from "../helpers/shared";
import { lerp } from "../helpers/vector2";
// Send pointer updates every 100ms
const sendTickRate = 100;
function NetworkedMapPointer({ session, active, gridSize }) {
const { userId } = useContext(AuthContext);
const [pointerState, setPointerState] = useState({});
useEffect(() => {
if (userId && !(userId in pointerState)) {
setPointerState({
@ -17,13 +21,28 @@ function NetworkedMapPointer({ session, active, gridSize }) {
}
}, [userId, pointerState]);
// Send pointer updates every sendTickRate to peers to save on bandwidth
const ownPointerUpdateRef = useRef();
useEffect(() => {
function sendOwnPointerUpdates() {
if (ownPointerUpdateRef.current) {
session.send("pointer", ownPointerUpdateRef.current);
ownPointerUpdateRef.current = null;
}
}
const sendInterval = setInterval(sendOwnPointerUpdates, sendTickRate);
return () => {
clearInterval(sendInterval);
};
}, [session]);
function updateOwnPointerState(position, visible) {
const update = { [userId]: { position, visible, id: userId } };
setPointerState((prev) => ({
...prev,
...update,
[userId]: { position, visible, id: userId },
}));
session.send("pointer", update);
ownPointerUpdateRef.current = { position, visible, id: userId };
}
function handleOwnPointerDown(position) {
@ -38,13 +57,31 @@ function NetworkedMapPointer({ session, active, gridSize }) {
updateOwnPointerState(position, false);
}
// Handle pointer data receive
const syncedPointerStateRef = useRef({});
useEffect(() => {
function handlePeerData({ id, data }) {
if (id === "pointer") {
setPointerState((prev) => ({
...prev,
...data,
}));
// Setup an interpolation to the current pointer data when receiving a pointer event
if (syncedPointerStateRef.current[data.id]) {
const from = syncedPointerStateRef.current[data.id].to;
syncedPointerStateRef.current[data.id] = {
id: data.id,
from: {
...from,
time: performance.now(),
},
to: {
...data,
time: performance.now() + sendTickRate,
},
};
} else {
syncedPointerStateRef.current[data.id] = {
from: null,
to: { ...data, time: performance.now() + sendTickRate },
};
}
}
}
@ -55,6 +92,54 @@ function NetworkedMapPointer({ session, active, gridSize }) {
};
});
// Animate to the peer pointer positions
useEffect(() => {
let request = requestAnimationFrame(animate);
function animate(time) {
request = requestAnimationFrame(animate);
let interpolatedPointerState = {};
for (let syncState of Object.values(syncedPointerStateRef.current)) {
if (!syncState.from || !syncState.to) {
continue;
}
const totalInterpTime = syncState.to.time - syncState.from.time;
const currentInterpTime = time - syncState.from.time;
const alpha = currentInterpTime / totalInterpTime;
if (alpha >= 0 && alpha <= 1) {
interpolatedPointerState[syncState.id] = {
id: syncState.to.id,
visible: syncState.from.visible,
position: lerp(
syncState.from.position,
syncState.to.position,
alpha
),
};
}
if (alpha > 1 && !syncState.to.visible) {
interpolatedPointerState[syncState.id] = {
id: syncState.id,
visible: syncState.to.visible,
position: syncState.to.position,
};
delete syncedPointerStateRef.current[syncState.to.id];
}
}
if (!isEmpty(interpolatedPointerState)) {
setPointerState((prev) => ({
...prev,
...interpolatedPointerState,
}));
}
}
return () => {
cancelAnimationFrame(request);
};
}, []);
return (
<Group>
{Object.values(pointerState).map((pointer) => (