From c1983067175f6ba72c28bd8a13fab126128fcc00 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Sat, 9 Apr 2022 17:49:30 +0300 Subject: [PATCH] lerp approach --- src/client/game.ts | 20 ++++--- src/client/object/player-entity.ts | 86 ++++++++++++++++++++++++------ src/client/object/player.ts | 8 ++- src/client/object/pony.ts | 10 +--- src/common/types/packet.ts | 12 ++++- src/server/object/game.ts | 37 ++++++++++--- 6 files changed, 131 insertions(+), 42 deletions(-) diff --git a/src/client/game.ts b/src/client/game.ts index 3ae2138..c2dcc3d 100644 --- a/src/client/game.ts +++ b/src/client/game.ts @@ -118,14 +118,18 @@ export class Game { }); this.socket.on('playerupdate', (data) => { - const player = this.players.find((player) => player.user.id === data.id); - if ( - player && - player instanceof PlayerEntity && - player.user.id !== this.me.id - ) { - player.addUncommittedChanges(data); - } + data.forEach((item) => { + const player = this.players.find( + (player) => player.user.id === item.id, + ); + if ( + player && + player instanceof PlayerEntity && + player.user.id !== this.me.id + ) { + player.addUncommittedChanges(item); + } + }); }); this.socket.on('chat', (event) => { diff --git a/src/client/object/player-entity.ts b/src/client/object/player-entity.ts index 539fa80..6ba0ecf 100644 --- a/src/client/object/player-entity.ts +++ b/src/client/object/player-entity.ts @@ -1,16 +1,29 @@ import { IcyNetUser } from '../../common/types/user'; import * as THREE from 'three'; import { PonyEntity } from './pony'; -import { Packet } from '../../common/types/packet'; +import { + FullStatePacket, + PositionUpdatePacket, +} from '../../common/types/packet'; import { CanvasUtils } from './canvas-utils'; import { ChatBubble } from './chat-bubble'; const chatBuilder = new CanvasUtils(); export class PlayerEntity extends PonyEntity { - private uncommittedPacket: Packet = {}; + private _updateQueue: PositionUpdatePacket[] = []; + private _targetFrame: any = null; + private _lastFrame: any = null; 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) { super(); } @@ -84,18 +97,31 @@ export class PlayerEntity extends PonyEntity { ); } - public addUncommittedChanges(packet: Packet) { - this.uncommittedPacket = { ...this.uncommittedPacket, ...packet }; + public addUncommittedChanges(packet: PositionUpdatePacket) { + const appendix = { ...packet, time: 0.1 }; + this._updateQueue.push(appendix); + if (!this._targetFrame) { + this.setFromPacket(packet); + this._targetFrame = appendix; + } } public update(dt: number) { - this.commitServerUpdate(); + this.commitServerUpdate(dt); super.update(dt); } - private setFromPacket(packet: Packet) { - if (packet.velocity) { - this.setVelocity(new THREE.Vector3().fromArray(packet.velocity)); + private setFromPacket(packet: FullStatePacket | PositionUpdatePacket) { + if ((packet as FullStatePacket).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) { @@ -106,17 +132,47 @@ export class PlayerEntity extends PonyEntity { this.setRotation(new THREE.Euler().fromArray(packet.rotation)); } - if (packet.angular) { - this.setAngularVelocity(new THREE.Vector3().fromArray(packet.angular)); - } - if (packet.animState) { this.setWalkAnimationState(packet.animState); } } - private commitServerUpdate() { - this.setFromPacket(this.uncommittedPacket); - this.uncommittedPacket = {}; + private commitServerUpdate(dt: number) { + if (this._updateQueue.length == 0) { + 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); + } } } diff --git a/src/client/object/player.ts b/src/client/object/player.ts index 87a9da2..760b0ab 100644 --- a/src/client/object/player.ts +++ b/src/client/object/player.ts @@ -92,20 +92,24 @@ export class Player extends PonyEntity { } else if (this._wasTurning && !wasExternalForce) { this._wasTurning = false; this.angularVelocity.set(0, 0, 0); - this.changes.rotation = this.container.rotation.toArray(); this.changes.angular = this.angularVelocity.toArray(); } + this.changes.rotation = this.container.rotation.toArray(); + if (vector.y !== 0) { this.velocity.copy(this._lookVector.clone().multiplyScalar(vector.y)); this.changes.velocity = this.velocity.toArray(); this._wasMoving = true; + this.setWalkAnimationState(1); } else if (this._wasMoving && !wasExternalForce) { this._wasMoving = false; this.velocity.set(0, 0, 0); - this.changes.position = this.container.position.toArray(); this.changes.velocity = this.velocity.toArray(); + this.setWalkAnimationState(0); } + + this.changes.position = this.container.position.toArray(); } update(dt: number): void { diff --git a/src/client/object/pony.ts b/src/client/object/pony.ts index d392ce2..9a09a60 100644 --- a/src/client/object/pony.ts +++ b/src/client/object/pony.ts @@ -1,7 +1,7 @@ import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils'; import * as THREE from 'three'; import modelLoaderInstance from './pony-loader'; -import { Packet } from '../../common/types/packet'; +import { FullStatePacket } from '../../common/types/packet'; import { NameTag } from './nametag'; import { CanvasUtils } from './canvas-utils'; @@ -17,7 +17,7 @@ export class PonyEntity { public idleAction: THREE.AnimationAction; public walkAction: THREE.AnimationAction; public nameTag?: NameTag; - public changes: Packet = {}; + public changes: FullStatePacket = {}; initialize() { this.model = (SkeletonUtils as any).clone(modelLoaderInstance.ponyModel); @@ -39,12 +39,6 @@ export class PonyEntity { ).add(this.angularVelocity.clone().multiplyScalar(dt)), ); this.mixer.update(dt); - - if (this.velocity.length() > 0) { - this.setWalkAnimationState(1); - } else { - this.setWalkAnimationState(0); - } } public addNameTag(name: string) { diff --git a/src/common/types/packet.ts b/src/common/types/packet.ts index 5fc2c2f..dcf8605 100644 --- a/src/common/types/packet.ts +++ b/src/common/types/packet.ts @@ -1,6 +1,14 @@ 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[]; angular?: number[]; position?: number[]; @@ -8,4 +16,4 @@ export interface Packet { animState?: number; } -export interface CompositePacket extends IcyNetUser, Packet {} +export interface CompositePacket extends IcyNetUser, FullStatePacket {} diff --git a/src/server/object/game.ts b/src/server/object/game.ts index ffc0389..64244a2 100644 --- a/src/server/object/game.ts +++ b/src/server/object/game.ts @@ -1,7 +1,10 @@ import { Server, Socket } from 'socket.io'; -import { config } from '../config'; import { RequestHandler } from 'express'; import { IcyNetUser } from '../../common/types/user'; +import { + CompositePacket, + PositionUpdatePacket, +} from '../../common/types/packet'; const PLACEHOLDER_USER = (socket: Socket): IcyNetUser => { const randomName = `player-${socket.id.substring(0, 8)}`; @@ -16,6 +19,7 @@ const PLACEHOLDER_USER = (socket: Socket): IcyNetUser => { export class Game { private _connections: Socket[] = []; + private _changedPlayers: number[] = []; constructor(private io: Server, private session: RequestHandler) {} @@ -50,9 +54,9 @@ export class Game { socket.data.user = user; socket.data.playerinfo = { velocity: [0, 0, 0], - angular: [0, 0, 0], + angular: [0, 0, 0, 'XYZ'], position: [0, 0, 0], - rotation: [0, 0, 0], + rotation: [0, 0, 0, 'XYZ'], animState: 0, }; @@ -71,10 +75,10 @@ export class Game { ...socket.data.playerinfo, ...packet, }; - socket.broadcast.emit('playerupdate', { - id: user.id, - ...packet, - }); + + if (!this._changedPlayers.includes(socket.data.user.id)) { + this._changedPlayers.push(socket.data.user.id); + } }); 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); } }