49 lines
2.4 KiB
TypeScript
49 lines
2.4 KiB
TypeScript
/** 图集位置由同版本远程 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 };
|
|
}
|