Adding PlayersPositionInterpolator to interpolate/extrapolate players positions

This commit is contained in:
David Négrier 2020-06-02 13:44:42 +02:00
parent d69ce8a6a6
commit d72e60610e
4 changed files with 97 additions and 14 deletions

View file

@ -13,6 +13,8 @@ import Sprite = Phaser.GameObjects.Sprite;
import CanvasTexture = Phaser.Textures.CanvasTexture;
import {AddPlayerInterface} from "./AddPlayerInterface";
import {PlayerAnimationNames} from "../Player/Animation";
import {PlayerMovement} from "./PlayerMovement";
import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator";
export enum Textures {
Player = "male1"
@ -37,6 +39,7 @@ export class GameScene extends Phaser.Scene {
startY = 32; // 1 case
circleTexture: CanvasTexture;
initPosition: PositionInterface;
private playersPositionInterpolator = new PlayersPositionInterpolator();
MapKey: string;
MapUrlFile: string;
@ -381,6 +384,17 @@ export class GameScene extends Phaser.Scene {
update(time: number, delta: number) : void {
this.currentTick = time;
this.CurrentPlayer.moveUser(delta);
// Let's move all users
let updatedPlayersPositions = this.playersPositionInterpolator.getUpdatedPositions(time);
updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: string) => {
let player : GamerInterface | undefined = this.MapPlayersByKey.get(userId);
if (player === undefined) {
throw new Error('Cannot find player with ID "' + userId +'"');
}
player.updatePosition(moveEvent);
});
let nextSceneKey = this.checkToExit();
if(nextSceneKey){
this.scene.start(nextSceneKey.key);
@ -424,15 +438,6 @@ export class GameScene extends Phaser.Scene {
});
}
private findPlayerInMap(UserId : string) : GamerInterface | null{
return this.MapPlayersByKey.get(UserId);
/*let player = this.MapPlayers.getChildren().find((player: Player) => UserId === player.userId);
if(!player){
return null;
}
return (player as GamerInterface);*/
}
/**
* Create new player
*/
@ -475,6 +480,7 @@ export class GameScene extends Phaser.Scene {
player.destroy();
this.MapPlayers.remove(player);
this.MapPlayersByKey.delete(userId);
this.playersPositionInterpolator.removePlayer(userId);
}
updatePlayerPosition(message: MessageUserMovedInterface): void {
@ -482,7 +488,11 @@ export class GameScene extends Phaser.Scene {
if (player === undefined) {
throw new Error('Cannot find player with ID "' + message.userId +'"');
}
player.updatePosition(message.position);
// We do not update the player position directly (because it is sent only every 200ms).
// Instead we use the PlayersPositionInterpolator that will do a smooth animation over the next 200ms.
let playerMovement = new PlayerMovement({ x: player.x, y: player.y }, this.currentTick, message.position, this.currentTick + POSITION_DELAY);
this.playersPositionInterpolator.updatePlayerPosition(player.userId, playerMovement);
}
shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface) {

View file

@ -1,15 +1,28 @@
import {HasMovedEvent} from "./GameManager";
import {MAX_EXTRAPOLATION_TIME} from "../../Enum/EnvironmentVariable";
import {PositionInterface} from "../../Connection";
export class PlayerMovement {
public constructor(private startPosition: HasMovedEvent, private startTick: number, private endPosition: HasMovedEvent, private endTick: number) {
public constructor(private startPosition: PositionInterface, private startTick: number, private endPosition: HasMovedEvent, private endTick: number) {
}
public isOutdated(tick: number): boolean {
//console.log(tick, this.endTick, MAX_EXTRAPOLATION_TIME)
// If the endPosition is NOT moving, no extrapolation needed.
if (this.endPosition.moving === false && tick > this.endTick) {
return true;
}
return tick > this.endTick + MAX_EXTRAPOLATION_TIME;
}
public getPosition(tick: number): HasMovedEvent {
// Special case: end position reached and end position is not moving
if (tick >= this.endTick && this.endPosition.moving === false) {
return this.endPosition;
}
let x = (this.endPosition.x - this.startPosition.x) * ((tick - this.startTick) / (this.endTick - this.startTick)) + this.startPosition.x;
let y = (this.endPosition.y - this.startPosition.y) * ((tick - this.startTick) / (this.endTick - this.startTick)) + this.startPosition.y;
@ -17,7 +30,7 @@ export class PlayerMovement {
x,
y,
direction: this.endPosition.direction,
moving: this.endPosition.moving
moving: true
}
}
}

View file

@ -2,6 +2,30 @@
* This class is in charge of computing the position of all players.
* Player movement is delayed by 200ms so position depends on ticks.
*/
export class PlayersPositionInterpolator {
import {PlayerMovement} from "./PlayerMovement";
import {HasMovedEvent} from "./GameManager";
export class PlayersPositionInterpolator {
playerMovements: Map<string, PlayerMovement> = new Map<string, PlayerMovement>();
updatePlayerPosition(userId: string, playerMovement: PlayerMovement) : void {
this.playerMovements.set(userId, playerMovement);
}
removePlayer(userId: string): void {
this.playerMovements.delete(userId);
}
getUpdatedPositions(tick: number) : Map<string, HasMovedEvent> {
let positions = new Map<string, HasMovedEvent>();
this.playerMovements.forEach((playerMovement: PlayerMovement, userId: string) => {
if (playerMovement.isOutdated(tick)) {
//console.log("outdated")
this.playerMovements.delete(userId);
}
//console.log("moving")
positions.set(userId, playerMovement.getPosition(tick))
});
return positions;
}
}

View file

@ -4,7 +4,7 @@ import {PlayerMovement} from "../../../src/Phaser/Game/PlayerMovement";
describe("Interpolation / Extrapolation", () => {
it("should interpolate", () => {
let playerMovement = new PlayerMovement({
x: 100, y: 200, moving: true, direction: "right"
x: 100, y: 200
}, 42000,
{
x: 200, y: 100, moving: true, direction: "up"
@ -37,4 +37,40 @@ describe("Interpolation / Extrapolation", () => {
moving: true
});
});
it("should not extrapolate if we stop", () => {
let playerMovement = new PlayerMovement({
x: 100, y: 200
}, 42000,
{
x: 200, y: 100, moving: false, direction: "up"
},
42200
);
expect(playerMovement.getPosition(42300)).toEqual({
x: 200,
y: 100,
direction: 'up',
moving: false
});
});
it("should should keep moving until it stops", () => {
let playerMovement = new PlayerMovement({
x: 100, y: 200
}, 42000,
{
x: 200, y: 100, moving: false, direction: "up"
},
42200
);
expect(playerMovement.getPosition(42100)).toEqual({
x: 150,
y: 150,
direction: 'up',
moving: true
});
});
})