From 2c6cd362c32a2e9460e5ddebd85f050b9b5ab4e4 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 21 Nov 2018 21:44:11 +0100 Subject: Allow using animated entities as map tiles A more optimized implementation for animated tiles is planned. --- src/view/map.ts | 127 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 24 deletions(-) (limited to 'src/view/map.ts') diff --git a/src/view/map.ts b/src/view/map.ts index c796c6f..5eedaca 100644 --- a/src/view/map.ts +++ b/src/view/map.ts @@ -1,3 +1,4 @@ +import { EntityView } from './entity'; import { Renderer } from './renderer/renderer'; import { SpriteCoords, SpriteView, SpriteViewBuilder } from './sprite'; import { loadImage, mkTexture } from './util/image'; @@ -6,39 +7,91 @@ import { MapData } from '../model/data/map'; import { nextPowerOf2 } from '../util'; +import { vec2 } from 'gl-matrix'; + +interface StaticMapTile { + type: 'static'; + image: HTMLImageElement; +} + +interface EntityTile { + type: 'entity'; + entity: EntityView; +} + +type MapTile = StaticMapTile | EntityTile; + +interface StaticTilesetTile { + type: 'static'; + coords: SpriteCoords; +} + +type TilesetTile = StaticTilesetTile | EntityTile; + interface Tileset { texture: WebGLTexture; - tiles: SpriteCoords[]; + tiles: TilesetTile[]; } -function loadTiles(tiles: string[]): Promise { - return Promise.all(tiles.map((t) => loadImage(`resources/sprite/tile/${t}.png`))); +async function loadTile(r: Renderer, tile: string): Promise { + 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'); + } +} + +function loadTiles(r: Renderer, tiles: string[]): Promise { + return Promise.all(tiles.map((tile) => loadTile(r, tile))); } function mkTileset( r: Renderer, - tiles: HTMLImageElement[], + mapTiles: MapTile[], ): Tileset { const tileSize = 32; - const canvasDim = nextPowerOf2(Math.sqrt(tiles.length)); + const canvasDim = nextPowerOf2(Math.sqrt(mapTiles.length)); const canvasSize = canvasDim * tileSize; const canvas = document.createElement('canvas'); canvas.width = canvas.height = canvasSize; let x = 0, y = 0; - const tileCoords: SpriteCoords[] = []; + const tiles: TilesetTile[] = []; const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; - for (const tile of tiles) { - ctx.drawImage(tile, x * tileSize, y * tileSize); - tileCoords.push([x / canvasDim, y / canvasDim, (x + 1) / canvasDim, (y + 1) / canvasDim]); - - x++; - if (x === canvasDim) { - x = 0; - y++; + 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; } } @@ -46,40 +99,66 @@ function mkTileset( return { texture, - tiles: tileCoords, + tiles, }; } -function addSprite(builder: SpriteViewBuilder, tileset: Tileset, x: number, y: number, tile: number) { - if (tile === 0) +function addSprite( + builder: SpriteViewBuilder, + entityTiles: Array<[[number, number], EntityView]>, + tileset: Tileset, + x: number, + y: number, + tile: number) { + if (!tile) return; - const tilePos = tileset.tiles[tile - 1]; - builder.addSprite([x, y, x + 1, y + 1], tilePos); + const tilesetTile = tileset.tiles[tile - 1]; + + switch (tilesetTile.type) { + case 'static': + builder.addSprite([x, y, x + 1, y + 1], tilesetTile.coords); + break; + + case 'entity': + entityTiles.push([[x + 0.5, y + 0.5], tilesetTile.entity]); + break; + } } 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, tileset, x, y, layer[y][x]); + addSprite(builder, entityTiles, tileset, x, y, layer[y][x]); - return new MapLayerView(builder.build()); + return new MapLayerView(r, builder.build(), entityTiles); } class MapLayerView { - public constructor(private sprites: SpriteView) { + public constructor( + private r: Renderer, + private staticTiles: SpriteView, + private entityTiles: Array<[[number, number], EntityView]>, + ) { } public render(time: number): void { - this.sprites.render(); + this.r.setTranslation([0, 0]); + this.staticTiles.render(); + + for (const [coords, entity] of this.entityTiles) { + this.r.setTranslation(coords); + entity.renderByTime(time); + } } } export class MapView { public static async load(r: Renderer, map: MapData): Promise { - const tiles = await loadTiles(map.tiles); + const tiles = await loadTiles(r, map.tiles); const tileset = mkTileset(r, tiles); const layers = map.layers.map((layer) => buildMapLayer(r, tileset, layer.tiles)); -- cgit v1.2.3