/** 图集位置由同版本远程 plist 决定,不再内置固定裁剪坐标。 */ export const GAMEPLAY_ATLAS_PATHS = ["BlockBtnUI", "door", "down"]; export interface GameplayAtlasLayout { width: number; height: number; // [x, y, width, height, rotated, offsetX, offsetY, originalWidth, originalHeight] frames: { [name: string]: number[] }; } export function isGameplayAtlas(path: string): boolean { return GAMEPLAY_ATLAS_PATHS.indexOf(path) >= 0; } /** Cocos 2.4 的 plist parser 已将 XML 转为对象;这里检查 TexturePacker format 2 的布局。 */ export function parseGameplayAtlas(data: any, atlas: string): GameplayAtlasLayout { const fail = () => new Error("图集 plist 无效或与 PNG 不匹配: " + atlas); if (!data || !data.metadata || data.metadata.format !== 2 || !data.frames) throw fail(); const numbers = (value: any, count: number): number[] => { if (typeof value !== "string" || !/^[\s{},.\d-]+$/.test(value)) throw fail(); const matches = value.match(/-?\d+(?:\.\d+)?/g) || []; const result = matches.map(Number); if (result.length !== count || result.some(n => !Number.isFinite(n))) throw fail(); return result; }; const size = numbers(data.metadata.size, 2); if (size.some(n => n <= 0 || !Number.isInteger(n))) throw fail(); const textureName = data.metadata.textureFileName || data.metadata.realTextureFileName; if (textureName !== atlas + ".png") throw fail(); const frames: { [name: string]: number[] } = Object.create(null); for (const filename of Object.keys(data.frames)) { const source = data.frames[filename]; const rect = numbers(source.frame, 4); const offset = numbers(source.offset, 2); const original = numbers(source.sourceSize, 2); if (typeof source.rotated !== "boolean") throw fail(); const rotated = source.rotated; const [x, y, w, h] = rect; if (rect.some(n => !Number.isInteger(n)) || x < 0 || y < 0 || w <= 0 || h <= 0 || original.some(n => n <= 0 || !Number.isInteger(n)) || x + (rotated ? h : w) > size[0] || y + (rotated ? w : h) > size[1]) throw fail(); const name = filename.replace(/\.png$/, ""); if (!name || frames[name]) throw fail(); frames[name] = rect.concat(rotated ? 1 : 0, offset, original); } if (!Object.keys(frames).length) throw fail(); return { width: size[0], height: size[1], frames }; }