Created
March 4, 2019 08:36
-
-
Save teak1/60d6864541f32c6ad8d870f40b62f322 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
import Tilemap from "./Tilemap.jsx"; | |
class ReadyEvent extends Event { | |
constructor(tilemap) { | |
super("ReadyEvent", { | |
Tiledap: tilemap, | |
map: tilemap.state.data | |
}); | |
} | |
} | |
class Tiledmap extends Component { | |
constructor(props) { | |
super(props); | |
this.x = 0; | |
this.y = 0; | |
this.state = { | |
ready: false | |
}; | |
this.data = null; | |
this.tilemap = null; | |
this.assetContainer = React.createRef(); | |
this.viewport = React.createRef(); | |
if (this.props.src) { | |
fetch(this.props.src).then(raw => raw.json()).then(map => { | |
this._mapLoaded(map); | |
}); | |
} else if (this.props.tilemap) { | |
this.data = this.props.tilemap; | |
this._mapLoaded(this.data); | |
} else { | |
throw new Error("expected props to include either 'tilemap' or 'src'"); | |
} | |
} | |
_mapLoaded(map) { | |
this.data = map; | |
this.tilemap = new Tilemap(this.data, this.assetContainer); | |
this.tilemap.onready.push(() => this.renderViewport(0, 0)); | |
this.setState({ ready: true, data: map }); | |
if (this.props.onReady) this.props.onReady(new ReadyEvent(this)); | |
} | |
render() { | |
this.renderViewport(Math.round(this.props.x + this.x), Math.round(this.props.y + this.y)); | |
return ( | |
<div className="Tiledmap-render" viewport={{}}> | |
<div ref={this.assetContainer} style={{ display: "none" }}></div> | |
<canvas ref={this.viewport} width={this.props.width} height={this.props.height}></canvas> | |
</div> | |
); | |
} | |
renderViewport(x, y) { | |
// let current = this.renderViewport.current; | |
if (this.tilemap) this.tilemap.drawViewport(this.viewportContext, x, y); | |
} | |
componentDidMount() { | |
this.viewportContext = this.viewport.current.getContext("2d"); | |
if (this.tilemap && this.tilemap.ready) this.renderViewport(this.props.x, this.props.y); | |
window.addEventListener("mousemove", (event) => { | |
if (event.buttons) { | |
this.x += event.movementX; | |
this.y += event.movementY; | |
this.setState({ ready: true, now: new Date() }); | |
} | |
}); | |
} | |
getContext() { | |
return this.viewportContext; | |
} | |
} | |
export default Tiledmap; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Tilemap { | |
constructor(data, assets) { | |
this.raw = data; | |
this.INF_region_cache = []; | |
this.internal = { | |
debug: false, | |
layers: [], | |
tilesets: [], | |
objectLayers: {}, | |
renderFunctions: [] | |
}; | |
this.assetContainer = assets; | |
this.finalRenderCanvas = document.createElement("canvas"); | |
this.finalRenderCanvas.setAttribute("width", this.getPixelWidth()); | |
this.finalRenderCanvas.setAttribute("height", this.getPixelHeight()); | |
this.context = this.finalRenderCanvas.getContext("2d"); | |
this.ready = false; | |
this.onready = []; | |
this.load(); | |
} | |
getLayersCanvas() { | |
return this.internal.layers.map(_ => _.element); | |
} | |
getPixelWidth() { | |
return this.width * this.tilewidth; | |
} | |
getPixelHeight() { | |
return this.height * this.tileheight; | |
} | |
renderTileOnContext(context, index, x, y) { | |
this.internal.renderFunctions[index](context, x, y, this.tilewidth, this.tileheight); | |
} | |
get backgroundcolor() { return this.raw.backgroundcolor; } | |
get height() { return this.raw.height; } | |
get hexsidelength() { return this.raw.hexsidelength; } | |
get infinite() { return this.raw.infinite; } | |
get layers() { return this.raw.layers; } | |
get nextlayerid() { return this.raw.nextlayerid; } | |
get nextobjectid() { return this.raw.nextobjectid; } | |
get orientation() { return this.raw.orientation; } | |
get properties() { return this.raw.properties; } | |
get renderorder() { return this.raw.renderorder; } | |
get staggeraxis() { return this.raw.staggeraxis; } | |
get staggerindex() { return this.raw.staggerindex; } | |
get tiledversion() { return this.raw.tiledversion; } | |
get tileheight() { return this.raw.tileheight; } | |
get tilesets() { return this.raw.tilesets; } | |
get tilewidth() { return this.raw.tilewidth; } | |
get type() { return this.raw.type; } | |
get version() { return this.raw.version; } | |
get width() { return this.raw.width; } | |
debug(state) { | |
this.internal.debug = state; | |
} | |
async load() { | |
let promises = []; | |
for (let i = 0; i < this.tilesets.length; i++) { | |
let promise = new Promise(async (resolve, reject) => { | |
let image = new Image(); | |
image.onload = resolve; | |
image.onerror = reject; | |
let potsrc = this.tilesets[i].image; | |
if (!potsrc) { | |
console.log((this.tilesets[i].source.replace(".tsx", ".json"))); | |
fetch(this.tilesets[i].source.replace(".tsx", ".json")).then(_ => _.json()).then(data => { | |
this.tilesets[i] = data; | |
image.src = this.tilesets[i].image; | |
console.log(image, this.tilesets[i]); | |
image.setAttribute("data-index", i); | |
}); | |
} else { | |
image.src = this.tilesets[i].image; | |
console.log(image, this.tilesets[i]); | |
image.setAttribute("data-index", i); | |
} | |
}); | |
promises.push(promise.catch(console.log)); | |
} | |
let all = await Promise.all(promises); | |
let images = all.map(event => { | |
if (!event || event.type === "error") return console.log(event); | |
let img = event.path[0]; | |
this.assetContainer.current.appendChild(img); | |
let index = +img.getAttribute("data-index"); | |
let tileset = this.tilesets[index]; | |
let tile_iw = tileset.tilewidth; | |
let tile_ih = tileset.tileheight; | |
for (let i = 0; i < tileset.tilecount; i++) { | |
let pos_x = i % tileset.columns; | |
let pos_y = Math.floor(i / tileset.columns); | |
pos_x *= tile_iw; | |
pos_y *= tile_ih; | |
this.internal.renderFunctions[tileset.firstgid + i] = (context, x, y, tw, th) => { | |
context.drawImage(img, pos_x, pos_y, tile_iw, tile_ih, x * tile_iw, y * tile_ih, tw, th); | |
}; | |
} | |
return img; | |
}); | |
this.internal.tilesets = images; | |
if (this.infinite) { | |
this.parseChunks(); | |
} else { | |
this.parseTiles(); | |
this.renderFinal(); | |
} | |
this.ready = true; | |
this.onready.forEach(cb => cb()); | |
} | |
parseChunks() { | |
let layers = this.layers; | |
for (let i = 0; i < layers.length; i++) { | |
console.log(layers[i]); | |
if (layers[i].type === "objectgroup") { | |
this.internal.objectLayers[layers[i].name] = layers[i]; | |
} else { | |
for (let chunkIndex = 0; chunkIndex < layers[i].chunks.length; chunkIndex++) { | |
let chunk = layers[i].chunks[chunkIndex]; | |
let tileLayer = new TileLayer(chunk, this, true); | |
this.INF_region_cache.push({ layer: tileLayer, z_index: i }); | |
} | |
} | |
} | |
console.log(this.INF_region_cache); | |
} | |
renderFinal() { | |
this.context.fillStyle = this.backgroundcolor; | |
if (this.context.fillStyle !== "") { | |
this.context.fillRect(0, 0, this.finalRenderCanvas.width, this.finalRenderCanvas.height); | |
} | |
for (let i = 0; i < this.internal.layers.length; i++) { | |
console.log("rendering", this.internal.layers[i]); | |
this.context.drawImage(this.internal.layers[i].element, 0, 0); | |
} | |
} | |
parseTiles() { | |
let z_index = 0; | |
for (let i = 0; i < this.layers.length; i++) { | |
let layer = this.layers[i]; | |
if (layer.type === "objectgroup") { | |
this.internal.objectLayers[layer.name] = layer; | |
} else { | |
this.internal.layers[z_index] = new TileLayer(layer, this); | |
z_index++; | |
} | |
} | |
} | |
getChunks(_x, _y, canvas) { | |
let width = canvas.width; | |
let height = canvas.height; | |
let chunks = []; | |
for (let i = 0; i < this.INF_region_cache.length; i++) { | |
let chunk = this.INF_region_cache[i]; | |
let layer = chunk.layer; | |
if (layer.x < _x + width || layer.y < _y + height || layer.x + layer.width > _x || layer.y + layer.height > _y) { | |
chunks.push(layer); | |
} | |
} | |
return chunks; | |
} | |
drawViewport(context, x, y) { | |
if (this.infinite) { | |
let toDraw = this.getChunks(x, y, context.canvas); | |
console.log(toDraw); | |
context.fillStyle = this.backgroundcolor; | |
if (context.fillStyle !== "") { | |
context.fillRect(0, 0, context.canvas.width, context.canvas.height); | |
} | |
for (let i = 0; i < toDraw.length; i++) { | |
context.drawImage(toDraw[i].element, x + toDraw[i].x, y + toDraw[i].y); | |
console.log(`chunk draw call`, toDraw[i].element, x + toDraw[i].x, y + toDraw[i].y); | |
} | |
} else { | |
context.drawImage(this.finalRenderCanvas, -x, -y); | |
} | |
} | |
} | |
class TileLayer { | |
constructor(data, tilemap, ischunk) { | |
this.tilemap = tilemap; | |
this.raw = data; | |
this.tileMatrix = []; | |
let _data = [...this.raw.data]; | |
for (let i = 0; i < this.raw.height; i++) { | |
let row = _data.splice(0, this.raw.width); | |
this.tileMatrix.push(row); | |
} | |
this.element = document.createElement("canvas"); | |
this.element.setAttribute("data-id", this.raw.id); | |
if (!ischunk) { | |
this.element.setAttribute("width", this.tilemap.getPixelWidth()); | |
this.element.setAttribute("height", this.tilemap.getPixelHeight()); | |
} else { | |
this.element.setAttribute("width", this.tilemap.tilewidth * this.raw.width); | |
this.element.setAttribute("height", this.tilemap.tileheight * this.raw.height); | |
this.x = this.tilemap.tilewidth * this.raw.x; | |
this.y = this.tilemap.tileheight * this.raw.y; | |
this.width = this.tilemap.tilewidth * this.raw.width; | |
this.height = this.tilemap.tileheight * this.raw.height; | |
} | |
this.context = this.element.getContext("2d"); | |
this.render(); | |
} | |
async render() { | |
let width = this.raw.width; | |
let height = this.raw.height; | |
for (let x = 0; x < width; x++) { | |
for (let y = 0; y < height; y++) { | |
let tile_index = this.tileMatrix[y][x]; | |
if (tile_index > 0) {//tile index 0 is empty; | |
this.tilemap.renderTileOnContext(this.context, tile_index, x, y); | |
} | |
} | |
} | |
} | |
} | |
export default Tilemap; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment