@hiddentao/clockwork-engine
Version:
A TypeScript/PIXI.js game engine for deterministic, replayable games with built-in rendering
86 lines (85 loc) • 3.31 kB
JavaScript
/**
* Spritesheet wrapper that provides high-level API for working with spritesheets.
* Wraps the platform RenderingLayer's low-level spritesheet methods.
*
* Example usage:
* ```typescript
* const sheet = await Spritesheet.load(
* loader,
* rendering,
* 'player.png',
* 'player.json' // optional, will try imageFile + '.json' if omitted
* )
*
* const idleFrame = sheet.getTexture('player_idle_0')
* const walkFrames = sheet.getAnimationFrames('player_walk_')
* ```
*/
export class Spritesheet {
constructor(spritesheetId, rendering) {
this.spritesheetId = spritesheetId;
this.rendering = rendering;
}
/**
* Load a spritesheet from image and JSON data.
*
* @param loader - Loader instance for fetching asset data
* @param rendering - RenderingLayer for loading spritesheet
* @param imageFile - Path to spritesheet image file
* @param jsonFile - Optional path to JSON metadata. If omitted, uses imageFile + '.json'
* @returns Promise resolving to Spritesheet instance
*/
static async load(loader, rendering, imageFile, jsonFile) {
// Load image data (loader returns data URL for binary assets)
const imageUrl = await loader.fetchData(imageFile);
// Load JSON data - use provided path or derive from imageFile
const jsonPath = jsonFile || `${imageFile.replace(/\.[^.]+$/, "")}.json`;
const jsonContent = await loader.fetchData(jsonPath);
// Parse JSON if it's a string (handle empty string for headless mode)
let jsonData;
if (typeof jsonContent === "string") {
jsonData = jsonContent === "" ? { frames: {} } : JSON.parse(jsonContent);
}
else {
jsonData = jsonContent;
}
// Load spritesheet via rendering layer
const spritesheetId = await rendering.loadSpritesheet(imageUrl, jsonData);
return new Spritesheet(spritesheetId, rendering);
}
/**
* Get a texture by frame name.
*
* @param name - Frame name from the spritesheet JSON
* @returns TextureId if found, undefined otherwise
*/
getTexture(name) {
const textureId = this.rendering.getTexture(this.spritesheetId, name);
return textureId !== null ? textureId : undefined;
}
/**
* Get all texture frames matching a prefix (useful for animations).
* Frames are returned in alphabetical order by frame name.
*
* @param prefix - Frame name prefix (e.g., 'player_walk_' to match 'player_walk_0', 'player_walk_1', etc.)
* @returns Array of TextureIds matching the prefix
*/
getAnimationFrames(prefix) {
// This is a simple implementation that tries sequential numbering
// More sophisticated implementations could introspect the spritesheet JSON
const frames = [];
let index = 0;
// Try up to 1000 frames (reasonable limit)
while (index < 1000) {
const frameName = `${prefix}${index}`;
const texture = this.rendering.getTexture(this.spritesheetId, frameName);
if (texture === null) {
// No more frames found
break;
}
frames.push(texture);
index++;
}
return frames;
}
}