UNPKG

psd2prefab

Version:

A tool to convert psd to a unity prefab.

243 lines 9.55 kB
import fs from 'fs-extra'; import path from 'path'; import env from './Env.js'; export class PrefabGenerator { async make(psd) { const instruction = { name: env.opts.name, children: [], width: psd.width, height: psd.height, x: 0, y: 0 }; const simStruct = { children: [] }; if (psd.children !== undefined) { if (env.opts.debug !== undefined) { await fs.ensureDir('test'); } for (const child of psd.children) { if (child.hidden === true) continue; simStruct.children.push(this.simplify(child)); } if (env.opts.debug !== undefined) { await fs.writeJSON(path.join('test', 'simplified.json'), simStruct, { spaces: 2 }); } for (const child of psd.children) { if (child.hidden === true) continue; await this.collectNodes(psd, instruction, child); } if (env.opts.debug !== undefined) { await fs.writeJSON(path.join('test', 'beforelefttop.json'), simStruct, { spaces: 2 }); } // convert position relative to parent left top for (const child of instruction.children) { this.toPositionRelativeToParentLeftTop(instruction, child); } if (env.opts.debug !== undefined) { await fs.writeJSON(path.join('test', 'beforeshake.json'), instruction, { spaces: 2 }); } // shake tree for (let i = instruction.children.length - 1; i >= 0; i--) { const node = instruction.children[i]; this.treeShake(instruction, i, node); } if (env.opts.debug !== undefined) { await fs.writeJSON(path.join('test', 'beforefixnosize.json'), instruction, { spaces: 2 }); } // convert position relative to parent center for (const child of instruction.children) { this.toPositionRelativeToParentCenter(instruction, child); } if (env.opts.debug !== undefined) { await fs.writeJSON(path.join('test', 'finished.json'), instruction, { spaces: 2 }); } } const prefabRoot = path.join(env.opts.project, env.cfg.PREFAB_ROOT); await fs.ensureDir(prefabRoot); await fs.writeJSON(path.join(prefabRoot, env.opts.name + '.json'), instruction, { spaces: 2 }); } simplify(node) { const snode = { name: node.name, left: node.left, top: node.top, right: node.right, bottom: node.bottom, children: [] }; if (node.children !== undefined) { for (let i = node.children.length - 1; i >= 0; i--) { const child = node.children[i]; if (child.hidden === true) { // remove hidden nodes node.children.splice(i, 1); continue; } snode.children.push(this.simplify(child)); } } return snode; } async collectNodes(psd, parentNode, layer) { // remove blank layer if (layer.children === undefined && layer.canvas === undefined) return; const left = layer.left ?? 0; const top = layer.top ?? 0; const right = layer.right ?? 0; const bottom = layer.bottom ?? 0; const x = left; // (left + right - psd.width) / 2 const y = top; // -(top + bottom - psd.height) / 2 const width = right - left; const height = bottom - top; let type; if (layer.text !== undefined && !this.isArtFont(layer)) { type = 'Text'; } else if (layer.canvas !== undefined) { type = 'Image'; } else { type = 'GameObject'; } const node = { type, width, height, x, y, children: [] }; if (layer.name !== undefined) node.name = layer.name; if (layer.text !== undefined && type === 'Text') { const textDetail = { text: layer.text.text }; if (layer.text.style !== undefined) { textDetail.style = this.toITextStyle(layer.text.style, layer.text.transform); } if (layer.text.styleRuns !== undefined) { const styleRuns = []; for (const sr of layer.text.styleRuns) { styleRuns.push({ length: sr.length, style: this.toITextStyle(sr.style) }); } textDetail.styleRuns = styleRuns; } node.text = textDetail; } else if (layer.canvas !== undefined) { const layerAlias = layer; node.image = layerAlias.pngFile; } parentNode.children.push(node); if (layer.children !== undefined) { for (const child of layer.children) { if (child.hidden === true) continue; await this.collectNodes(psd, node, child); } } } treeShake(parent, index, node) { for (let i = node.children.length - 1; i >= 0; i--) { const child = node.children[i]; this.treeShake(node, i, child); } if (node.width === 0 && node.height === 0 && node.type === 'GameObject') { if (node.children.length <= 1) { // single-child node without size can be shake off console.log('shake off:', node.name); parent.children.splice(index, 1); for (const child of node.children) { child.x += node.x; child.y += node.y; parent.children.push(child); } } else { // replace with the the first child if it's the biggest const first = node.children[0]; const firstSize = first.width * first.height; let canShakeOff = true; for (let i = 1; i < node.children.length; i++) { const child = node.children[i]; const childSize = child.width * child.height; if (childSize === firstSize) { canShakeOff = false; break; } } if (canShakeOff) { console.log('shake off and replace with first child:', node.name); for (let i = 1; i < node.children.length; i++) { const child = node.children[i]; child.x -= first.x; child.y -= first.y; first.children.push(child); } first.x += node.x; first.y += node.y; parent.children.splice(index, 1, first); } else { // set the size and position equal to the first child node.width = first.width; node.height = first.height; const offsetX = first.x - node.x; const offsetY = first.y - node.y; node.x = first.x; node.y = first.y; for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; child.x -= offsetX; child.y -= offsetY; } } } } } toPositionRelativeToParentLeftTop(parent, node) { for (const child of node.children) { this.toPositionRelativeToParentLeftTop(node, child); } node.x -= parent.x; node.y -= parent.y; } toPositionRelativeToParentCenter(parent, node) { for (const child of node.children) { this.toPositionRelativeToParentCenter(node, child); } node.x = (node.x + node.x + node.width - parent.width) / 2; node.y = -(node.y + node.y + node.height - parent.height) / 2; } toITextStyle(style, transform) { const out = {}; if (style.font !== undefined) { out.font = style.font.name; } if (style.fontSize !== undefined) { let fontSize = style.fontSize; if (transform !== undefined) { // transform is suspected to be [xx, xy, yx, yy, tx, ty] fontSize = Math.round((fontSize * transform[3]) * 100) * 0.01; } out.fontSize = fontSize; } if (style.fillColor !== undefined) { out.color = style.fillColor; } if (style.strokeColor !== undefined) { out.strokeColor = style.strokeColor; } return out; } isArtFont(layer) { if (layer.canvas !== undefined && layer.text !== undefined && env.cfg.NOT_ART_FONTS !== undefined && layer.text.style?.font?.name !== undefined) { return !env.cfg.NOT_ART_FONTS.includes(layer.text.style.font.name); } return false; } } //# sourceMappingURL=PrefabGenerator.js.map