Compare commits

...

3 Commits

Author SHA1 Message Date
b2624eb989 Draw a room 2025-06-04 01:17:36 -04:00
d320f0c1dc Parallelize asset loading 2025-06-03 21:41:46 -04:00
6b1b31af29 Load and use a single asset 2025-06-03 21:33:59 -04:00
10 changed files with 270 additions and 3 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.tiled-session

BIN
assets/RPGpack_sheet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

82
rooms/sample.tmj Normal file
View File

@ -0,0 +1,82 @@
{ "compressionlevel":-1,
"height":20,
"infinite":false,
"layers":[
{
"data":[195, 22, 22, 22, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
22, 22, 22, 22, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
22, 22, 22, 22, 44, 145, 146, 146, 146, 146, 147, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
22, 22, 22, 22, 44, 165, 166, 166, 166, 166, 167, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
22, 44, 44, 44, 44, 85, 86, 86, 86, 86, 87, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 62, 61, 61, 106, 61, 64, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 45, 44, 44, 44, 44, 44, 44, 44, 44, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44],
"height":20,
"id":1,
"name":"Tile Layer 1",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":30,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 189, 229, 0, 0, 0, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 221, 0, 0, 0, 0, 201, 0, 0, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":20,
"id":2,
"name":"Tile Layer 2",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":30,
"x":0,
"y":0
}],
"nextlayerid":3,
"nextobjectid":1,
"orientation":"orthogonal",
"renderorder":"right-down",
"tiledversion":"1.11.2",
"tileheight":64,
"tilesets":[
{
"firstgid":1,
"source":"..\/tilesets\/kenney_rpg.tsj"
}],
"tilewidth":64,
"type":"map",
"version":"1.10",
"width":30
}

67
src/assets.js Normal file
View File

@ -0,0 +1,67 @@
import Room from "./room.js"
import Tileset from "./tileset.js"
const IMAGE_ASSETS = {
}
const TILESET_ASSETS = {
}
const ROOM_ASSETS = {
sampleRoom: "./rooms/sample.tmj"
}
export default class Assets {
constructor() {
this.assetMap = {}
}
get(assetName) {
console.log("getting", assetName)
console.log("from", this.assetMap)
return this.assetMap[assetName]
}
async load() {
await Promise.all([
this.loadImages(IMAGE_ASSETS),
this.loadTilesets(TILESET_ASSETS),
this.loadRooms(ROOM_ASSETS)
])
Object.keys(ROOM_ASSETS).forEach(roomName => this.get(roomName).populateTilesets(this))
}
loadImages(images) {
return Promise.all(Object.entries(images).map(entry => this.loadImage(...entry)))
}
loadTilesets(tilesets) {
return Promise.all(Object.entries(tilesets).map(entry => this.loadTileset(...entry)))
}
loadRooms(rooms) {
return Promise.all(Object.entries(rooms).map(entry => this.loadRoom(...entry)))
}
loadImage(name, path) {
console.log(name, path)
return new Promise(resolve => {
const img = new Image()
this.assetMap[name] = img
img.src = path
img.addEventListener("load", () => resolve(img))
})
}
loadTileset(name, path) {
return fetch(path).then(rsp => rsp.json()).then(json => {
return this.assetMap[name] = new Tileset(json, name)
}).then(tileset => this.loadImages(tileset.imagesToLoad))
}
loadRoom(name, path) {
return fetch(path).then(rsp => rsp.json()).then(json => {
return this.assetMap[name] = new Room(json, name)
}).then(room => this.loadTilesets(room.tilesetsToLoad))
}
}

View File

@ -10,9 +10,12 @@ export default class Game {
this.actors.push(new Player(this, 200, 200))
this.input = new Input().initialize()
this.currentRoom = null
}
start() {
this.currentRoom = this.assets.get("sampleRoom")
requestAnimationFrame(this.loop.bind(this))
}
@ -31,8 +34,7 @@ export default class Game {
draw() {
const { canvas, ctx } = this
ctx.fillStyle = "#222"
ctx.fillRect(0, 0, canvas.width, canvas.height)
this.currentRoom.draw(ctx)
this.actors.forEach(actor => actor.draw(ctx))
}
}

View File

@ -1,8 +1,12 @@
import Game from "./game.js"
import Assets from "./assets.js"
document.addEventListener("DOMContentLoaded", e => {
document.addEventListener("DOMContentLoaded", async e => {
const canvas = document.getElementById("game-canvas")
const game = new Game(canvas)
game.assets = new Assets()
await game.assets.load()
game.start()
})

48
src/room.js Normal file
View File

@ -0,0 +1,48 @@
export default class Room {
constructor(json, name) {
this.json = json
this.name = name
console.log(json)
}
get tilesetsToLoad() {
const ts = {}
this.json.tilesets.forEach((tileset, index) => {
ts[`${this.name}-${index}`] = tileset.source
})
return ts
}
populateTilesets(assets) {
console.log(this.json)
this.tilesets = this.json.tilesets.map((tileset, index) => {
const ts = assets.get(`${this.name}-${index}`)
ts.populateImage(assets)
return ts
})
}
draw(ctx) {
this.json.layers.forEach(layer => {
for (let y = 0; y < layer.height; y++) {
for (let x = 0; x < layer.width; x++) {
const index = x + (y * layer.width)
const tileIndex = layer.data[index] - 1
const tileset = this.tilesets[0]
const [sx, sy] = tileset.tileOffset(tileIndex)
ctx.drawImage(
tileset.image,
sx,
sy,
tileset.tileWidth,
tileset.tileHeight,
x * this.json.tilewidth,
y * this.json.tileheight,
this.json.tilewidth,
this.json.tileheight
)
}
}
})
}
}

35
src/tileset.js Normal file
View File

@ -0,0 +1,35 @@
export default class Tileset {
constructor(json, name) {
this.json = json
this.name = name
}
get imagesToLoad() {
const image = {}
image[`${this.name}-image`] = this.json.image
return image
}
populateImage(assets) {
this.image = assets.get(`${this.name}-image`)
}
get tileWidth() {
return this.json.tilewidth
}
get tileHeight() {
return this.json.tileheight
}
get columns() {
return this.json.columns
}
tileOffset(index) {
return [
((index % this.columns) * this.tileWidth),
(Math.floor(index / this.columns) * this.tileHeight)
]
}
}

14
tilesets/kenney_rpg.tsj Normal file
View File

@ -0,0 +1,14 @@
{ "columns":20,
"image":"..\/assets\/RPGpack_sheet.png",
"imageheight":832,
"imagewidth":1280,
"margin":0,
"name":"kenney_rpg",
"spacing":0,
"tilecount":260,
"tiledversion":"1.11.2",
"tileheight":64,
"tilewidth":64,
"type":"tileset",
"version":"1.10"
}

14
zelder.tiled-project Normal file
View File

@ -0,0 +1,14 @@
{
"automappingRulesFile": "",
"commands": [
],
"compatibilityVersion": 1100,
"extensionsPath": "extensions",
"folders": [
"."
],
"properties": [
],
"propertyTypes": [
]
}