diff --git a/src/pages/agent-office/canvas/SpriteLoader.js b/src/pages/agent-office/canvas/SpriteLoader.js new file mode 100644 index 0000000..64e72d8 --- /dev/null +++ b/src/pages/agent-office/canvas/SpriteLoader.js @@ -0,0 +1,77 @@ +// src/pages/agent-office/canvas/SpriteLoader.js + +import { ProceduralSprite } from './ProceduralSprite.js'; + +/** + * 스프라이트 로더 — PNG 스프라이트시트가 있으면 사용, 없으면 프로시저럴 폴백 + * + * 스프라이트시트 규격 (Phase 2): + * - 프레임 크기: 16×32px + * - 행: 방향 (0=down, 1=up, 2=right) + * - 열: 상태별 프레임 (idle 2 | walk 4 | type 2 | wait 2 | break 2 = 12열) + */ +export class SpriteLoader { + constructor() { + this.sprites = new Map(); // agentId → { image: Image, loaded: boolean } + } + + /** PNG 스프라이트시트 로드 시도 */ + async tryLoad(agentId, url) { + return new Promise((resolve) => { + const img = new Image(); + img.onload = () => { + this.sprites.set(agentId, { image: img, loaded: true }); + resolve(true); + }; + img.onerror = () => { + resolve(false); // 폴백 사용 + }; + img.src = url; + }); + } + + hasSprite(agentId) { + return this.sprites.has(agentId) && this.sprites.get(agentId).loaded; + } + + /** + * 에이전트 1프레임 그리기 (스프라이트 또는 프로시저럴) + */ + draw(ctx, agentId, state, direction, frame, x, y, scale) { + if (this.hasSprite(agentId)) { + this._drawFromSheet(ctx, agentId, state, direction, frame, x, y, scale); + } else { + ProceduralSprite.draw(ctx, agentId, state, direction, frame, x, y, scale); + } + } + + _drawFromSheet(ctx, agentId, state, direction, frame, x, y, scale) { + const { image } = this.sprites.get(agentId); + const frameW = 16; + const frameH = 32; + + // 방향 → 행 + const dirRow = direction === 'up' ? 1 : direction === 'right' || direction === 'left' ? 2 : 0; + + // 상태 → 열 오프셋 + const stateOffsets = { idle: 0, walk: 2, type: 6, wait: 8, break_anim: 10 }; + const mappedState = state === 'working' ? 'type' : state === 'waiting' ? 'wait' + : state === 'reporting' ? 'type' : state === 'break' ? 'break_anim' + : state === 'walk' ? 'walk' : 'idle'; + const colOffset = stateOffsets[mappedState] || 0; + + const srcX = (colOffset + frame) * frameW; + const srcY = dirRow * frameH; + const destW = frameW * scale; + const destH = frameH * scale; + + ctx.save(); + if (direction === 'left') { + ctx.translate(x, 0); + ctx.scale(-1, 1); + ctx.translate(-x, 0); + } + ctx.drawImage(image, srcX, srcY, frameW, frameH, x - destW / 2, y - destH, destW, destH); + ctx.restore(); + } +}