Optimize performance
This commit is contained in:
parent
e4cd3284b7
commit
b8ef90c100
31 changed files with 319 additions and 206 deletions
|
|
@ -114,9 +114,8 @@ body {
|
|||
}
|
||||
|
||||
.player-tag {
|
||||
transform: translateX(-50%) translateY(-50%) rotate(-15deg);
|
||||
transition: left 150ms, top 150ms;
|
||||
border-radius: 1000px;
|
||||
transition: transform 200ms;
|
||||
|
||||
&.decla {
|
||||
color: $bright-decla;
|
||||
|
|
@ -163,8 +162,6 @@ body {
|
|||
|
||||
.ownership {
|
||||
font-size: 0;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
|
||||
box-shadow: inset 0 0 3px 0px rgba(0, 0, 0, 0.2);
|
||||
|
||||
@include square(50px);
|
||||
|
|
|
|||
|
|
@ -1,21 +1,11 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import {
|
||||
CommandGenerator,
|
||||
PrimaryActionCommand,
|
||||
SecondaryActionCommand,
|
||||
TernaryActionCommand,
|
||||
} from 'shared';
|
||||
import { CommandGenerator, SecondaryActionCommand, TernaryActionCommand } from 'shared';
|
||||
import { Game } from '../../game';
|
||||
|
||||
export class MouseListener extends CommandGenerator {
|
||||
constructor(target: HTMLElement, private readonly game: Game) {
|
||||
super();
|
||||
|
||||
target.addEventListener('mousemove', (event: MouseEvent) => {
|
||||
const position = this.positionFromEvent(event);
|
||||
this.sendCommandToSubcribers(new PrimaryActionCommand(position));
|
||||
});
|
||||
|
||||
target.addEventListener('mousedown', (event: MouseEvent) => {
|
||||
const position = this.positionFromEvent(event);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,15 @@ export class CommandReceiverSocket extends CommandReceiver {
|
|||
super();
|
||||
}
|
||||
|
||||
private commandQueue: Array<Command> = [];
|
||||
protected defaultCommandExecutor(command: Command) {
|
||||
this.socket.emit(TransportEvents.PlayerToServer, serialize(command));
|
||||
this.commandQueue.push(command);
|
||||
}
|
||||
|
||||
public sendQueuedCommands() {
|
||||
if (this.commandQueue.length > 0) {
|
||||
this.socket.emit(TransportEvents.PlayerToServer, serialize(this.commandQueue));
|
||||
this.commandQueue = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import { startAnimation } from './start-animation';
|
|||
import { PlayerDecision } from './join-form-handler';
|
||||
import { GameObjectContainer } from './objects/game-object-container';
|
||||
import { OptionsHandler } from './options-handler';
|
||||
import parser from 'socket.io-msgpack-parser';
|
||||
|
||||
export class Game extends CommandReceiver {
|
||||
public gameObjects = new GameObjectContainer(this);
|
||||
|
|
@ -43,6 +44,7 @@ export class Game extends CommandReceiver {
|
|||
private announcementText = document.createElement('h2');
|
||||
private progressBar = document.createElement('div');
|
||||
private arrowElements: Array<HTMLElement> = [];
|
||||
private socketReceiver!: CommandReceiverSocket;
|
||||
|
||||
constructor(
|
||||
private readonly playerDecision: PlayerDecision,
|
||||
|
|
@ -71,7 +73,8 @@ export class Game extends CommandReceiver {
|
|||
reconnectionDelayMax: 10000,
|
||||
transports: ['websocket'],
|
||||
forceNew: true,
|
||||
});
|
||||
parser,
|
||||
} as any);
|
||||
|
||||
this.socket.on('reconnect_attempt', () => {
|
||||
this.socket.io.opts.transports = ['polling', 'websocket'];
|
||||
|
|
@ -87,17 +90,19 @@ export class Game extends CommandReceiver {
|
|||
this.socket.emit(TransportEvents.Pong);
|
||||
});
|
||||
|
||||
this.socket.on(TransportEvents.ServerToPlayer, (serialized: string) =>
|
||||
this.sendCommand(deserialize(serialized)),
|
||||
);
|
||||
this.socket.on(TransportEvents.ServerToPlayer, (serializedCommands: string) => {
|
||||
const commands: Array<Command> = deserialize(serializedCommands);
|
||||
commands.forEach((c) => this.sendCommand(c));
|
||||
});
|
||||
|
||||
this.socketReceiver = new CommandReceiverSocket(this.socket);
|
||||
broadcastCommands(
|
||||
[
|
||||
new KeyboardListener(document.body),
|
||||
new MouseListener(this.canvas, this),
|
||||
new TouchJoystickListener(this.canvas, this.overlay, this),
|
||||
],
|
||||
[this.gameObjects, new CommandReceiverSocket(this.socket)],
|
||||
[this.socketReceiver],
|
||||
);
|
||||
|
||||
this.isBetweenGames = false;
|
||||
|
|
@ -111,29 +116,31 @@ export class Game extends CommandReceiver {
|
|||
this.gameObjects.sendCommand(c);
|
||||
}
|
||||
|
||||
private lastGameState?: UpdateGameState;
|
||||
|
||||
private lastAnnouncementText = '';
|
||||
protected commandExecutors: CommandExecutors = {
|
||||
[ServerAnnouncement.type]: (c: ServerAnnouncement) =>
|
||||
(this.announcementText.innerText = c.text),
|
||||
(this.lastAnnouncementText = c.text),
|
||||
[PlayerDiedCommand.type]: (c: PlayerDiedCommand) => {
|
||||
if (OptionsHandler.options.vibrationEnabled) {
|
||||
navigator.vibrate(150);
|
||||
}
|
||||
},
|
||||
[UpdateGameState.type]: (c: UpdateGameState) => {
|
||||
this.declaPlanetCountElement.style.width = (c.declaCount / c.limit) * 50 + '%';
|
||||
this.redPlanetCountElement.style.width = (c.redCount / c.limit) * 50 + '%';
|
||||
},
|
||||
[UpdateGameState.type]: (c: UpdateGameState) => (this.lastGameState = c),
|
||||
[GameEnd.type]: (c: GameEnd) => {
|
||||
const team =
|
||||
c.winningTeam === CharacterTeam.decla
|
||||
? '<span class="decla">decla</span>'
|
||||
: '<span class="red">red</span>';
|
||||
this.announcementText.innerHTML = `Team ${team} won 🎉`;
|
||||
this.lastAnnouncementText = `Team ${team} won 🎉`;
|
||||
},
|
||||
[UpdateOtherPlayerDirections.type]: this.handleOtherPlayerDirections.bind(this),
|
||||
[UpdateOtherPlayerDirections.type]: (c: UpdateOtherPlayerDirections) =>
|
||||
(this.lastOtherPlayerDirections = c),
|
||||
[GameStart.type]: this.initialize.bind(this),
|
||||
};
|
||||
|
||||
private lastOtherPlayerDirections?: UpdateOtherPlayerDirections;
|
||||
private handleOtherPlayerDirections(command: UpdateOtherPlayerDirections) {
|
||||
this.arrowElements
|
||||
.splice(command.otherPlayerDirections.length, this.arrowElements.length)
|
||||
|
|
@ -205,10 +212,7 @@ export class Game extends CommandReceiver {
|
|||
}
|
||||
|
||||
public aspectRatioChanged(aspectRatio: number) {
|
||||
this.socket.emit(
|
||||
TransportEvents.PlayerToServer,
|
||||
serialize(new SetAspectRatioActionCommand(aspectRatio)),
|
||||
);
|
||||
this.socketReceiver.sendCommand(new SetAspectRatioActionCommand(aspectRatio));
|
||||
}
|
||||
|
||||
private isActive = true;
|
||||
|
|
@ -216,6 +220,7 @@ export class Game extends CommandReceiver {
|
|||
this.isActive = false;
|
||||
}
|
||||
|
||||
private framesSinceLastLayoutUpdate = 0;
|
||||
private gameLoop(
|
||||
renderer: Renderer,
|
||||
_: DOMHighResTimeStamp,
|
||||
|
|
@ -224,10 +229,34 @@ export class Game extends CommandReceiver {
|
|||
this.resolveStarted();
|
||||
deltaTime /= 1000;
|
||||
|
||||
let shouldChangeLayout = false;
|
||||
if (++this.framesSinceLastLayoutUpdate > 1) {
|
||||
shouldChangeLayout = true;
|
||||
this.framesSinceLastLayoutUpdate = 0;
|
||||
this.draw();
|
||||
}
|
||||
|
||||
this.renderer = renderer;
|
||||
|
||||
this.socketReceiver.sendQueuedCommands();
|
||||
this.gameObjects.stepObjects(deltaTime);
|
||||
this.gameObjects.drawObjects(this.renderer, this.overlay);
|
||||
this.gameObjects.drawObjects(this.renderer, this.overlay, shouldChangeLayout);
|
||||
|
||||
return this.isActive;
|
||||
}
|
||||
|
||||
private draw() {
|
||||
if (this.lastGameState) {
|
||||
this.declaPlanetCountElement.style.width =
|
||||
(this.lastGameState.declaCount / this.lastGameState.limit) * 50 + '%';
|
||||
this.redPlanetCountElement.style.width =
|
||||
(this.lastGameState.redCount / this.lastGameState.limit) * 50 + '%';
|
||||
}
|
||||
|
||||
if (this.lastOtherPlayerDirections) {
|
||||
this.handleOtherPlayerDirections(this.lastOtherPlayerDirections);
|
||||
}
|
||||
|
||||
this.announcementText.innerHTML = this.lastAnnouncementText;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import { ServerInformation, serverInformationEndpoint, TransportEvents } from 'shared';
|
||||
import io from 'socket.io-client';
|
||||
import { Configuration } from './config/configuration';
|
||||
import parser from 'socket.io-msgpack-parser';
|
||||
|
||||
export type PlayerDecision = {
|
||||
playerName: string;
|
||||
server: string;
|
||||
};
|
||||
|
||||
const pollInterval = 10000;
|
||||
const pollInterval = 8000;
|
||||
export class JoinFormHandler {
|
||||
private waitingForDecision: Promise<PlayerDecision>;
|
||||
private resolvePlayerDecision!: (d: PlayerDecision) => void;
|
||||
|
|
@ -122,8 +123,9 @@ class ServerChooserOption {
|
|||
|
||||
this.socket = io(url, {
|
||||
reconnection: false,
|
||||
timeout: 1500,
|
||||
});
|
||||
timeout: 4000,
|
||||
parser,
|
||||
} as any);
|
||||
|
||||
this.socket.on('connect_error', this.destroy.bind(this));
|
||||
this.socket.on('connect_timeout', this.destroy.bind(this));
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export class Camera extends GameObject implements ViewObject {
|
|||
|
||||
public step(deltaTimeInSeconds: number): void {}
|
||||
|
||||
public draw(renderer: Renderer, overlay: HTMLElement) {
|
||||
public draw(renderer: Renderer, overlay: HTMLElement, shouldChangeLayout: boolean) {
|
||||
const canvasAspectRatio = renderer.canvasSize.x / renderer.canvasSize.y;
|
||||
if (canvasAspectRatio !== this.aspectRatio) {
|
||||
this.aspectRatio = canvasAspectRatio;
|
||||
|
|
|
|||
|
|
@ -56,8 +56,12 @@ export class GameObjectContainer extends CommandReceiver {
|
|||
this.objects.forEach((o) => o.step(deltaTimeInSeconds));
|
||||
}
|
||||
|
||||
public drawObjects(renderer: Renderer, overlay: HTMLElement) {
|
||||
this.objects.forEach((o) => o.draw(renderer, overlay));
|
||||
public drawObjects(
|
||||
renderer: Renderer,
|
||||
overlay: HTMLElement,
|
||||
shouldChangeLayout: boolean,
|
||||
) {
|
||||
this.objects.forEach((o) => o.draw(renderer, overlay, shouldChangeLayout));
|
||||
}
|
||||
|
||||
private addObject(object: ViewObject) {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,11 @@ export class LampView extends LampBase implements ViewObject {
|
|||
|
||||
public beforeDestroy(): void {}
|
||||
|
||||
public draw(renderer: Renderer, overlay: HTMLElement): void {
|
||||
public draw(
|
||||
renderer: Renderer,
|
||||
overlay: HTMLElement,
|
||||
shouldChangeLayout: boolean,
|
||||
): void {
|
||||
renderer.addDrawable(this.light);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,31 +47,76 @@ export class PlanetView extends PlanetBase implements ViewObject {
|
|||
vec2.scale(vec2.create(), p.velocity, deltaTimeInSeconds),
|
||||
);
|
||||
|
||||
if ((p.timeToLive -= deltaTimeInSeconds) <= 0) {
|
||||
p.element.parentElement?.removeChild(p.element);
|
||||
} else {
|
||||
p.element.style.opacity = Math.min(1, p.timeToLive).toString();
|
||||
}
|
||||
p.timeToLive -= deltaTimeInSeconds;
|
||||
});
|
||||
|
||||
this.generatedPointElements = this.generatedPointElements.filter(
|
||||
(p) => p.timeToLive > 0,
|
||||
);
|
||||
}
|
||||
|
||||
private generatedPointElements: Array<FallingPoint> = [];
|
||||
|
||||
private lastGeneratedPoint?: number;
|
||||
public generatedPoints(value: number) {
|
||||
const element = document.createElement('div');
|
||||
element.className = 'falling-point ' + (this.ownership < 0.5 ? 'decla' : 'red');
|
||||
element.innerText = '+' + value;
|
||||
this.generatedPointElements.push({
|
||||
element,
|
||||
addedToOverlay: false,
|
||||
timeToLive: Random.getRandomInRange(2, 3),
|
||||
position: vec2.create(),
|
||||
velocity: vec2.fromValues(Random.getRandomInRange(-30, 30), 0),
|
||||
});
|
||||
this.lastGeneratedPoint = value;
|
||||
}
|
||||
|
||||
public beforeDestroy(): void {
|
||||
this.ownershipProgess.parentElement?.removeChild(this.ownershipProgess);
|
||||
this.generatedPointElements.forEach((p) =>
|
||||
p.element.parentElement?.removeChild(p.element),
|
||||
);
|
||||
}
|
||||
|
||||
public draw(
|
||||
renderer: Renderer,
|
||||
overlay: HTMLElement,
|
||||
shouldChangeLayout: boolean,
|
||||
): void {
|
||||
if (shouldChangeLayout) {
|
||||
if (!this.ownershipProgess.parentElement) {
|
||||
overlay.appendChild(this.ownershipProgess);
|
||||
}
|
||||
|
||||
const screenPosition = renderer.worldToDisplayCoordinates(this.center);
|
||||
|
||||
this.generatedPointElements.forEach((p) => {
|
||||
if (!p.addedToOverlay) {
|
||||
overlay.appendChild(p.element);
|
||||
}
|
||||
|
||||
p.element.style.transform = `translateX(${
|
||||
screenPosition.x + p.position.x
|
||||
}px) translateY(${screenPosition.y + p.position.y}px)`;
|
||||
|
||||
if (p.timeToLive <= 0) {
|
||||
p.element.parentElement?.removeChild(p.element);
|
||||
} else {
|
||||
p.element.style.opacity = Math.min(1, p.timeToLive).toString();
|
||||
}
|
||||
});
|
||||
|
||||
this.generatedPointElements = this.generatedPointElements.filter(
|
||||
(p) => p.timeToLive > 0,
|
||||
);
|
||||
|
||||
this.ownershipProgess.style.transform = `translateX(${screenPosition.x}px) translateY(${screenPosition.y}px) translateX(-50%) translateY(-50%)`;
|
||||
this.ownershipProgess.style.background = this.getGradient();
|
||||
|
||||
if (this.lastGeneratedPoint !== undefined) {
|
||||
const element = document.createElement('div');
|
||||
element.className = 'falling-point ' + (this.ownership < 0.5 ? 'decla' : 'red');
|
||||
element.innerText = '+' + this.lastGeneratedPoint;
|
||||
this.generatedPointElements.push({
|
||||
element,
|
||||
addedToOverlay: false,
|
||||
timeToLive: Random.getRandomInRange(2, 3),
|
||||
position: vec2.create(),
|
||||
velocity: vec2.fromValues(Random.getRandomInRange(-30, 30), 0),
|
||||
});
|
||||
|
||||
this.lastGeneratedPoint = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
renderer.addDrawable(this.shape);
|
||||
}
|
||||
|
||||
private getGradient(): string {
|
||||
|
|
@ -91,33 +136,4 @@ export class PlanetView extends PlanetBase implements ViewObject {
|
|||
var(--bright-red) 100%
|
||||
)`;
|
||||
}
|
||||
public beforeDestroy(): void {
|
||||
this.ownershipProgess.parentElement?.removeChild(this.ownershipProgess);
|
||||
this.generatedPointElements.forEach((p) =>
|
||||
p.element.parentElement?.removeChild(p.element),
|
||||
);
|
||||
}
|
||||
|
||||
public draw(renderer: Renderer, overlay: HTMLElement): void {
|
||||
if (!this.ownershipProgess.parentElement) {
|
||||
overlay.appendChild(this.ownershipProgess);
|
||||
}
|
||||
|
||||
const screenPosition = renderer.worldToDisplayCoordinates(this.center);
|
||||
|
||||
this.generatedPointElements.forEach((p) => {
|
||||
if (!p.addedToOverlay) {
|
||||
overlay.appendChild(p.element);
|
||||
}
|
||||
|
||||
p.element.style.left = screenPosition.x + p.position.x + 'px';
|
||||
p.element.style.top = screenPosition.y + p.position.y + 'px';
|
||||
});
|
||||
|
||||
this.ownershipProgess.style.left = screenPosition.x + 'px';
|
||||
this.ownershipProgess.style.top = screenPosition.y + 'px';
|
||||
this.ownershipProgess.style.background = this.getGradient();
|
||||
|
||||
renderer.addDrawable(this.shape);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,8 +49,6 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
|
|||
}
|
||||
|
||||
public step(deltaTimeInSeconds: number): void {
|
||||
this.healthElement.style.width = (50 * this.health) / settings.playerMaxHealth + 'px';
|
||||
this.statsElement.innerText = this.getStatsText();
|
||||
if (this.previousHealth > this.health) {
|
||||
this.previousHealth = this.health;
|
||||
if (OptionsHandler.options.vibrationEnabled) {
|
||||
|
|
@ -68,16 +66,26 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
|
|||
this.nameElement.parentElement?.removeChild(this.nameElement);
|
||||
}
|
||||
|
||||
public draw(renderer: Renderer, overlay: HTMLElement): void {
|
||||
if (!this.nameElement.parentElement) {
|
||||
overlay.appendChild(this.nameElement);
|
||||
}
|
||||
public draw(
|
||||
renderer: Renderer,
|
||||
overlay: HTMLElement,
|
||||
shouldChangeLayout: boolean,
|
||||
): void {
|
||||
if (shouldChangeLayout) {
|
||||
if (!this.nameElement.parentElement) {
|
||||
overlay.appendChild(this.nameElement);
|
||||
}
|
||||
|
||||
const screenPosition = renderer.worldToDisplayCoordinates(
|
||||
this.calculateTextPosition(),
|
||||
);
|
||||
this.nameElement.style.left = screenPosition.x + 'px';
|
||||
this.nameElement.style.top = screenPosition.y + 'px';
|
||||
const screenPosition = renderer.worldToDisplayCoordinates(
|
||||
this.calculateTextPosition(),
|
||||
);
|
||||
|
||||
this.nameElement.style.transform = `translateX(${screenPosition.x}px) translateY(${screenPosition.y}px) translateX(-50%) translateY(-50%) rotate(-15deg)`;
|
||||
|
||||
this.healthElement.style.width =
|
||||
(50 * this.health) / settings.playerMaxHealth + 'px';
|
||||
this.statsElement.innerText = this.getStatsText();
|
||||
}
|
||||
|
||||
this.shape.setCircles([this.head!, this.leftFoot!, this.rightFoot!]);
|
||||
renderer.addDrawable(this.shape);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,11 @@ export class ProjectileView extends ProjectileBase implements ViewObject {
|
|||
|
||||
public beforeDestroy(): void {}
|
||||
|
||||
public draw(renderer: Renderer, overlay: HTMLElement): void {
|
||||
public draw(
|
||||
renderer: Renderer,
|
||||
overlay: HTMLElement,
|
||||
shouldChangeLayout: boolean,
|
||||
): void {
|
||||
renderer.addDrawable(this.circle);
|
||||
renderer.addDrawable(this.light);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@ import { GameObject } from 'shared';
|
|||
|
||||
export interface ViewObject extends GameObject {
|
||||
step(deltaTimeInMilliseconds: number): void;
|
||||
draw(renderer: Renderer, overlay: HTMLElement): void;
|
||||
draw(renderer: Renderer, overlay: HTMLElement, shouldChangeLayout: boolean): void;
|
||||
beforeDestroy(): void;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue