Improve multiplayer

This commit is contained in:
schmelczerandras 2020-10-08 13:16:05 +02:00 committed by Schmelczer András
parent 220b20476f
commit 37954e2ef1
29 changed files with 321 additions and 267 deletions

View file

@ -10,15 +10,17 @@ export class KeyboardListener extends CommandGenerator {
target.addEventListener('keydown', (event: KeyboardEvent) => {
const key = this.normalize(event.key);
this.keysDown.add(key);
this.generateCommands();
});
target.addEventListener('keyup', (event: KeyboardEvent) => {
const key = this.normalize(event.key);
this.keysDown.delete(key);
this.generateCommands();
});
}
public generateCommands() {
private generateCommands() {
const up = ~~(
this.keysDown.has('w') ||
this.keysDown.has('arrowup') ||
@ -31,8 +33,8 @@ export class KeyboardListener extends CommandGenerator {
const movement = vec2.fromValues(right - left, up - down);
if (vec2.squaredLength(movement) > 0) {
vec2.normalize(movement, movement);
this.sendCommandToSubcribers(new MoveActionCommand(movement));
}
this.sendCommandToSubcribers(new MoveActionCommand(movement));
}
private normalize(key: string): string {

View file

@ -9,9 +9,6 @@ import {
export class TouchListener extends CommandGenerator {
private previousPosition = vec2.create();
private currentPosition = vec2.create();
private previousDeltas: Array<vec2> = [];
constructor(target: HTMLElement) {
super();
@ -22,7 +19,6 @@ export class TouchListener extends CommandGenerator {
const touchCount = event.touches.length;
const position = this.positionFromEvent(event);
this.previousPosition = position;
this.currentPosition = position;
if (touchCount == 1) {
this.sendCommandToSubcribers(new PrimaryActionCommand(position));
@ -37,24 +33,20 @@ export class TouchListener extends CommandGenerator {
event.preventDefault();
const position = this.positionFromEvent(event);
this.previousDeltas.push(
vec2.subtract(vec2.create(), position, this.previousPosition),
);
this.currentPosition = position;
});
}
const movement = vec2.subtract(vec2.create(), position, this.previousPosition);
public generateCommands() {
const movement = vec2.subtract(
vec2.create(),
this.currentPosition,
this.previousPosition,
);
this.previousPosition = this.currentPosition;
if (vec2.squaredLength(movement) > 0) {
vec2.normalize(movement, movement);
this.sendCommandToSubcribers(new MoveActionCommand(movement));
}
if (vec2.squaredLength(movement) > 0) {
vec2.normalize(movement, movement);
this.sendCommandToSubcribers(new MoveActionCommand(movement));
}
this.previousPosition = position;
});
target.addEventListener('touchend', (event: TouchEvent) => {
event.preventDefault();
this.sendCommandToSubcribers(new MoveActionCommand(vec2.create()));
});
}
private positionFromEvent(event: TouchEvent): vec2 {

View file

@ -13,12 +13,13 @@ import {
import {
broadcastCommands,
deserialize,
prettyPrint,
serialize,
settings,
SetViewAreaActionCommand,
StepCommand,
TransportEvents,
} from 'shared';
import { SetAspectRatioActionCommand } from 'shared/src/main';
import io from 'socket.io-client';
import { KeyboardListener } from './commands/generators/keyboard-listener';
import { MouseListener } from './commands/generators/mouse-listener';
@ -33,14 +34,14 @@ import { GameObjectContainer } from './objects/game-object-container';
import { BlobShape } from './shapes/blob-shape';
export class Game {
public readonly gameObjects = new GameObjectContainer();
private readonly canvas: HTMLCanvasElement = document.querySelector('canvas#main');
private renderer: Renderer;
private socket: SocketIOClient.Socket;
public readonly gameObjects = new GameObjectContainer(this);
private readonly canvas: HTMLCanvasElement = document.querySelector(
'canvas#main',
) as HTMLCanvasElement;
private renderer!: Renderer;
private socket!: SocketIOClient.Socket;
private deltaTimeCalculator = new DeltaTimeCalculator();
private overlay: HTMLElement = document.querySelector('#overlay');
private keyboardListener: KeyboardListener;
private touchListener: TouchListener;
private overlay: HTMLElement = document.querySelector('#overlay') as HTMLDivElement;
private async setupCommunication(): Promise<void> {
await Configuration.initialize();
@ -56,17 +57,21 @@ export class Game {
this.socket.on(TransportEvents.ServerToPlayer, (serialized: string) => {
const command = deserialize(serialized);
//console.log(command);
this.gameObjects.sendCommand(command);
});
this.socket.on(TransportEvents.Ping, () => {
this.socket.emit(TransportEvents.Pong);
});
this.socket.emit(TransportEvents.PlayerJoining, null);
this.keyboardListener = new KeyboardListener(document.body);
this.touchListener = new TouchListener(this.canvas);
broadcastCommands(
[this.keyboardListener, new MouseListener(this.canvas), this.touchListener],
[
new KeyboardListener(document.body),
new MouseListener(this.canvas),
new TouchListener(this.canvas),
],
[this.gameObjects, new CommandReceiverSocket(this.socket)],
);
}
@ -140,36 +145,21 @@ export class Game {
return this.renderer.displayToWorldCoordinates(p);
}
public aspectRatioChanged(aspectRatio: number) {
this.socket.emit(
TransportEvents.PlayerToServer,
serialize(new SetAspectRatioActionCommand(aspectRatio)),
);
}
private gameLoop(time: DOMHighResTimeStamp) {
const deltaTime = this.deltaTimeCalculator.getNextDeltaTimeInMilliseconds(time);
this.keyboardListener.generateCommands();
this.touchListener.generateCommands();
if (this.gameObjects.camera) {
// todo: Should only send aspect ratio
this.socket.emit(
TransportEvents.PlayerToServer,
serialize(new SetViewAreaActionCommand(this.gameObjects.camera.viewArea)),
);
}
this.gameObjects.sendCommand(new StepCommand(deltaTime));
/*this.camera.sendCommand(new MoveToCommand(this.character.position));
this.camera.sendCommand(new RenderCommand(this.renderer));*/
/*const shouldBeDrawn = this.physics
.findIntersecting(this.camera.viewArea)
.map((b) => b.owner);
for (const object of shouldBeDrawn) {
object?.sendCommand(new RenderCommand(this.renderer));
}*/
this.gameObjects.sendCommand(new RenderCommand(this.renderer) as any);
this.gameObjects.sendCommand(new RenderCommand(this.renderer));
this.renderer.renderDrawables();
//this.overlay.innerText = prettyPrint(this.renderer.insights);
this.overlay.innerText = prettyPrint(this.renderer.insights);
requestAnimationFrame(this.gameLoop.bind(this));
}
}

View file

@ -1,36 +1,29 @@
import { vec2 } from 'gl-matrix';
import { CommandExecutors, GameObject, Rectangle, settings } from 'shared';
import { calculateViewArea, CommandExecutors, GameObject } from 'shared';
import { RenderCommand } from '../commands/types/render';
import { Game } from '../game';
export class Camera extends GameObject {
private static readonly inViewAreaSize = settings.inViewAreaSize;
private _viewArea = new Rectangle();
public center: vec2 = vec2.create();
private aspectRatio?: number;
protected commandExecutors: CommandExecutors = {
[RenderCommand.type]: this.draw.bind(this),
};
constructor(public center: vec2 = vec2.create()) {
constructor(private game: Game) {
super(null);
}
public get viewArea(): Rectangle {
return this._viewArea;
}
private draw(c: RenderCommand) {
const canvasAspectRatio = c.renderer.canvasSize.x / c.renderer.canvasSize.y;
if (canvasAspectRatio !== this.aspectRatio) {
this.aspectRatio = canvasAspectRatio;
this.game.aspectRatioChanged(canvasAspectRatio);
}
this._viewArea.topLeft = vec2.fromValues(
this.center.x - this._viewArea.size.x / 2,
this.center.y + this._viewArea.size.y / 2,
);
this._viewArea.size = vec2.fromValues(
Math.sqrt(Camera.inViewAreaSize * canvasAspectRatio),
Math.sqrt(Camera.inViewAreaSize / canvasAspectRatio),
);
c.renderer.setViewArea(this._viewArea.topLeft, this._viewArea.size);
const viewArea = calculateViewArea(this.center, canvasAspectRatio);
c.renderer.setViewArea(viewArea.topLeft, viewArea.size);
}
}

View file

@ -10,6 +10,7 @@ import {
StepCommand,
UpdateObjectsCommand,
} from 'shared';
import { Game } from '../game';
import { Camera } from './camera';
import { CharacterView } from './character-view';
@ -21,7 +22,7 @@ export class GameObjectContainer extends CommandReceiver {
protected commandExecutors: CommandExecutors = {
[CreatePlayerCommand.type]: (c: CreatePlayerCommand) => {
this.player = c.character as CharacterView;
this.camera = new Camera();
this.camera = new Camera(this.game);
this.addObject(this.player);
this.addObject(this.camera);
},
@ -49,6 +50,10 @@ export class GameObjectContainer extends CommandReceiver {
},
};
constructor(private game: Game) {
super();
}
protected defaultCommandExecutor(c: Command) {
this.objects.forEach((o) => o.sendCommand(c));
}