Add basic gameobject system

This commit is contained in:
schmelczerandras 2020-07-19 13:50:03 +02:00
commit efb01476b2
33 changed files with 401 additions and 173 deletions

View file

@ -6,6 +6,7 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "webpack-dev-server --mode development", "start": "webpack-dev-server --mode development",
"start-unsafe": "webpack-dev-server --mode development --useLocalIp",
"build": "webpack && find dist -type f -not -name '*.html' | xargs rm" "build": "webpack && find dist -type f -not -name '*.html' | xargs rm"
}, },
"keywords": [], "keywords": [],
@ -25,6 +26,7 @@
"usedExports": true "usedExports": true
}, },
"devDependencies": { "devDependencies": {
"@types/uuid": "^8.0.0",
"autoprefixer": "^9.8.5", "autoprefixer": "^9.8.5",
"clean-webpack-plugin": "^3.0.0", "clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.5.2", "css-loader": "^3.5.2",

View file

@ -17,7 +17,10 @@
<body> <body>
<main> <main>
<noscript><h1>Javascript is required for this website.</h1></noscript> <noscript><h1>Javascript is required for this website.</h1></noscript>
<canvas id="main"></canvas> <div class="canvas-container">
<div id="overlay"></div>
<canvas id="main"></canvas>
</div>
</main> </main>
</body> </body>
</html> </html>

View file

@ -1,4 +1,4 @@
import './styles/main.scss'; import './styles/main.scss';
import { main } from './scripts/main'; import { Game } from './scripts/game';
main(); new Game();

View file

@ -1,3 +1,6 @@
import { Typed } from '../transport/serializable'; import { Typed } from '../transport/serializable';
import { Id } from '../identity/identity';
export abstract class Command extends Typed {} export abstract class Command extends Typed {
target?: Id;
}

View file

@ -0,0 +1,8 @@
import { Drawer } from '../../drawing/drawer';
import { Command } from '../command';
export class DrawCommand extends Command {
public constructor(public readonly drawer?: Drawer) {
super();
}
}

View file

@ -1,7 +1,7 @@
import { Command } from '../command'; import { Command } from '../command';
export class KeyDownCommand extends Command { export class KeyDownCommand extends Command {
public constructor(public readonly key: string) { public constructor(public readonly key?: string) {
super(); super();
} }
} }

View file

@ -1,7 +1,7 @@
import { Command } from '../command'; import { Command } from '../command';
export class KeyUpCommand extends Command { export class KeyUpCommand extends Command {
public constructor(public readonly key: string) { public constructor(public readonly key?: string) {
super(); super();
} }
} }

View file

@ -0,0 +1,9 @@
import { Drawer } from '../../drawing/drawer';
import { Command } from '../command';
import { Vec2 } from '../../math/vec2';
export class MoveToCommand extends Command {
public constructor(public readonly position?: Vec2) {
super();
}
}

View file

@ -2,7 +2,7 @@ import { Command } from '../command';
import { Vec2 } from '../../math/vec2'; import { Vec2 } from '../../math/vec2';
export class PrimaryActionCommand extends Command { export class PrimaryActionCommand extends Command {
public constructor(public readonly position: Vec2) { public constructor(public readonly position?: Vec2) {
super(); super();
} }
} }

View file

@ -2,7 +2,7 @@ import { Command } from '../command';
import { Vec2 } from '../../math/vec2'; import { Vec2 } from '../../math/vec2';
export class SecondaryActionCommand extends Command { export class SecondaryActionCommand extends Command {
public constructor(public readonly position: Vec2) { public constructor(public readonly position?: Vec2) {
super(); super();
} }
} }

View file

@ -0,0 +1,9 @@
import { Command } from '../command';
export class StepCommand extends Command {
public constructor(
public readonly deltaTimeInMiliseconds?: DOMHighResTimeStamp
) {
super();
}
}

View file

@ -2,7 +2,7 @@ import { Command } from '../command';
import { Vec2 } from '../../math/vec2'; import { Vec2 } from '../../math/vec2';
export class SwipeCommand extends Command { export class SwipeCommand extends Command {
public constructor(public readonly delta: Vec2) { public constructor(public readonly delta?: Vec2) {
super(); super();
} }
} }

View file

@ -0,0 +1,9 @@
import { Vec2 } from '../math/vec2';
export interface Drawer {
startWaitingForInstructions();
finishWaitingForInstructions();
setCameraPosition(position: Vec2);
setViewBoxSize(size: Vec2);
drawCornerText(text: string);
}

View file

@ -0,0 +1 @@
//export function

View file

@ -1,10 +1,9 @@
const twgl = require('twgl.js'); const twgl = require('twgl.js');
import { TimeIt } from '../helper/timing'; import { Drawer } from './drawer';
import { Vec2 } from '../math/vec2'; import { Vec2 } from '../math/vec2';
import { ObjectContainer } from '../objects/object-container';
export class Renderer { export class WebGl2Renderer implements Drawer {
private gl: WebGL2RenderingContext; private gl: WebGL2RenderingContext;
private programInfo: any; private programInfo: any;
private bufferInfo: any; private bufferInfo: any;
@ -12,7 +11,7 @@ export class Renderer {
constructor( constructor(
private canvas: HTMLCanvasElement, private canvas: HTMLCanvasElement,
private objects: ObjectContainer, private overlay: HTMLElement,
shaderSources: Array<string> shaderSources: Array<string>
) { ) {
twgl.setDefaults({ attribPrefix: 'a_' }); twgl.setDefaults({ attribPrefix: 'a_' });
@ -42,7 +41,14 @@ export class Renderer {
); );
} }
public render(time: number) { startWaitingForInstructions() {
//throw new Error("Method not implemented.");
}
private cameraPosition: Vec2;
private viewBoxSize: Vec2;
finishWaitingForInstructions() {
const gl = this.gl; const gl = this.gl;
twgl.resizeCanvasToDisplaySize(this.canvas); twgl.resizeCanvasToDisplaySize(this.canvas);
@ -51,8 +57,8 @@ export class Renderer {
gl.clear(gl.COLOR_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT);
const uniforms = { const uniforms = {
cameraPosition: this.objects.camera.position.list, cameraPosition: this.cameraPosition.list,
viewBoxSize: [this.gl.canvas.width, this.gl.canvas.height], viewBoxSize: this.viewBoxSize.list,
resolution: [this.gl.canvas.width, this.gl.canvas.height], resolution: [this.gl.canvas.width, this.gl.canvas.height],
}; };
@ -63,4 +69,18 @@ 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);
} }
setCameraPosition(position: Vec2) {
this.cameraPosition = position;
}
setViewBoxSize(size: Vec2) {
this.viewBoxSize = size;
}
drawCornerText(text: string) {
if (this.overlay.innerText != text) {
this.overlay.innerText = text;
}
}
} }

View file

@ -1,54 +0,0 @@
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);
}
}

