From 7f0bd17b9e56ce79b6c9050a8fbeb0576ce2d3d4 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Wed, 2 Sep 2026 08:09:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(scripts):=20computeLpos=20/=20computeConte?= =?UTF-8?q?ntSize=20-=20=E9=94=9A=E7=82=B9=E8=BD=AC=E6=8D=A2=E5=85=AC?= =?UTF-8?q?=E5=BC=8F=EF=BC=88=E5=AE=9E=E6=B5=8B=E9=AA=8C=E8=AF=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coord-calculator.test.mjs | 50 +++++++++++++++++++ .../scripts/legacy-layer-to-cocos-prefab.mjs | 43 ++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 cocoscreator_projects/framework-tests/legacy-layer-migration/coord-calculator.test.mjs diff --git a/cocoscreator_projects/framework-tests/legacy-layer-migration/coord-calculator.test.mjs b/cocoscreator_projects/framework-tests/legacy-layer-migration/coord-calculator.test.mjs new file mode 100644 index 0000000..1e6e157 --- /dev/null +++ b/cocoscreator_projects/framework-tests/legacy-layer-migration/coord-calculator.test.mjs @@ -0,0 +1,50 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { computeLpos, computeContentSize } from '../../scripts/legacy-layer-to-cocos-prefab.mjs'; + +test('computeLpos 锚点转换(实测验证:渠道logo)', () => { + const spirit = { x: '734', y: '143', w: '59', h: '148' }; + const lpos = computeLpos(spirit); + // x = 734 + 59/2 - 640 = 123.5 + // y = 360 - (143 + 148/2) = 143 + assert.equal(lpos.x, 123.5); + assert.equal(lpos.y, 143); + assert.equal(lpos.z, 0); +}); + +test('computeLpos 锚点转换(实测验证:游客登录)', () => { + const spirit = { x: '540', y: '388', w: '320', h: '95' }; + const lpos = computeLpos(spirit); + // x = 540 + 160 - 640 = 60 + // y = 360 - (388 + 47.5) = -75.5 + assert.equal(lpos.x, 60); + assert.equal(lpos.y, -75.5); +}); + +test('computeLpos 字符串转数字(X/Y 为字符串 "0"/"-100")', () => { + const spirit = { x: '0', y: '-100', w: '100', h: '100' }; + const lpos = computeLpos(spirit); + // x = 0 + 50 - 640 = -590 + // y = 360 - (-100 + 50) = 410 + assert.equal(lpos.x, -590); + assert.equal(lpos.y, 410); +}); + +test('computeContentSize 直接读 Width/Height(SizeCalcMode=0 路径)', () => { + const spirit = { w: '320', h: '95', sizeCalcMode: '0' }; + const size = computeContentSize(spirit); + assert.equal(size.width, 320); + assert.equal(size.height, 95); +}); + +test('computeContentSize 默认 SizeCalcMode=0(字段缺失视作 0)', () => { + const spirit = { w: '100', h: '50' }; // no sizeCalcMode + const size = computeContentSize(spirit); + assert.equal(size.width, 100); + assert.equal(size.height, 50); +}); + +test('computeContentSize SizeCalcMode=1 → throw(不静默)', () => { + const spirit = { w: '100', h: '50', sizeCalcMode: '1' }; + assert.throws(() => computeContentSize(spirit), /SizeCalcMode=1.*reverse gameabc\.min\.js/); +}); \ No newline at end of file diff --git a/cocoscreator_projects/scripts/legacy-layer-to-cocos-prefab.mjs b/cocoscreator_projects/scripts/legacy-layer-to-cocos-prefab.mjs index 7bc36b5..1d64032 100644 --- a/cocoscreator_projects/scripts/legacy-layer-to-cocos-prefab.mjs +++ b/cocoscreator_projects/scripts/legacy-layer-to-cocos-prefab.mjs @@ -53,3 +53,46 @@ export function readLayerXml(path) { return { layerId, layerName, spirits }; } + +/** + * 锚点转换: 原坐标系 (left-top, 1280×720 绝对) → Cocos 中心锚点 + Y-up. + * + * 公式 (实测验证 2026-09-02, Login_Layer 渠道logo + 游客登录双节点 _lpos 匹配): + * cocos_lpos.x = original_X + original_W/2 - 640 # 1280/2 + * cocos_lpos.y = 360 - (original_Y + original_H/2) # Y-up 翻转 + 720/2 + * + * @param {{ x?: string, y?: string, w?: string, h?: string }} spirit + * @returns {{ x: number, y: number, z: 0 }} + */ +export function computeLpos(spirit) { + const x = parseFloat(spirit.x || '0'); + const y = parseFloat(spirit.y || '0'); + const w = parseFloat(spirit.w || '0'); + const h = parseFloat(spirit.h || '0'); + // 锚点转换:原 X/Y 是左上角(1280×720 绝对坐标)→ Cocos 中心锚点 + return { + x: x + w / 2 - 640, // 原坐标系半宽 1280/2 + y: 360 - (y + h / 2), // Y-up 翻转 + 半高 720/2 + z: 0, + }; +} + +/** + * 计算 Spirit 的 contentSize (Cocos UI 节点尺寸). + * + * 数据源权威: SizeCalcMode 缺失视作 0 (直接读 Width/Height); 非 0 抛错, + * 由上游显式决定是否逆向 gameabc.min.js (Task 5 范围, 本任务不支持). + * + * @param {{ w?: string, h?: string, sizeCalcMode?: string, id?: string, name?: string }} spirit + * @returns {{ width: number, height: number }} + */ +export function computeContentSize(spirit) { + const sizeCalcMode = parseInt(spirit.sizeCalcMode || '0', 10); + if (sizeCalcMode !== 0) { + throw new Error(`SizeCalcMode=${sizeCalcMode} not yet supported for Spirit ${spirit.id} (${spirit.name}) - reverse gameabc.min.js first (Task 5)`); + } + return { + width: parseFloat(spirit.w || '0'), + height: parseFloat(spirit.h || '0'), + }; +}