summaryrefslogtreecommitdiffstats
path: root/src/renderer/runtime/view
diff options
context:
space:
mode:
Diffstat (limited to 'src/renderer/runtime/view')
-rw-r--r--src/renderer/runtime/view/entity.ts26
-rw-r--r--src/renderer/runtime/view/input/gameinput.ts26
-rw-r--r--src/renderer/runtime/view/input/inputhandler.ts12
-rw-r--r--src/renderer/runtime/view/map.ts111
-rw-r--r--src/renderer/runtime/view/renderer/renderer.ts14
-rw-r--r--src/renderer/runtime/view/renderer/shaders.ts12
-rw-r--r--src/renderer/runtime/view/sprite.ts62
-rw-r--r--src/renderer/runtime/view/util/image.ts20
8 files changed, 128 insertions, 155 deletions
diff --git a/src/renderer/runtime/view/entity.ts b/src/renderer/runtime/view/entity.ts
index ec91503..4fc0937 100644
--- a/src/renderer/runtime/view/entity.ts
+++ b/src/renderer/runtime/view/entity.ts
@@ -1,4 +1,4 @@
-import { EntityData } from '../model/data/entity';
+import { EntityData, EntityDataInput } from '../model/data/entity';
import { Renderer } from './renderer/renderer';
import { SpriteCoords, SpriteView, SpriteViewBuilder } from './sprite';
import { loadImage, mkTexture } from './util/image';
@@ -9,7 +9,7 @@ import { vec2 } from 'gl-matrix';
export class EntityView {
public static async load(r: Renderer, name: string): Promise<EntityView> {
- const data = new EntityData(await getJSON(`resources/entity/${name}.json`));
+ const data = new EntityData((await getJSON(`resources/entity/${name}.json`)) as EntityDataInput);
const tile = await loadImage(`resources/sprite/entity/${data.sprite}.png`);
const [texture, size] = mkTexture(r, tile);
@@ -33,22 +33,14 @@ export class EntityView {
sprites.push(builder.build());
}
- return new EntityView(
- data,
- sprites,
- );
+ return new EntityView(data, sprites);
}
private readonly totalTime: number;
- private constructor(
- public readonly data: EntityData,
- public readonly sprites: SpriteView[],
- ) {
- if (data.animation)
- this.totalTime = data.animation.sequence.reduce((a, s) => a + s[0], 0);
- else
- this.totalTime = 0;
+ private constructor(public readonly data: EntityData, public readonly sprites: SpriteView[]) {
+ if (data.animation) this.totalTime = data.animation.sequence.reduce((a, s) => a + s[0], 0);
+ else this.totalTime = 0;
}
public getSpriteByTime(time: number): SpriteView {
@@ -57,16 +49,14 @@ export class EntityView {
if (this.data.animation) {
for (const [len, sprite] of this.data.animation.sequence) {
time -= len;
- if (time < 0)
- return this.sprites[sprite];
+ if (time < 0) return this.sprites[sprite];
}
}
return this.sprites[0];
}
- public renderByTime(time: number) {
+ public renderByTime(time: number): void {
this.getSpriteByTime(time).render();
}
-
}
diff --git a/src/renderer/runtime/view/input/gameinput.ts b/src/renderer/runtime/view/input/gameinput.ts
index 67fbe0c..b116839 100644
--- a/src/renderer/runtime/view/input/gameinput.ts
+++ b/src/renderer/runtime/view/input/gameinput.ts
@@ -35,13 +35,8 @@ export class GameInputHandler extends Listenable<[GameInput]> {
super();
this.input = new InputHandler(
- new Set([
- 'ArrowLeft',
- 'ArrowUp',
- 'ArrowRight',
- 'ArrowDown',
- ...Object.keys(buttonMapping),
- ]));
+ new Set(['ArrowLeft', 'ArrowUp', 'ArrowRight', 'ArrowDown', ...Object.keys(buttonMapping)]),
+ );
this.input.addListener((key: string, pressed: boolean) => {
const button = buttonMapping[key];
@@ -57,17 +52,12 @@ export class GameInputHandler extends Listenable<[GameInput]> {
const dir = vec2.create();
- if (this.input.has('ArrowLeft'))
- vec2.add(dir, dir, [-1, 0]);
- if (this.input.has('ArrowUp'))
- vec2.add(dir, dir, [0, -1]);
- if (this.input.has('ArrowRight'))
- vec2.add(dir, dir, [1, 0]);
- if (this.input.has('ArrowDown'))
- vec2.add(dir, dir, [0, 1]);
-
- if (vec2.sqrLen(dir) > 0)
- vec2.normalize(dir, dir);
+ if (this.input.has('ArrowLeft')) vec2.add(dir, dir, [-1, 0]);
+ if (this.input.has('ArrowUp')) vec2.add(dir, dir, [0, -1]);
+ if (this.input.has('ArrowRight')) vec2.add(dir, dir, [1, 0]);
+ if (this.input.has('ArrowDown')) vec2.add(dir, dir, [0, 1]);
+
+ if (vec2.sqrLen(dir) > 0) vec2.normalize(dir, dir);
this.runListeners({
type: 'direction',
diff --git a/src/renderer/runtime/view/input/inputhandler.ts b/src/renderer/runtime/view/input/inputhandler.ts
index 17abfe6..4f5f150 100644
--- a/src/renderer/runtime/view/input/inputhandler.ts
+++ b/src/renderer/runtime/view/input/inputhandler.ts
@@ -7,26 +7,22 @@ export class InputHandler extends Listenable<[string, boolean]> {
super();
window.addEventListener('keydown', (ev) => {
- if (!relevantKeys.has(ev.code))
- return;
+ if (!relevantKeys.has(ev.code)) return;
ev.preventDefault();
- if (ev.repeat)
- return;
+ if (ev.repeat) return;
this.keys.add(ev.code);
this.runListeners(ev.code, true);
});
window.addEventListener('keyup', (ev) => {
- if (!relevantKeys.has(ev.code))
- return;
+ if (!relevantKeys.has(ev.code)) return;
ev.preventDefault();
- if (!this.keys.has(ev.code))
- return;
+ if (!this.keys.has(ev.code)) return;
this.keys.delete(ev.code);
this.runListeners(ev.code, false);
diff --git a/src/renderer/runtime/view/map.ts b/src/renderer/runtime/view/map.ts
index 18def05..90d0c52 100644
--- a/src/renderer/runtime/view/map.ts
+++ b/src/renderer/runtime/view/map.ts
@@ -34,20 +34,20 @@ interface Tileset {
async function loadTile(r: Renderer, tile: string): Promise<MapTile> {
const name = tile.substr(1);
switch (tile[0]) {
- case '-':
- return {
- type: 'static',
- image: await loadImage(`resources/sprite/tile/${name}.png`),
- };
-
- case '@':
- return {
- type: 'entity',
- entity: await EntityView.load(r, name),
- };
-
- default:
- throw new Error('invalid tile specifier');
+ case '-':
+ return {
+ type: 'static',
+ image: await loadImage(`resources/sprite/tile/${name}.png`),
+ };
+
+ case '@':
+ return {
+ type: 'entity',
+ entity: await EntityView.load(r, name),
+ };
+
+ default:
+ throw new Error('invalid tile specifier');
}
}
@@ -55,10 +55,7 @@ function loadTiles(r: Renderer, tiles: string[]): Promise<MapTile[]> {
return Promise.all(tiles.map((tile) => loadTile(r, tile)));
}
-function mkTileset(
- r: Renderer,
- mapTiles: MapTile[],
-): Tileset {
+function mkTileset(r: Renderer, mapTiles: MapTile[]): Tileset {
const tileSize = 32;
const canvasDim = nextPowerOf2(Math.sqrt(mapTiles.length));
@@ -67,29 +64,35 @@ function mkTileset(
const canvas = document.createElement('canvas');
canvas.width = canvas.height = canvasSize;
- let x = 0, y = 0;
+ let x = 0;
+ let y = 0;
const tiles: TilesetTile[] = [];
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
for (const tile of mapTiles) {
switch (tile.type) {
- case 'static':
- ctx.drawImage(tile.image, x * tileSize, y * tileSize);
- tiles.push({
- type: 'static',
- coords: [x / canvasDim, y / canvasDim, (x + 1) / canvasDim, (y + 1) / canvasDim],
- });
-
- x++;
- if (x === canvasDim) {
- x = 0;
- y++;
- }
- break;
-
- case 'entity':
- tiles.push(tile);
- break;
+ case 'static':
+ ctx.drawImage(tile.image, x * tileSize, y * tileSize);
+ tiles.push({
+ type: 'static',
+ coords: [
+ x / canvasDim,
+ y / canvasDim,
+ (x + 1) / canvasDim,
+ (y + 1) / canvasDim,
+ ],
+ });
+
+ x++;
+ if (x === canvasDim) {
+ x = 0;
+ y++;
+ }
+ break;
+
+ case 'entity':
+ tiles.push(tile);
+ break;
}
}
@@ -107,9 +110,9 @@ function addSprite(
tileset: Tileset,
x: number,
y: number,
- tile: number) {
- if (!tile)
- return;
+ tile: number,
+): void {
+ if (!tile) return;
const tilesetTile = tileset.tiles[tile - 1];
@@ -124,24 +127,12 @@ function addSprite(
}
}
-function buildMapLayer(r: Renderer, tileset: Tileset, layer: number[][]): MapLayerView {
- const builder = new SpriteViewBuilder(r, tileset.texture);
- const entityTiles: Array<[[number, number], EntityView]> = [];
-
- for (let x = 0; x < layer[0].length; x++)
- for (let y = 0; y < layer.length; y++)
- addSprite(builder, entityTiles, tileset, x, y, layer[y][x]);
-
- return new MapLayerView(r, builder.build(), entityTiles);
-}
-
class MapLayerView {
public constructor(
private r: Renderer,
private staticTiles: SpriteView,
private entityTiles: Array<[[number, number], EntityView]>,
- ) {
- }
+ ) {}
public render(time: number): void {
this.r.setTranslation([0, 0]);
@@ -154,6 +145,16 @@ class MapLayerView {
}
}
+function buildMapLayer(r: Renderer, tileset: Tileset, layer: number[][]): MapLayerView {
+ const builder = new SpriteViewBuilder(r, tileset.texture);
+ const entityTiles: Array<[[number, number], EntityView]> = [];
+
+ for (let x = 0; x < layer[0].length; x++)
+ for (let y = 0; y < layer.length; y++) addSprite(builder, entityTiles, tileset, x, y, layer[y][x]);
+
+ return new MapLayerView(r, builder.build(), entityTiles);
+}
+
export class MapView {
public static async load(r: Renderer, map: MapData): Promise<MapView> {
const tiles = await loadTiles(r, map.tiles);
@@ -163,11 +164,9 @@ export class MapView {
return new MapView(layers);
}
- private constructor(private layers: MapLayerView[]) {
- }
+ private constructor(private layers: MapLayerView[]) {}
public render(time: number): void {
- for (const layer of this.layers)
- layer.render(time);
+ for (const layer of this.layers) layer.render(time);
}
}
diff --git a/src/renderer/runtime/view/renderer/renderer.ts b/src/renderer/runtime/view/renderer/renderer.ts
index 90583a6..1e5b699 100644
--- a/src/renderer/runtime/view/renderer/renderer.ts
+++ b/src/renderer/runtime/view/renderer/renderer.ts
@@ -28,8 +28,7 @@ export class Renderer {
public createBuffer(): WebGLBuffer {
const ret = this.gl.createBuffer();
- if (!ret)
- throw new Error('unable to create buffer');
+ if (!ret) throw new Error('unable to create buffer');
return ret;
}
@@ -50,11 +49,11 @@ export class Renderer {
return this.shaders.samplerLoc;
}
- public setCenter(v: vec2|number[]) {
+ public setCenter(v: vec2 | number[]): void {
this.snapToGrid(this.center, v);
}
- public setTranslation(v: vec2|number[]) {
+ public setTranslation(v: vec2 | number[]): void {
vec2.sub(this.translation, v, this.center);
this.snapToGrid(this.translation, this.translation);
this.gl.uniform2fv(this.shaders.translateLoc, this.translation);
@@ -66,7 +65,7 @@ export class Renderer {
this.setTranslation([0, 0]);
}
- public snapToGrid(out: vec2, a: vec2|number[]): void {
+ public snapToGrid(out: vec2, a: vec2 | number[]): void {
vec2.scale(out, a, this.coordScale);
vec2.round(out, out);
vec2.scale(out, out, 1 / this.coordScale);
@@ -84,14 +83,13 @@ export class Renderer {
const scale = this.viewScale * this.coordScale;
mat4.identity(this.viewport);
- mat4.scale(this.viewport, this.viewport, [2 * scale / ws, -2 * scale / hs, 1.0]);
+ mat4.scale(this.viewport, this.viewport, [(2 * scale) / ws, (-2 * scale) / hs, 1.0]);
this.gl.uniformMatrix4fv(this.shaders.viewportLoc, false, this.viewport);
}
private mkContext(): WebGLRenderingContext {
const gl = this.canvas.getContext('webgl');
- if (!gl)
- throw new Error('unable to initialize WebGL context');
+ if (!gl) throw new Error('unable to initialize WebGL context');
return gl;
}
diff --git a/src/renderer/runtime/view/renderer/shaders.ts b/src/renderer/runtime/view/renderer/shaders.ts
index 8fd1fda..c00568e 100644
--- a/src/renderer/runtime/view/renderer/shaders.ts
+++ b/src/renderer/runtime/view/renderer/shaders.ts
@@ -11,8 +11,7 @@ export class Shaders {
constructor(private readonly gl: WebGLRenderingContext) {
const shaderProgram = this.gl.createProgram();
- if (!shaderProgram)
- throw new Error('Unable to create shader program');
+ if (!shaderProgram) throw new Error('Unable to create shader program');
const vertexShader = this.compileShader(this.gl.VERTEX_SHADER, vertexShaderSrc);
const fragmentShader = this.compileShader(this.gl.FRAGMENT_SHADER, fragmentShaderSrc);
@@ -46,8 +45,7 @@ export class Shaders {
private compileShader(type: number, src: string): WebGLShader {
const shader = this.gl.createShader(type);
- if (!shader)
- throw new Error('Unable to create shader');
+ if (!shader) throw new Error('Unable to create shader');
this.gl.shaderSource(shader, src);
this.gl.compileShader(shader);
@@ -63,16 +61,14 @@ export class Shaders {
private getAttribLocation(program: WebGLProgram, name: string): number {
const ret = this.gl.getAttribLocation(program, name);
- if (ret < 0)
- throw new Error("unable to get location of attribute '" + name + "'");
+ if (ret < 0) throw new Error("unable to get location of attribute '" + name + "'");
return ret;
}
private getUniformLocation(program: WebGLProgram, name: string): WebGLUniformLocation {
const ret = this.gl.getUniformLocation(program, name);
- if (!ret)
- throw new Error("unable to get location of uniform '" + name + "'");
+ if (!ret) throw new Error("unable to get location of uniform '" + name + "'");
return ret;
}
diff --git a/src/renderer/runtime/view/sprite.ts b/src/renderer/runtime/view/sprite.ts
index 3a5ebcb..877b40e 100644
--- a/src/renderer/runtime/view/sprite.ts
+++ b/src/renderer/runtime/view/sprite.ts
@@ -2,34 +2,6 @@ import { Renderer } from './renderer/renderer';
export type SpriteCoords = [number, number, number, number];
-export class SpriteViewBuilder {
- private static pushSprite(buf: number[], coords: SpriteCoords): void {
- const [x1, y1, x2, y2] = coords;
-
- buf.push(x1); buf.push(y1);
- buf.push(x2); buf.push(y1);
- buf.push(x1); buf.push(y2);
-
- buf.push(x1); buf.push(y2);
- buf.push(x2); buf.push(y1);
- buf.push(x2); buf.push(y2);
- }
-
- private readonly vertexData: number[] = [];
- private readonly textureData: number[] = [];
-
- constructor(private readonly r: Renderer, private readonly texture: WebGLTexture) {}
-
- public addSprite(vertexCoords: SpriteCoords, texCoords: SpriteCoords): void {
- SpriteViewBuilder.pushSprite(this.vertexData, vertexCoords);
- SpriteViewBuilder.pushSprite(this.textureData, texCoords);
- }
-
- public build(): SpriteView {
- return new SpriteView(this.r, this.texture, this.vertexData, this.textureData);
- }
-}
-
export class SpriteView {
private readonly vertexCount: number;
private readonly vertexBuffer: WebGLBuffer;
@@ -70,3 +42,37 @@ export class SpriteView {
gl.drawArrays(gl.TRIANGLES, 0, this.vertexCount);
}
}
+
+export class SpriteViewBuilder {
+ private static pushSprite(buf: number[], coords: SpriteCoords): void {
+ const [x1, y1, x2, y2] = coords;
+
+ buf.push(x1);
+ buf.push(y1);
+ buf.push(x2);
+ buf.push(y1);
+ buf.push(x1);
+ buf.push(y2);
+
+ buf.push(x1);
+ buf.push(y2);
+ buf.push(x2);
+ buf.push(y1);
+ buf.push(x2);
+ buf.push(y2);
+ }
+
+ private readonly vertexData: number[] = [];
+ private readonly textureData: number[] = [];
+
+ constructor(private readonly r: Renderer, private readonly texture: WebGLTexture) {}
+
+ public addSprite(vertexCoords: SpriteCoords, texCoords: SpriteCoords): void {
+ SpriteViewBuilder.pushSprite(this.vertexData, vertexCoords);
+ SpriteViewBuilder.pushSprite(this.textureData, texCoords);
+ }
+
+ public build(): SpriteView {
+ return new SpriteView(this.r, this.texture, this.vertexData, this.textureData);
+ }
+}
diff --git a/src/renderer/runtime/view/util/image.ts b/src/renderer/runtime/view/util/image.ts
index 37c5e7f..42e7eb7 100644
--- a/src/renderer/runtime/view/util/image.ts
+++ b/src/renderer/runtime/view/util/image.ts
@@ -3,20 +3,20 @@ import { Renderer } from '../renderer/renderer';
export function loadImage(url: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const img = new Image();
- img.addEventListener('load', () => { resolve(img); });
- img.addEventListener('error', () => { reject(new Error('failed to load ' + url)); });
+ img.addEventListener('load', () => {
+ resolve(img);
+ });
+ img.addEventListener('error', () => {
+ reject(new Error('failed to load ' + url));
+ });
img.src = url;
});
}
-export function mkTexture(
- r: Renderer,
- src: HTMLCanvasElement|HTMLImageElement,
-): [WebGLTexture, [number, number]] {
+export function mkTexture(r: Renderer, src: HTMLCanvasElement | HTMLImageElement): [WebGLTexture, [number, number]] {
const gl = r.getContext();
const texture = gl.createTexture();
- if (!texture)
- throw new Error('unable to create texture');
+ if (!texture) throw new Error('unable to create texture');
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, src);
@@ -25,9 +25,7 @@ export function mkTexture(
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- const size: [number, number] = [
- src.width / r.coordScale, src.height / r.coordScale,
- ];
+ const size: [number, number] = [src.width / r.coordScale, src.height / r.coordScale];
return [texture, size];
}