View file

@ -0,0 +1,60 @@
import { WebGl2Renderer } from './drawing/renderer';
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 { ObjectContainer } from './objects/object-container';
import { DrawCommand } from './commands/types/draw';
import passthroughVertexShader from '../shaders/passthrough.vert';
import distanceFragmentShader from '../shaders/dist.frag';
import { StepCommand } from './commands/types/step';
import { Character } from './objects/types/character';
import { InfoText } from './objects/types/info-text';
import { timeIt } from './helper/timing';
export class Game {
private previousTime: DOMHighResTimeStamp = 0;
private objects: ObjectContainer = new ObjectContainer();
private renderer: WebGl2Renderer;
constructor() {
const canvas: HTMLCanvasElement = document.querySelector('canvas#main');
const overlay: HTMLElement = document.querySelector('#overlay');
new CommandBroadcaster(
[
new KeyboardListener(document.body),
new MouseListener(canvas),
new TouchListener(canvas),
],
[this.objects]
);
this.renderer = new WebGl2Renderer(canvas, overlay, [
passthroughVertexShader,
distanceFragmentShader,
]);
this.initializeScene();
requestAnimationFrame(this.gameLoop.bind(this));
}
private initializeScene() {
this.objects.addObject(new Character(this.objects));
this.objects.addObject(new InfoText());
}
@timeIt()
private gameLoop(time: DOMHighResTimeStamp) {
const deltaTime = time - this.previousTime;
this.previousTime = time;
this.objects.sendCommand(new StepCommand(deltaTime));
this.renderer.startWaitingForInstructions();
this.objects.sendCommand(new DrawCommand(this.renderer));
this.renderer.finishWaitingForInstructions();
requestAnimationFrame(this.gameLoop.bind(this));
}
}

View file

@ -1,22 +1,37 @@
export const TimeIt = (func, interval = 60) => { import { InfoText } from '../objects/types/info-text';
let i = 0;
let values = [];
return () => { export function timeIt(interval = 60) {
const start = performance.now(); return function (
func(); target: any,
const end = performance.now(); propertyKey: string,
descriptor: PropertyDescriptor
) {
let i = 0;
let previousTimes: Array<DOMHighResTimeStamp> = [];
values.push(end - start); const targetFunction = descriptor.value;
if (++i % interval == 0) {
values.sort(); descriptor.value = function (...values) {
console.log( const start = performance.now();
`${func.name}\n\tMax ${values[values.length - 1].toFixed( targetFunction.bind(this)(...values);
const end = performance.now();
previousTimes.push(end - start);
if (i++ % interval == 0) {
previousTimes.sort();
const text = `Max: ${previousTimes[previousTimes.length - 1].toFixed(
2 2
)} ms\n\tMedian ${values[Math.floor(values.length / 2)].toFixed(2)} ms` )} ms\n\tMedian: ${previousTimes[
); Math.floor(previousTimes.length / 2)
].toFixed(2)} ms`;
values = []; InfoText.modifyRecord(propertyKey, text);
}
previousTimes = [];
}
};
return descriptor;
}; };
}; }

View file

@ -1,4 +1,8 @@
import { Id } from './identity';
import { v4 } from 'uuid';
export class IdentityManager { export class IdentityManager {
public static sourceId: string; public static generateId(): Id {
public static targetId: string; return v4();
}
} }

View file

@ -0,0 +1 @@
export type Id = string;

Some files were not shown because too many files have changed in this diff Show more