Add separate loading support for external Tilesets
Closed this issue · 1 comments
Context
In Tiled, tilesets can be external, which means they can be shared across multiple maps. By allowing the separate loading of these external tilesets, we can optimize asset loading by ensuring that the same tileset isn't loaded multiple times. Furthermore, for certain use-cases, there's a need to load tilesets without associating them with any map.
Proposal
Introduce a TiledTilesetResource
class that would allow for the separate loading of tilesets, independent of the map they may be associated with. This would not only cater to external tilesets in Tiled but also provide more flexibility for developers who may want to load tilesets without a map.
Here's a possible implementation of the TiledTilesetResource
class:
import { Resource, ImageSource, SpriteSheet } from 'excalibur';
import { TiledTileset, RawTiledTileset, parseExternalTsx, parseExternalJson } from '@excaliburjs/plugin-tiled';
import type { Loadable } from 'excalibur';
export class TiledTilesetResource implements Loadable<TiledTileset | null> {
protected _resource: Resource<RawTiledTileset>
/**
* Data associated with a loadable
*/
public data: TiledTileset | null = null;
public image: ImageSource | null = null;
public spriteSheet: SpriteSheet | null = null;
get raw() {
return this._resource.data;
}
constructor(public path: string, public isJson: boolean = false) {
this._resource = new Resource<RawTiledTileset>(path, isJson ? 'json' : 'text');
}
/**
* Begins loading the resource and returns a promise to be resolved on completion
*/
public async load(): Promise<TiledTileset> {
let rawTileSet = await this._resource.load();
if (this.isJson) {
this.data = parseExternalJson(rawTileSet, 0, this.path);
} else {
this.data = parseExternalTsx(rawTileSet as unknown as string, 0, this.path);
}
if(this.data.image) {
this.image = new ImageSource(this.convertPath(this.path, this.data.image));
await this.image.load();
const rows = this.data.tileCount / this.data.columns;
this.spriteSheet = SpriteSheet.fromImageSource({
image: this.image,
grid: {
rows: rows,
columns: this.data.columns,
spriteWidth: this.data.tileWidth,
spriteHeight: this.data.tileHeight
},
});
}
return this.data;
}
/**
* Returns true if the loadable is loaded
*/
public isLoaded(): boolean {
return this._resource.isLoaded();
}
protected convertPath(originPath: string, relativePath: string) {
// Use absolute path if specified
if (relativePath.indexOf('/') === 0) {
return relativePath;
}
const originSplit = originPath.split('/');
const relativeSplit = relativePath.split('/');
// if origin path is a file, remove it so it's a directory
if (originSplit[originSplit.length - 1].includes('.')) {
originSplit.pop();
}
return originSplit.concat(relativeSplit).join('/');
}
}
With this class in place, developers can have better control over the loading of tilesets, thus enhancing the efficiency and flexibility of asset management.