lerp approach
This commit is contained in:
parent
e075590734
commit
c198306717
@ -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) => {
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
@ -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 {}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user