feat(scripts): PoC 阶段 2 - Layer10_Protocol + Widget 锚定

computeWidgetAlignment(spirit) 简化版 (水平+垂直居中):
  _alignFlags = 16 | 8
  _horizontalCenter = x + w/2 - 640
  _verticalCenter = 360 - (y + h/2)

emitPrefabJson 仅在 Spirit 父节点是 group (非 Layer 根) 时调用;
root / group-X envelope 沿用默认 _alignFlags=45 全屏铺满.

PoC 验证: Layer00010.xml Protol_Layer (group 37, 4 spirits)
生成 Protol_Layer.prefab — 6 Widget 节点 (1 root + 1 group-37 + 4 spirit),
4 spirit widget 全部 _alignFlags=24.
This commit is contained in:
2026-09-02 08:35:03 +08:00
parent ba5aec9bca
commit e8f4180483
4 changed files with 2322 additions and 4 deletions
@@ -114,6 +114,39 @@ export function computeContentSize(spirit) {
};
}
/**
* 计算 Spirit 在其所属 group 内的 cc.Widget 锚定参数 (PoC 阶段 2 简化版).
*
* 数据源权威: 公式仅处理 Spirit.Y < 360 (上) vs > 360 (下) 与水平偏移, 完全基于
* 已知字段 (x/y/w/h). SizeCalcMode / CoordRelaX / CoordRelaY 等逆向未出的字段
* 暂不支持 — 由 buildSpiritBlock 调用方保证父 group 已经处理 SizeCalcMode ≠ 0.
*
* 简化规则:
* _alignFlags = 16 | 8 # 16=水平居中, 8=垂直居中
* _horizontalCenter = x + w/2 - 640 # 像素偏移, 1280 坐标系水平中心
* _verticalCenter = 360 - (y + h/2) # Y-up 翻转 + 720 坐标系垂直中心
*
* @param {{ x?: string, y?: string, w?: string, h?: string }} spirit
* @returns {object} cc.Widget 字段 (除 __type__/node/__prefab/_id 外)
*/
export function computeWidgetAlignment(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');
return {
_alignFlags: 16 | 8, // 水平居中 + 垂直居中
_target: null,
_left: 0, _right: 0, _top: 0, _bottom: 0,
_horizontalCenter: x + w / 2 - 640, // 1280 体系半宽
_verticalCenter: 360 - (y + h / 2), // Y-up + 720 体系半高
_isAbsLeft: true, _isAbsRight: true, _isAbsTop: true, _isAbsBottom: true,
_isAbsHorizontalCenter: true, _isAbsVerticalCenter: true,
_originalWidth: 0, _originalHeight: 0,
_alignMode: 2, _lockFlags: 0,
};
}
/**
* 将 Spirit 记录按 BelongGroupID 归类, 生成 group-X 父节点集合 + Layer 根直挂子节点.
*
@@ -365,6 +398,25 @@ function makeWidget(nodeId, compPrefabId) {
};
}
// Spirit 专用的 Widget 工厂: 调用 computeWidgetAlignment 计算锚定参数.
// 用法: buildSpiritBlock 在父节点是 group 时调用此工厂, 替代默认 makeWidget.
// 注意: 此工厂只应在 parent 确实是 group 容器 (而非 Layer 根) 时调用 — 见
// buildSpiritBlock 的入参语义. Layer 根与 group-X 末尾 envelope 仍用 makeWidget.
function makeSpiritWidget(nodeId, compPrefabId, spirit) {
const align = computeWidgetAlignment(spirit);
return {
__type__: 'cc.Widget',
_name: '',
_objFlags: 0,
__editorExtras__: {},
node: { __id__: nodeId },
_enabled: true,
__prefab: { __id__: compPrefabId },
...align, // _alignFlags, _horizontal/verticalCenter...
_id: '',
};
}
function makeSprite(nodeId, compPrefabId, spriteFrameUuid, type = 0) {
return {
__type__: 'cc.Sprite',
@@ -574,7 +626,7 @@ export function emitPrefabJson(layer) {
if (!spirit) {
throw new Error(`emitPrefabJson: spirit id ${spiritId} referenced by ${gi.group.name} but not found in layer.spirits`);
}
const info = buildSpiritBlock(spirit, { __id__: gi.nodeId }, out);
const info = buildSpiritBlock(spirit, { __id__: gi.nodeId }, out, { parentIsGroup: true });
spiritInfo.set(spiritId, info);
gi.node._children.push({ __id__: info.nodeId });
}
@@ -585,7 +637,7 @@ export function emitPrefabJson(layer) {
if (!spirit) {
throw new Error(`emitPrefabJson: root child spirit id ${spiritId} not found in layer.spirits`);
}
const info = buildSpiritBlock(spirit, { __id__: rootId }, out);
const info = buildSpiritBlock(spirit, { __id__: rootId }, out, { parentIsGroup: false });
spiritInfo.set(spiritId, info);
rootNode._children.push({ __id__: info.nodeId });
}
@@ -605,8 +657,15 @@ export function emitPrefabJson(layer) {
* 构造单个 Spirit 的所有对象 (Node + UITransform + CompPrefabInfo + 主组件 +
* CompPrefabInfo + Widget + CompPrefabInfo + PrefabInfo), 按 schema 顺序 push
* 到 out 数组, 返回关键 id 供外层 wiring.
*
* @param {object} spirit - Spirit 字段
* @param {{ __id__: number }} parentRef - 父节点引用
* @param {Array<object>} out - 输出 JSON 数组
* @param {{ parentIsGroup: boolean }} [opts] - parentIsGroup=true 时 Widget
* 走 computeWidgetAlignment (锚定 = 水平/垂直居中); false (Layer 根直挂)
* 走默认 makeWidget (_alignFlags=45 全屏铺满).
*/
function buildSpiritBlock(spirit, parentRef, out) {
function buildSpiritBlock(spirit, parentRef, out, opts = { parentIsGroup: false }) {
const lpos = computeLpos(spirit);
const size = computeContentSize(spirit);
const componentName = mapSpiritTypeToComponent(spirit.type);
@@ -656,7 +715,11 @@ function buildSpiritBlock(spirit, parentRef, out) {
const widgetCompInfo = makeCompPrefabInfo();
out.push(widgetCompInfo);
const widgetCompInfoId = out.length - 1;
const widget = makeWidget(nodeId, widgetCompInfoId);
// parentIsGroup 时走 computeWidgetAlignment (水平+垂直居中); Layer 根直挂时
// 用默认 _alignFlags=45 (全屏铺满占位, 后续 PoC 阶段再细化).
const widget = opts.parentIsGroup
? makeSpiritWidget(nodeId, widgetCompInfoId, spirit)
: makeWidget(nodeId, widgetCompInfoId);
out.push(widget);
const widgetId = out.length - 1;