lerp approach

This commit is contained in:
Evert Prants 2022-04-09 17:49:30 +03:00
parent e075590734
commit c198306717
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
6 changed files with 131 additions and 42 deletions

View File

@ -118,14 +118,18 @@ export class Game {
}); });
this.socket.on('playerupdate', (data) => { this.socket.on('playerupdate', (data) => {
const player = this.players.find((player) => player.user.id === data.id); data.forEach((item) => {
if ( const player = this.players.find(
player && (player) => player.user.id === item.id,
player instanceof PlayerEntity && );
player.user.id !== this.me.id if (
) { player &&
player.addUncommittedChanges(data); player instanceof PlayerEntity &&
} player.user.id !== this.me.id
) {
player.addUncommittedChanges(item);
}
});
}); });
this.socket.on('chat', (event) => { this.socket.on('chat', (event) => {

View File

@ -1,16 +1,29 @@
import { IcyNetUser } from '../../common/types/user'; import { IcyNetUser } from '../../common/types/user';
import * as THREE from 'three'; import * as THREE from 'three';
import { PonyEntity } from './pony'; import { PonyEntity } from './pony';
import { Packet } from '../../common/types/packet'; import {
FullStatePacket,
PositionUpdatePacket,
} from '../../common/types/packet';
import { CanvasUtils } from './canvas-utils'; import { CanvasUtils } from './canvas-utils';
import { ChatBubble } from './chat-bubble'; import { ChatBubble } from './chat-bubble';
const chatBuilder = new CanvasUtils(); const chatBuilder = new CanvasUtils();
export class PlayerEntity extends PonyEntity { export class PlayerEntity extends PonyEntity {
private uncommittedPacket: Packet = {}; private _updateQueue: PositionUpdatePacket[] = [];
private _targetFrame: any = null;
private _lastFrame: any = null;
private _chats: ChatBubble[] = []; private _chats: ChatBubble[] = [];
private _p1 = new THREE.Vector3();
private _p2 = new THREE.Vector3();
private _q1 = new THREE.Vector3();
private _q2 = new THREE.Vector3();
private _pf = new THREE.Vector3();
private _qf = new THREE.Vector3();
constructor(public user: IcyNetUser) { constructor(public user: IcyNetUser) {
super(); super();
} }
@ -84,18 +97,31 @@ export class PlayerEntity extends PonyEntity {
); );
} }
public addUncommittedChanges(packet: Packet) { public addUncommittedChanges(packet: PositionUpdatePacket) {
this.uncommittedPacket = { ...this.uncommittedPacket, ...packet }; const appendix = { ...packet, time: 0.1 };
this._updateQueue.push(appendix);
if (!this._targetFrame) {
this.setFromPacket(packet);
this._targetFrame = appendix;
}
} }
public update(dt: number) { public update(dt: number) {
this.commitServerUpdate(); this.commitServerUpdate(dt);
super.update(dt); super.update(dt);
} }
private setFromPacket(packet: Packet) { private setFromPacket(packet: FullStatePacket | PositionUpdatePacket) {
if (packet.velocity) { if ((packet as FullStatePacket).velocity) {
this.setVelocity(new THREE.Vector3().fromArray(packet.velocity)); this.setVelocity(
new THREE.Vector3().fromArray((packet as FullStatePacket).velocity),
);
}
if ((packet as FullStatePacket).angular) {
this.setAngularVelocity(
new THREE.Vector3().fromArray((packet as FullStatePacket).angular),
);
} }
if (packet.position) { if (packet.position) {
@ -106,17 +132,47 @@ export class PlayerEntity extends PonyEntity {
this.setRotation(new THREE.Euler().fromArray(packet.rotation)); this.setRotation(new THREE.Euler().fromArray(packet.rotation));
} }
if (packet.angular) {
this.setAngularVelocity(new THREE.Vector3().fromArray(packet.angular));
}
if (packet.animState) { if (packet.animState) {
this.setWalkAnimationState(packet.animState); this.setWalkAnimationState(packet.animState);
} }
} }
private commitServerUpdate() { private commitServerUpdate(dt: number) {
this.setFromPacket(this.uncommittedPacket); if (this._updateQueue.length == 0) {
this.uncommittedPacket = {}; return;
}
for (let i = 0; i < this._updateQueue.length; ++i) {
this._updateQueue[i].time -= dt;
}
while (this._updateQueue.length > 0 && this._updateQueue[0].time <= 0.0) {
this._lastFrame = {
animState: this._targetFrame.animState,
position: this.container.position.toArray(),
rotation: this.container.rotation.toArray(),
};
this._targetFrame = this._updateQueue.shift();
this._targetFrame.time = 0.0;
}
// Lerp the position and rotation
if (this._targetFrame && this._lastFrame) {
this._targetFrame.time += dt;
this._p1.fromArray(this._lastFrame.position);
this._p2.fromArray(this._targetFrame.position);
this._q1.fromArray(this._lastFrame.rotation);
this._q2.fromArray(this._targetFrame.rotation);
this._pf.copy(this._p1);
this._qf.copy(this._q1);
const t = Math.max(Math.min(this._targetFrame.time / 0.1, 1.0), 0.0);
this._pf.lerp(this._p2, t);
this._qf.lerp(this._q2, t);
this.setPosition(this._pf);
this.setRotation(this._qf);
this.setWalkAnimationState(this._lastFrame.animState);
}
} }
} }

View File

@ -92,20 +92,24 @@ export class Player extends PonyEntity {
} else if (this._wasTurning && !wasExternalForce) { } else if (this._wasTurning && !wasExternalForce) {
this._wasTurning = false; this._wasTurning = false;
this.angularVelocity.set(0, 0, 0); this.angularVelocity.set(0, 0, 0);
this.changes.rotation = this.container.rotation.toArray();
this.changes.angular = this.angularVelocity.toArray(); this.changes.angular = this.angularVelocity.toArray();
} }
this.changes.rotation = this.container.rotation.toArray();
if (vector.y !== 0) { if (vector.y !== 0) {
this.velocity.copy(this._lookVector.clone().multiplyScalar(vector.y)); this.velocity.copy(this._lookVector.clone().multiplyScalar(vector.y));
this.changes.velocity = this.velocity.toArray(); this.changes.velocity = this.velocity.toArray();
this._wasMoving = true; this._wasMoving = true;
this.setWalkAnimationState(1);
} else if (this._wasMoving && !wasExternalForce) { } else if (this._wasMoving && !wasExternalForce) {
this._wasMoving = false; this._wasMoving = false;
this.velocity.set(0, 0, 0); this.velocity.set(0, 0, 0);
this.changes.position = this.container.position.toArray();
this.changes.velocity = this.velocity.toArray(); this.changes.velocity = this.velocity.toArray();
this.setWalkAnimationState(0);
} }
this.changes.position = this.container.position.toArray();
} }
update(dt: number): void { update(dt: number): void {

View File

@ -1,7 +1,7 @@
import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils'; import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils';
import * as THREE from 'three'; import * as THREE from 'three';
import modelLoaderInstance from './pony-loader'; import modelLoaderInstance from './pony-loader';
import { Packet } from '../../common/types/packet'; import { FullStatePacket } from '../../common/types/packet';
import { NameTag } from './nametag'; import { NameTag } from './nametag';
import { CanvasUtils } from './canvas-utils'; import { CanvasUtils } from './canvas-utils';
@ -17,7 +17,7 @@ export class PonyEntity {
public idleAction: THREE.AnimationAction; public idleAction: THREE.AnimationAction;
public walkAction: THREE.AnimationAction; public walkAction: THREE.AnimationAction;
public nameTag?: NameTag; public nameTag?: NameTag;
public changes: Packet = {}; public changes: FullStatePacket = {};
initialize() { initialize() {
this.model = (SkeletonUtils as any).clone(modelLoaderInstance.ponyModel); this.model = (SkeletonUtils as any).clone(modelLoaderInstance.ponyModel);
@ -39,12 +39,6 @@ export class PonyEntity {
).add(this.angularVelocity.clone().multiplyScalar(dt)), ).add(this.angularVelocity.clone().multiplyScalar(dt)),
); );
this.mixer.update(dt); this.mixer.update(dt);
if (this.velocity.length() > 0) {
this.setWalkAnimationState(1);
} else {
this.setWalkAnimationState(0);
}
} }
public addNameTag(name: string) { public addNameTag(name: string) {

View File

@ -1,6 +1,14 @@
import { IcyNetUser } from './user'; import { IcyNetUser } from './user';
export interface Packet { export interface PositionUpdatePacket {
id?: number;
position?: number[];
rotation?: (number | string)[];
animState?: number;
time?: number;
}
export interface FullStatePacket {
velocity?: number[]; velocity?: number[];
angular?: number[]; angular?: number[];
position?: number[]; position?: number[];
@ -8,4 +16,4 @@ export interface Packet {
animState?: number; animState?: number;
} }
export interface CompositePacket extends IcyNetUser, Packet {} export interface CompositePacket extends IcyNetUser, FullStatePacket {}

View File

@ -1,7 +1,10 @@
import { Server, Socket } from 'socket.io'; import { Server, Socket } from 'socket.io';
import { config } from '../config';
import { RequestHandler } from 'express'; import { RequestHandler } from 'express';
import { IcyNetUser } from '../../common/types/user'; import { IcyNetUser } from '../../common/types/user';
import {
CompositePacket,
PositionUpdatePacket,
} from '../../common/types/packet';
const PLACEHOLDER_USER = (socket: Socket): IcyNetUser => { const PLACEHOLDER_USER = (socket: Socket): IcyNetUser => {
const randomName = `player-${socket.id.substring(0, 8)}`; const randomName = `player-${socket.id.substring(0, 8)}`;
@ -16,6 +19,7 @@ const PLACEHOLDER_USER = (socket: Socket): IcyNetUser => {
export class Game { export class Game {
private _connections: Socket[] = []; private _connections: Socket[] = [];
private _changedPlayers: number[] = [];
constructor(private io: Server, private session: RequestHandler) {} constructor(private io: Server, private session: RequestHandler) {}
@ -50,9 +54,9 @@ export class Game {
socket.data.user = user; socket.data.user = user;
socket.data.playerinfo = { socket.data.playerinfo = {
velocity: [0, 0, 0], velocity: [0, 0, 0],
angular: [0, 0, 0], angular: [0, 0, 0, 'XYZ'],
position: [0, 0, 0], position: [0, 0, 0],
rotation: [0, 0, 0], rotation: [0, 0, 0, 'XYZ'],
animState: 0, animState: 0,
}; };
@ -71,10 +75,10 @@ export class Game {
...socket.data.playerinfo, ...socket.data.playerinfo,
...packet, ...packet,
}; };
socket.broadcast.emit('playerupdate', {
id: user.id, if (!this._changedPlayers.includes(socket.data.user.id)) {
...packet, this._changedPlayers.push(socket.data.user.id);
}); }
}); });
socket.on('chat-send', (raw) => { socket.on('chat-send', (raw) => {
@ -91,5 +95,24 @@ export class Game {
} }
}); });
}); });
setInterval(() => {
const playerInfo: PositionUpdatePacket[] = [];
this._connections
.filter(
(conn) =>
conn.data.user && this._changedPlayers.includes(conn.data.user.id),
)
.forEach((conn) =>
playerInfo.push({
position: conn.data.playerinfo.position,
rotation: conn.data.playerinfo.rotation,
animState: conn.data.playerinfo.animState,
id: conn.data.user.id,
}),
);
this.io.emit('playerupdate', playerInfo);
this._changedPlayers.length = 0;
}, 100);
} }
} }