tilegame/src/tiles.js

193 lines
4.5 KiB
JavaScript

import { ctx, ResourceCacheFactory } from './canvas'
import Resource from './resource'
const cacheFactory = new ResourceCacheFactory()
class TileMap {
constructor (image, rows) {
this._src = image
this.rows = rows
this.defs = {}
}
get height () {
return this.image.height
}
get width () {
return this.image.width
}
get tile () {
return this.width / this.rows
}
get image () {
return Resource.loadImage(this._src, true)
}
tileAt (i) {
return { x: (i % this.rows) * this.tile, y: Math.floor(i / this.rows) * this.tile }
}
define (tile, index) {
if (typeof tile === 'object') {
this.defs = Object.assign(this.defs, tile)
return
}
this.defs[tile] = index
}
indexOf (tile) {
return this.defs[tile] || null
}
positionOf (tile) {
return this.tileAt(this.indexOf(tile))
}
}
class TileChunk {
constructor (ix, iy, size = 16, tileSize = 16) {
this.x = ix
this.y = iy
this.size = size
this.tile = tileSize
this.tiles = []
this.dirty = true
this.img = null
this._updated = false
}
generateMap (tileMap, heightMap) {
for (let i = 0; i < this.size * this.size; i++) {
let tileCoords = this.toXY(i)
let tileAbs = this.toAbs(tileCoords)
let y = Math.ceil(heightMap.getHeight(tileAbs.x) * 5 / 2) - 4
if (tileAbs.y < y) {
this.tiles.push(-1)
continue
}
if (tileAbs.y === y) {
this.tiles.push(tileMap.indexOf('GRASS_TOP'))
continue
}
if (tileAbs.y < y + 10) {
this.tiles.push(tileMap.indexOf('DIRT'))
continue
}
this.tiles.push(tileMap.indexOf('STONE'))
}
}
tileAt (i) {
return this.tiles[i]
}
tileAtXY (x, y) {
return this.tileAt(x + this.size * y)
}
toXY (i) {
return { x: i % this.size, y: Math.floor(i / this.size) }
}
toAbs (x, y) {
if (typeof x === 'object') {
y = x.y
x = x.x
}
return { x: this.x * this.size + x, y: this.y * this.size + y }
}
get absPos () {
return { x: this.x * this.size * this.tile, y: this.y * this.size * this.tile }
}
draw (view, map) {
// Create a cached image of the chunk
if (this.dirty || !this.img) {
cacheFactory.prepare(this.size * this.tile, this.size * this.tile)
for (let i in this.tiles) {
let tilei = this.tiles[i]
if (tilei === -1) continue
let coords = this.toXY(parseInt(i))
let tileCoords = map.tileAt(tilei)
cacheFactory.ctx.drawImage(map.image, tileCoords.x, tileCoords.y, map.tile, map.tile,
coords.x * this.tile, coords.y * this.tile, this.tile, this.tile)
}
this.img = cacheFactory.capture()
this.dirty = false
this._updated = true
return
}
// Draw the cached image
let p = this.absPos
ctx.drawImage(this.img, p.x - view.x, p.y - view.y)
}
update (dt) {
}
}
class World {
constructor (heightMap, tileMaps, chunkSize = 16, tileSize = 16) {
this.heightMap = heightMap
this.chunkSize = chunkSize
this.tileSize = tileSize
this.tileMaps = tileMaps
this.chunks = []
// Debug info
this._lastDrawCount = 0
this._lastUpdateCount = 0
}
getChunk (x, y) {
for (let i in this.chunks) {
let chunk = this.chunks[i]
if (chunk && chunk.x === x && chunk.y === y) return chunk
}
return null
}
update (dt, vp) {
let posPoint = vp.chunkIn(this.chunkSize * this.tileSize)
for (let x = posPoint.x - 4; x < posPoint.x + 5; x++) {
for (let y = posPoint.y - 4; y < posPoint.y + 5; y++) {
if (x < 0 || y < 0) continue
let exists = this.getChunk(x, y)
if (!exists) {
let n = new TileChunk(x, y, this.chunkSize, this.tileSize)
n.generateMap(this.tileMaps.GROUND, this.heightMap)
this.chunks.push(n)
break
}
}
}
for (let i in this.chunks) {
this.chunks[i].update(dt)
}
}
draw (vp) {
this._lastDrawCount = 0
this._lastUpdateCount = 0
for (let i in this.chunks) {
let chunk = this.chunks[i]
let absPos = chunk.absPos
let chunkSize = chunk.size * this.tileSize
if (absPos.x > vp.x + vp.width || absPos.x + chunkSize < vp.x ||
absPos.y > vp.y + vp.height || absPos.y + chunkSize < vp.y) continue
chunk._updated = false
chunk.draw(vp, this.tileMaps.GROUND)
if (chunk._updated) this._lastUpdateCount++
this._lastDrawCount++
}
}
}
export { TileMap, TileChunk, World }