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) => {
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) => {

View File

@ -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);
}
}
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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 {}

View File

@ -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);
}
}