Add input handling
This commit is contained in:
parent
29c6b14440
commit
d5be727040
36 changed files with 417 additions and 113 deletions
|
|
@ -19,3 +19,7 @@ A good-looking 2D adventure.
|
||||||
- lightweight object storage
|
- lightweight object storage
|
||||||
- 502 error page for ingress
|
- 502 error page for ingress
|
||||||
- vs code glsl formatter
|
- vs code glsl formatter
|
||||||
|
- docker engine dashboard?
|
||||||
|
- local dev env
|
||||||
|
- vs code config
|
||||||
|
- prettier script
|
||||||
|
|
|
||||||
5
frontend/.vscode/settings.json
vendored
Normal file
5
frontend/.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"typescript.tsdk": "node_modules\\typescript\\lib",
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
}
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
"ts-loader": "^8.0.1",
|
"ts-loader": "^8.0.1",
|
||||||
"twgl.js": "^4.15.2",
|
"twgl.js": "^4.15.2",
|
||||||
"typescript": "^3.8.3",
|
"typescript": "^3.8.3",
|
||||||
|
"uuid": "^8.2.0",
|
||||||
"webpack": "^4.43.0",
|
"webpack": "^4.43.0",
|
||||||
"webpack-cli": "^3.3.11",
|
"webpack-cli": "^3.3.11",
|
||||||
"webpack-dev-server": "^3.10.3"
|
"webpack-dev-server": "^3.10.3"
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,14 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" />
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width,initial-scale=1,viewport-fit=cover"
|
||||||
|
/>
|
||||||
<meta name="theme-color" content="#b7455e" />
|
<meta name="theme-color" content="#b7455e" />
|
||||||
|
|
||||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
|
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
||||||
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||||
|
|
||||||
<title>Test</title>
|
<title>Test</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import './styling/main.scss';
|
import './styles/main.scss';
|
||||||
import { main } from './scripting/main';
|
import { main } from './scripts/main';
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
export class KeyboardListener {
|
|
||||||
private keysDown: Set<string> = new Set();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
document.addEventListener('keydown', (event) => {
|
|
||||||
this.keysDown.add(event.key);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('keyup', (event) => {
|
|
||||||
this.keysDown.delete(event.key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
isKeyDown(key: string): boolean {
|
|
||||||
return (
|
|
||||||
this.keysDown.has(key) ||
|
|
||||||
this.keysDown.has(key.toLowerCase()) ||
|
|
||||||
this.keysDown.has(key.toUpperCase())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import { Renderer } from './drawing/renderer';
|
|
||||||
import { KeyboardListener } from './helper/keyboard-listener';
|
|
||||||
|
|
||||||
import passthroughVertexShader from '../shaders/passthrough.vert';
|
|
||||||
import distanceFragmentShader from '../shaders/dist.frag';
|
|
||||||
|
|
||||||
export const main = async () => {
|
|
||||||
try {
|
|
||||||
const canvas: HTMLCanvasElement = document.querySelector('canvas#main');
|
|
||||||
|
|
||||||
const renderer = new Renderer(canvas, [
|
|
||||||
passthroughVertexShader,
|
|
||||||
distanceFragmentShader,
|
|
||||||
]);
|
|
||||||
|
|
||||||
renderer.start();
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
export interface Vec2 {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import { Line } from './line';
|
|
||||||
|
|
||||||
export class WorldMap {
|
|
||||||
private lines: Array<Line> = [];
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
const delta = 0.01;
|
|
||||||
for (let i = delta; i < 30; i += delta) {
|
|
||||||
const linePoints = {
|
|
||||||
a: { x: i - delta, y: Math.sin(i - delta) },
|
|
||||||
b: { x: i, y: Math.sin(i) },
|
|
||||||
};
|
|
||||||
/*Vec2 tangent = lines[i].b - lines[i].a;
|
|
||||||
lines[i].normal = vec2(-lines[i].normal.x * tangent.y, lines[i].normal.x * tangent.x);
|
|
||||||
linePoints.normal = */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
13
frontend/src/scripts/commands/command-broadcaster.ts
Normal file
13
frontend/src/scripts/commands/command-broadcaster.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { CommandReceiver } from './command-receiver';
|
||||||
|
import { CommandGenerator } from './command-generator';
|
||||||
|
|
||||||
|
export class CommandBroadcaster {
|
||||||
|
constructor(
|
||||||
|
commandGenerators: Array<CommandGenerator>,
|
||||||
|
commandReceivers: Array<CommandReceiver>
|
||||||
|
) {
|
||||||
|
commandReceivers.forEach((r) =>
|
||||||
|
commandGenerators.forEach((g) => g.subscribe(r))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
frontend/src/scripts/commands/command-generator.ts
Normal file
14
frontend/src/scripts/commands/command-generator.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { CommandReceiver } from './command-receiver';
|
||||||
|
import { Command } from './command';
|
||||||
|
|
||||||
|
export class CommandGenerator {
|
||||||
|
private subscribers: Array<CommandReceiver> = [];
|
||||||
|
|
||||||
|
public subscribe(subscriber: CommandReceiver): void {
|
||||||
|
this.subscribers.push(subscriber);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected sendCommand(command: Command): void {
|
||||||
|
this.subscribers.forEach((s) => s.sendCommand(command));
|
||||||
|
}
|
||||||
|
}
|
||||||
5
frontend/src/scripts/commands/command-receiver.ts
Normal file
5
frontend/src/scripts/commands/command-receiver.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { Command } from './command';
|
||||||
|
|
||||||
|
export interface CommandReceiver {
|
||||||
|
sendCommand(command: Command): void;
|
||||||
|
}
|
||||||
3
frontend/src/scripts/commands/command.ts
Normal file
3
frontend/src/scripts/commands/command.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { Typed } from '../transport/serializable';
|
||||||
|
|
||||||
|
export abstract class Command extends Typed {}
|
||||||
7
frontend/src/scripts/commands/types/key-down.ts
Normal file
7
frontend/src/scripts/commands/types/key-down.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { Command } from '../command';
|
||||||
|
|
||||||
|
export class KeyDownCommand extends Command {
|
||||||
|
public constructor(public readonly key: string) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
7
frontend/src/scripts/commands/types/key-up.ts
Normal file
7
frontend/src/scripts/commands/types/key-up.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { Command } from '../command';
|
||||||
|
|
||||||
|
export class KeyUpCommand extends Command {
|
||||||
|
public constructor(public readonly key: string) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
8
frontend/src/scripts/commands/types/primary-action.ts
Normal file
8
frontend/src/scripts/commands/types/primary-action.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Command } from '../command';
|
||||||
|
import { Vec2 } from '../../math/vec2';
|
||||||
|
|
||||||
|
export class PrimaryActionCommand extends Command {
|
||||||
|
public constructor(public readonly position: Vec2) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
8
frontend/src/scripts/commands/types/secondary-action.ts
Normal file
8
frontend/src/scripts/commands/types/secondary-action.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Command } from '../command';
|
||||||
|
import { Vec2 } from '../../math/vec2';
|
||||||
|
|
||||||
|
export class SecondaryActionCommand extends Command {
|
||||||
|
public constructor(public readonly position: Vec2) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
8
frontend/src/scripts/commands/types/swipe.ts
Normal file
8
frontend/src/scripts/commands/types/swipe.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Command } from '../command';
|
||||||
|
import { Vec2 } from '../../math/vec2';
|
||||||
|
|
||||||
|
export class SwipeCommand extends Command {
|
||||||
|
public constructor(public readonly delta: Vec2) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
import * as twgl from 'twgl.js';
|
const twgl = require('twgl.js');
|
||||||
|
|
||||||
import { TimeIt } from '../helper/timing';
|
import { TimeIt } from '../helper/timing';
|
||||||
import { KeyboardListener } from '../helper/keyboard-listener';
|
import { Vec2 } from '../math/vec2';
|
||||||
import { Vec2 } from '../shared/vec2';
|
import { ObjectContainer } from '../objects/object-container';
|
||||||
|
|
||||||
export class Renderer {
|
export class Renderer {
|
||||||
private gl: WebGL2RenderingContext;
|
private gl: WebGL2RenderingContext;
|
||||||
private programInfo: any;
|
private programInfo: any;
|
||||||
private bufferInfo: any;
|
private bufferInfo: any;
|
||||||
private vao: any;
|
private vao: any;
|
||||||
private cameraPosition: Vec2 = { x: 0, y: 0 };
|
|
||||||
private keys: KeyboardListener = new KeyboardListener();
|
|
||||||
|
|
||||||
constructor(private canvas: HTMLCanvasElement, shaderSources: Array<string>) {
|
constructor(
|
||||||
|
private canvas: HTMLCanvasElement,
|
||||||
|
private objects: ObjectContainer,
|
||||||
|
shaderSources: Array<string>
|
||||||
|
) {
|
||||||
twgl.setDefaults({ attribPrefix: 'a_' });
|
twgl.setDefaults({ attribPrefix: 'a_' });
|
||||||
|
|
||||||
this.gl = this.canvas.getContext('webgl2');
|
this.gl = this.canvas.getContext('webgl2');
|
||||||
|
|
@ -39,13 +42,7 @@ export class Renderer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
public render(time: number) {
|
||||||
requestAnimationFrame(this.timedRender);
|
|
||||||
}
|
|
||||||
|
|
||||||
private timedRender = TimeIt(this.render.bind(this), 240);
|
|
||||||
|
|
||||||
private render(time: number) {
|
|
||||||
const gl = this.gl;
|
const gl = this.gl;
|
||||||
|
|
||||||
twgl.resizeCanvasToDisplaySize(this.canvas);
|
twgl.resizeCanvasToDisplaySize(this.canvas);
|
||||||
|
|
@ -53,24 +50,8 @@ export class Renderer {
|
||||||
|
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
if (this.keys.isKeyDown('w')) {
|
|
||||||
this.cameraPosition.y += 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.keys.isKeyDown('s')) {
|
|
||||||
this.cameraPosition.y -= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.keys.isKeyDown('a')) {
|
|
||||||
this.cameraPosition.x -= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.keys.isKeyDown('d')) {
|
|
||||||
this.cameraPosition.x += 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uniforms = {
|
const uniforms = {
|
||||||
cameraPosition: [this.cameraPosition.x, this.cameraPosition.y],
|
cameraPosition: this.objects.camera.position.list,
|
||||||
viewBoxSize: [this.gl.canvas.width, this.gl.canvas.height],
|
viewBoxSize: [this.gl.canvas.width, this.gl.canvas.height],
|
||||||
resolution: [this.gl.canvas.width, this.gl.canvas.height],
|
resolution: [this.gl.canvas.width, this.gl.canvas.height],
|
||||||
};
|
};
|
||||||
|
|
@ -81,7 +62,5 @@ export class Renderer {
|
||||||
|
|
||||||
twgl.setUniforms(this.programInfo, uniforms);
|
twgl.setUniforms(this.programInfo, uniforms);
|
||||||
twgl.drawBufferInfo(this.gl, this.bufferInfo);
|
twgl.drawBufferInfo(this.gl, this.bufferInfo);
|
||||||
|
|
||||||
requestAnimationFrame(this.timedRender);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
54
frontend/src/scripts/game-logic/game-logic.ts
Normal file
54
frontend/src/scripts/game-logic/game-logic.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { Command } from '../commands/command';
|
||||||
|
import { CommandReceiver } from '../commands/command-receiver';
|
||||||
|
import { ObjectContainer } from '../objects/object-container';
|
||||||
|
import { KeyDownCommand } from '../commands/types/key-down';
|
||||||
|
import { KeyUpCommand } from '../commands/types/key-up';
|
||||||
|
import { SwipeCommand } from '../commands/types/swipe';
|
||||||
|
|
||||||
|
export class GameLogic implements CommandReceiver {
|
||||||
|
private commandBuffer: Array<Command> = [];
|
||||||
|
private keysDown: Set<string> = new Set();
|
||||||
|
|
||||||
|
constructor(private objects: ObjectContainer) {}
|
||||||
|
|
||||||
|
public step(time: DOMHighResTimeStamp) {
|
||||||
|
while (this.commandBuffer.length > 0) {
|
||||||
|
const command = this.commandBuffer.pop();
|
||||||
|
console.log(command);
|
||||||
|
|
||||||
|
if (command instanceof KeyDownCommand) {
|
||||||
|
this.keysDown.add(command.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command instanceof KeyUpCommand) {
|
||||||
|
this.keysDown.delete(command.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command instanceof SwipeCommand) {
|
||||||
|
this.objects.camera.position = this.objects.camera.position.subtract(
|
||||||
|
command.delta.scale(200)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.keysDown.has('w')) {
|
||||||
|
this.objects.camera.position.y += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.keysDown.has('s')) {
|
||||||
|
this.objects.camera.position.y -= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.keysDown.has('a')) {
|
||||||
|
this.objects.camera.position.x -= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.keysDown.has('d')) {
|
||||||
|
this.objects.camera.position.x += 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendCommand(command: Command) {
|
||||||
|
this.commandBuffer.push(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
frontend/src/scripts/identity/identity-manager.ts
Normal file
4
frontend/src/scripts/identity/identity-manager.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export class IdentityManager {
|
||||||
|
public static sourceId: string;
|
||||||
|
public static targetId: string;
|
||||||
|
}
|
||||||
19
frontend/src/scripts/input/keyboard-listener.ts
Normal file
19
frontend/src/scripts/input/keyboard-listener.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { CommandGenerator } from '../commands/command-generator';
|
||||||
|
import { KeyDownCommand } from '../commands/types/key-down';
|
||||||
|
import { KeyUpCommand } from '../commands/types/key-up';
|
||||||
|
|
||||||
|
export class KeyboardListener extends CommandGenerator {
|
||||||
|
constructor(target: Element = document.body) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
target.addEventListener('keydown', (event: KeyboardEvent) => {
|
||||||
|
const key = event.key.toLowerCase();
|
||||||
|
this.sendCommand(new KeyDownCommand(key));
|
||||||
|
});
|
||||||
|
|
||||||
|
target.addEventListener('keyup', (event: KeyboardEvent) => {
|
||||||
|
const key = event.key.toLowerCase();
|
||||||
|
this.sendCommand(new KeyUpCommand(key));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
56
frontend/src/scripts/input/mouse-listener.ts
Normal file
56
frontend/src/scripts/input/mouse-listener.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { CommandGenerator } from '../commands/command-generator';
|
||||||
|
import { PrimaryActionCommand } from '../commands/types/primary-action';
|
||||||
|
import { SecondaryActionCommand } from '../commands/types/secondary-action';
|
||||||
|
import { Vec2 } from '../math/vec2';
|
||||||
|
import { SwipeCommand } from '../commands/types/swipe';
|
||||||
|
|
||||||
|
export class MouseListener extends CommandGenerator {
|
||||||
|
private previousPosition: Vec2 = null;
|
||||||
|
private isMouseDown = false;
|
||||||
|
|
||||||
|
constructor(private target: Element = document.body) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
target.addEventListener('mousedown', (event: MouseEvent) => {
|
||||||
|
const position = this.positionFromEvent(event);
|
||||||
|
|
||||||
|
this.previousPosition = position;
|
||||||
|
this.isMouseDown = true;
|
||||||
|
|
||||||
|
if (event.button == 0) {
|
||||||
|
this.sendCommand(new PrimaryActionCommand(position));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
target.addEventListener('mousemove', (event: MouseEvent) => {
|
||||||
|
if (this.isMouseDown) {
|
||||||
|
const position = this.positionFromEvent(event);
|
||||||
|
this.sendCommand(
|
||||||
|
new SwipeCommand(this.previousPosition.subtract(position))
|
||||||
|
);
|
||||||
|
this.previousPosition = position;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
target.addEventListener('mouseup', (event: MouseEvent) => {
|
||||||
|
this.isMouseDown = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
target.addEventListener('mouseleave', (event: MouseEvent) => {
|
||||||
|
this.isMouseDown = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
target.addEventListener('contextmenu', (event: MouseEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const position = this.positionFromEvent(event);
|
||||||
|
this.sendCommand(new SecondaryActionCommand(position));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private positionFromEvent(event: MouseEvent): Vec2 {
|
||||||
|
return new Vec2(
|
||||||
|
-event.clientX / this.target.clientWidth,
|
||||||
|
event.clientY / this.target.clientHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
frontend/src/scripts/input/touch-listener.ts
Normal file
42
frontend/src/scripts/input/touch-listener.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { CommandGenerator } from '../commands/command-generator';
|
||||||
|
import { PrimaryActionCommand } from '../commands/types/primary-action';
|
||||||
|
import { SecondaryActionCommand } from '../commands/types/secondary-action';
|
||||||
|
import { Vec2 } from '../math/vec2';
|
||||||
|
import { SwipeCommand } from '../commands/types/swipe';
|
||||||
|
|
||||||
|
export class TouchListener extends CommandGenerator {
|
||||||
|
private previousPosition: Vec2 = null;
|
||||||
|
|
||||||
|
constructor(private target: Element = document.body) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
target.addEventListener('touchstart', (event: TouchEvent) => {
|
||||||
|
const touchCount = event.touches.length;
|
||||||
|
const position = this.positionFromEvent(event);
|
||||||
|
this.previousPosition = position;
|
||||||
|
|
||||||
|
if (touchCount == 1) {
|
||||||
|
this.sendCommand(new PrimaryActionCommand(position));
|
||||||
|
} else {
|
||||||
|
this.sendCommand(new SecondaryActionCommand(position));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
target.addEventListener('touchmove', (event: TouchEvent) => {
|
||||||
|
const position = this.positionFromEvent(event);
|
||||||
|
|
||||||
|
this.sendCommand(
|
||||||
|
new SwipeCommand(position.subtract(this.previousPosition))
|
||||||
|
);
|
||||||
|
|
||||||
|
this.previousPosition = position;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private positionFromEvent(event: TouchEvent): Vec2 {
|
||||||
|
return new Vec2(
|
||||||
|
event.touches[0].clientX / this.target.clientWidth,
|
||||||
|
-event.touches[0].clientY / this.target.clientHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
48
frontend/src/scripts/main.ts
Normal file
48
frontend/src/scripts/main.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { Renderer } from './drawing/renderer';
|
||||||
|
|
||||||
|
import passthroughVertexShader from '../shaders/passthrough.vert';
|
||||||
|
import distanceFragmentShader from '../shaders/dist.frag';
|
||||||
|
import { KeyboardListener } from './input/keyboard-listener';
|
||||||
|
import { MouseListener } from './input/mouse-listener';
|
||||||
|
import { TouchListener } from './input/touch-listener';
|
||||||
|
import { CommandBroadcaster } from './commands/command-broadcaster';
|
||||||
|
import { GameLogic } from './game-logic/game-logic';
|
||||||
|
import { GameObject } from './objects/game-object';
|
||||||
|
import { ObjectContainer } from './objects/object-container';
|
||||||
|
import { Camera } from './objects/types/camera';
|
||||||
|
|
||||||
|
const startGameLoop = (gameLogic: GameLogic, renderer: Renderer) => {
|
||||||
|
const loop = (time: DOMHighResTimeStamp) => {
|
||||||
|
gameLogic.step(time);
|
||||||
|
renderer.render(time);
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
};
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const main = async () => {
|
||||||
|
try {
|
||||||
|
const canvas: HTMLCanvasElement = document.querySelector('canvas#main');
|
||||||
|
|
||||||
|
const objects = new ObjectContainer(new Camera());
|
||||||
|
|
||||||
|
const gameLogic = new GameLogic(objects);
|
||||||
|
new CommandBroadcaster(
|
||||||
|
[
|
||||||
|
new KeyboardListener(document.body),
|
||||||
|
new MouseListener(canvas),
|
||||||
|
new TouchListener(canvas),
|
||||||
|
],
|
||||||
|
[gameLogic]
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderer = new Renderer(canvas, objects, [
|
||||||
|
passthroughVertexShader,
|
||||||
|
distanceFragmentShader,
|
||||||
|
]);
|
||||||
|
|
||||||
|
startGameLoop(gameLogic, renderer);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
27
frontend/src/scripts/math/vec2.ts
Normal file
27
frontend/src/scripts/math/vec2.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { Typed } from '../transport/serializable';
|
||||||
|
|
||||||
|
export class Vec2 extends Typed {
|
||||||
|
public constructor(public x: number = 0.0, public y: number = null) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
if (this.y === null) {
|
||||||
|
this.y = this.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public scale(scalar: number): Vec2 {
|
||||||
|
return new Vec2(this.x * scalar, this.y * scalar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public add(other: Vec2): Vec2 {
|
||||||
|
return new Vec2(this.x + other.x, this.y + other.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public subtract(other: Vec2): Vec2 {
|
||||||
|
return new Vec2(this.x - other.x, this.y - other.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get list(): [number, number] {
|
||||||
|
return [this.x, this.y];
|
||||||
|
}
|
||||||
|
}
|
||||||
7
frontend/src/scripts/objects/game-object.ts
Normal file
7
frontend/src/scripts/objects/game-object.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { Typed } from '../transport/serializable';
|
||||||
|
import { Vec2 } from '../math/vec2';
|
||||||
|
|
||||||
|
export class GameObject extends Typed {
|
||||||
|
public position = new Vec2();
|
||||||
|
public boundingBoxSize = new Vec2();
|
||||||
|
}
|
||||||
8
frontend/src/scripts/objects/object-container.ts
Normal file
8
frontend/src/scripts/objects/object-container.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { GameObject } from './game-object';
|
||||||
|
import { Camera } from './types/camera';
|
||||||
|
|
||||||
|
export class ObjectContainer {
|
||||||
|
private objects: Array<GameObject> = [];
|
||||||
|
|
||||||
|
constructor(public camera: Camera) {}
|
||||||
|
}
|
||||||
3
frontend/src/scripts/objects/types/camera.ts
Normal file
3
frontend/src/scripts/objects/types/camera.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { GameObject } from '../game-object';
|
||||||
|
|
||||||
|
export class Camera extends GameObject {}
|
||||||
9
frontend/src/scripts/transport/serializable.ts
Normal file
9
frontend/src/scripts/transport/serializable.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
export class Typed {
|
||||||
|
public get type(): string {
|
||||||
|
return this.constructor.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON() {
|
||||||
|
return { type: this.type, ...this };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -41,6 +41,24 @@ float terrain(float x) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec2 random2(vec2 st){
|
||||||
|
st = vec2( dot(st,vec2(127.1,311.7)),
|
||||||
|
dot(st,vec2(269.5,183.3)) );
|
||||||
|
return -1.0 + 2.0*fract(sin(st)*43758.5453123);
|
||||||
|
}
|
||||||
|
|
||||||
|
float noise(vec2 st) {
|
||||||
|
vec2 i = floor(st);
|
||||||
|
vec2 f = fract(st);
|
||||||
|
|
||||||
|
vec2 u = f*f*(3.0-2.0*f);
|
||||||
|
|
||||||
|
return mix( mix( dot( random2(i + vec2(0.0,0.0) ), f - vec2(0.0,0.0) ),
|
||||||
|
dot( random2(i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) ), u.x),
|
||||||
|
mix( dot( random2(i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) ),
|
||||||
|
dot( random2(i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) ), u.x), u.y);
|
||||||
|
}
|
||||||
|
|
||||||
float lineDistance(in vec2 position, in Line line, out float h) {
|
float lineDistance(in vec2 position, in Line line, out float h) {
|
||||||
vec2 pa = position - line.a, ba = line.b - line.a;
|
vec2 pa = position - line.a, ba = line.b - line.a;
|
||||||
h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
||||||
|
|
@ -50,6 +68,7 @@ float lineDistance(in vec2 position, in Line line, out float h) {
|
||||||
return length(delta) * side + terrain(length(ba * h));
|
return length(delta) * side + terrain(length(ba * h));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Line endDummyLineFromLine(Line line) {
|
Line endDummyLineFromLine(Line line) {
|
||||||
return Line(line.b, line.b + rotate90deg(line.normal), line.normal, false);
|
return Line(line.b, line.b + rotate90deg(line.normal), line.normal, false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
|
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
|
||||||
const Sharp = require('responsive-loader/sharp');
|
const Sharp = require('responsive-loader/sharp');
|
||||||
const Sass = require('sass');
|
const Sass = require('sass');
|
||||||
|
const tsNameof = require('ts-nameof');
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
|
|
@ -73,7 +74,7 @@ module.exports = {
|
||||||
{
|
{
|
||||||
test: /\.(frag|vert)$/i,
|
test: /\.(frag|vert)$/i,
|
||||||
use: {
|
use: {
|
||||||
loader: 'raw-loader'
|
loader: 'raw-loader',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -120,7 +121,12 @@ module.exports = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.ts$/,
|
test: /\.ts$/,
|
||||||
use: 'ts-loader',
|
use: {
|
||||||
|
loader: 'ts-loader',
|
||||||
|
options: {
|
||||||
|
getCustomTransformers: () => ({ before: [tsNameof] }),
|
||||||
|
},
|
||||||
|
},
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue