Adding PlayersPositionInterpolator to interpolate/extrapolate players positions
This commit is contained in:
parent
d69ce8a6a6
commit
d72e60610e
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
});
|
||||
});
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user