二七王:文档同步图集叠绘数字(清单 §1.3/§1.5/§3.6/§5.4/§6.8 + 前端 02 新增一节)

- docs_dev 清单:§3.6 数字资源改为「等宽连续排列的一张图」并给出字符顺序/单字宽高/整图尺寸,
  §1.3 说明叫分档位分数为何必须改叠绘(14 档里 13 档两位数、每档一个精灵),
  §1.5/§5.4 选主张数由 8 个精灵改回 4 个(1678–1681 记跳号不复用),
  §6.8 布局节点合并 + 新增 NUM_STYLE 数字样式表;各处均带 2026-08-26 修订说明与缘由。
- 前端 02:新增「三种显示机制:文字精灵 / 多帧图片精灵 / 图集叠绘」一节,
  给出三问选用判据、多位数误用 setFrame 的坑与真实事故、叠绘的三条前提与出图规格;
  §5 补一条「转发必须接上且参数对齐,否则事件静默失效」的警示;DO/DON'T 各补一行。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-26 23:56:32 +08:00
co-authored by Claude Opus 5
parent 2fe0f661b4
commit dbdd6607d7
2 changed files with 95 additions and 21 deletions
@@ -27,6 +27,8 @@ UI 组件 → SpriteManager(业务级 API:ID 范围校验 + 单位换算)
| `setScale(id, scale)` | 缩放 | 业务用倍数(`1.2`),框架自动转引擎百分比 |
| `setOpacity(id, o)` | 透明度 | 业务用 `0.0–1.0`,框架自动转 `0–255` |
| `setText(id, text)` / `setTextWithWidth(...)` | 文字 | 文字精灵 |
| `drawImage(id, imgId, dx,dy,dw,dh)` | 在精灵上叠绘整张图 | **只能在绘制回调里调**(见 §5) |
| `drawImageRegion(id, imgId, dx,dy,dw,dh, sx,sy,sw,sh)` | 在精灵上叠绘图片的**指定源矩形** | 从图集里裁一格再画,同上只在绘制回调里调 |
| `showGroup/hideGroup(gid)` | 群组批量 | 整块 UI 显隐 |
| `showLayer/hideLayer(lid)` | 图层批量 | 整个界面显隐 |
| `exists(id)` | 存在性检查 | |
@@ -55,6 +57,49 @@ SpriteManager.show(sid); SpriteManager.setFrame(sid, card.code - 1);
ID 必须与编辑器中实际存在的精灵**完全一致**。超范围 → 校验失败返回 `false`;编辑器里不存在的 ID → 操作静默无效。**绝不随意编造 ID。**
### 三种显示机制:文字精灵 / 多帧图片精灵 / 图集叠绘
同样是"在精灵上显示内容",引擎给了三条路,**能力边界完全不同,选错了会在后期变成返工**:
| 机制 | API | 一次能显示 | 字形 | 适用 |
|------|-----|-----------|------|------|
| **文字精灵** | `setText(id, text)` | **任意长度**的字符串 | **系统字体**(只能调字号/颜色,做不出描边、渐变、投影) | 纯数据型文本:昵称、局数、`N对`/`主N` 角标、`2子`、提示文案 |
| **多帧图片精灵** | `setFrame(id, frame)` | **一帧**(整张精灵就是那一帧) | 美术出图 | **状态切换**:按钮可用/置灰、单选框选中/未选、花色图标、牌面(一张牌 = 一帧) |
| **图集叠绘** | `NumberRenderer`(底层 `drawImageRegion`) | **一行任意个字符**,都画在**同一个**精灵内 | 美术出图 | **多位美术字**:倒计时、结算得分、叫分档位分数、张数、比分 |
**选用判据(按顺序问自己三个问题)**:
1. **要不要美术字形**(描边/渐变/投影)?不要 → **文字精灵**,到此为止,最省。
2. 要美术字形,那**一次只显示一个符号**吗(一张牌、一个图标、一个按钮态)?是 → **多帧图片精灵** + `setFrame`。
3. 要美术字形、且**可能不止一个字符**(两位数、带正负号、可变长度)?→ **图集叠绘**。
> ⚠️ **最容易踩的坑:用多帧图片精灵去显示多位数。**
> `setFrame` 是"整个精灵显示图集的第 N 帧",**一个精灵一次只能显示一位数字**。于是两位数被迫拆成"十位精灵 + 个位精灵",三位数拆三个……精灵数量随位数线性膨胀,布局、显隐、清理全都要按位处理(十位为 0 还要单独隐藏),而**位数一变就得重排**。
> 真实事故:某处 14 个档位按钮各配了**一个**分数精灵,而 14 档里 13 档是两位数——按 `setFrame` 方案**根本渲染不出来**,直到改成叠绘才修好。
> **判据一句话:值可能超过一个字符,就不要用 `setFrame`。**
**图集叠绘怎么工作**:`GameABCUtils.Draw.drawImage` 支持**源矩形**,`SpriteManager.drawImageRegion` 把这能力暴露到业务层——可以从一张图里裁出任意区域,画到精灵内的任意位置。数字图出成**等宽连续排列的一行**,第 `idx` 格的源矩形就是 `(idx × 单字宽, 0, 单字宽, 单字高)`;把每一位分别裁出来、画到精灵内递增的 x 上,一个精灵就显示了一个多位数。
```js
// 框架的 NumberRenderer 封装了这套逻辑,业务侧只有三步(样式全部来自常量,禁裸值)
NumberRenderer.bind(SPRITE_ID, {
imageId: ImageResources.NUM_SCORE, // 等宽连续排列的数字图
charWidth: 30, charHeight: 40, // 图集单字宽高
spacing: 0, // 字间距(可为负 = 重叠)
align: 'center', width: 74 // 在绘制区内怎么对齐、绘制区多宽
});
NumberRenderer.setValue(SPRITE_ID, 70); // 只写值;引擎每帧回调时才真正画
NumberRenderer.unbind(SPRITE_ID); // 组件 onDestroy 里注销回调,防残留
```
**三条前提,缺一不可**:
- **叠绘只能在精灵的绘制回调里进行**(`SpriteEventController.registerDraw` 注册的 handler 内),在回调之外调 `drawImageRegion` 不会留下任何画面——因为引擎只在绘制那一帧接受叠绘。
- **绘制回调依赖平台事件链路接通**:`Game_Modify.gamemydraw` →(转发壳)→ 子游戏 hook → `SpriteEventController.handleDraw`。这条链断了,叠绘、按钮点击一起静默失效(见 §5)。
- **只对编辑器预置的数字 ID 精灵有效**:`SpriteCopyUtils` 复制出的字符串 ID 精灵不接收独立绘制事件,不能用 `NumberRenderer`;那种场合改用文字精灵,或由父容器统一叠绘。
**图集出图规格**(写进图片资源常量的注释里,供美术照做):字符**排列顺序**(如 `0…9` 后接 `+`、`-`)、**单字符宽高**、**整图尺寸**;字符必须严格等宽对齐格子、格间不留缝,否则裁出来的字会串位。
---
## 2. 资源与布局常量三件套
@@ -173,6 +218,8 @@ SpriteEventController.registerMouseMove(sprites.HAND_AREA, function (event) { se
支持的事件类型:`mouseDown` / `mouseDownNoMove`(长按) / `mouseUp` / `mouseMove`(拖拽) / `drawBegin` / `draw`。
> ⚠️ **转发这一段必须真的接上,且参数顺序要逐个对齐 `handleXxx` 的签名**。转发缺失或参数错位不会报错,只会让**所有**精灵交互与叠绘**静默失效**——按钮点了没反应、叠绘一片空白,却查不到任何异常日志。接线时对着 `SpriteEventController` 里各 `handleXxx` 的形参表逐个核对,转发层**只转发、不写业务**。
对**每个**绘制精灵统一处理(不针对某个固定 ID)用 `registerGlobalDraw(fn)`——框架对每个 draw 事件都回调它、但**不认识**其业务含义,保持中立(“精牌标记”这类玩法叠绘即以此挂载,框架零感知,见 05「框架中立」)。
### 更高层:手势识别 `SpriteGestureRecognizer`
@@ -217,7 +264,8 @@ list.onClick = function (type, rowIndex, rowData) { /* ... */ };
| DO ✅ | DON'T ❌ |
|------|---------|
| UI 只调 `SpriteManager` | 直接调 `GameABCUtils`/引擎原生 API |
| 一精灵多帧、`setFrame` 切换 | 为每种牌面建一个精灵 |
| 一精灵多帧、`setFrame` 切换**状态** | 为每种牌面建一个精灵 |
| 多位美术字用 `NumberRenderer` 叠绘,**一个精灵画完整个数** | 用 `setFrame` 显示多位数(一帧只有一位)/ 拆十位个位各建一个精灵 |
| 精灵 ID 1001–3000、群组 ≥201、从编辑器查证 | 随意编造 ID / 超范围 ID |
| 检查 `SpriteManager` 返回值 | 忽略 `false` 返回 |
| 精灵/资源/坐标全进常量文件 | 在业务代码内联裸数字 |
+46 -20
View File
@@ -535,10 +535,15 @@ EQW_RoomOptions = {
| 元素 | 精灵类型 | 说明 |
| --- | --- | --- |
| **叫分的分数**(70/65/…/5) | **数字精灵**(图片多帧,资源 `NUM_CALL_SCORE`) | 带描边、需与图一致的美术字 |
| **叫分的分数**(70/65/…/5) | **图集叠绘数字精灵**(资源 `NUM_CALL_SCORE`,样式 `NUM_STYLE.CALL_SCORE`) | 带描边、需与图一致的美术字;**一个精灵画完整两位数** |
| **角标的子数**(`2子`/`7子`…) | **文字精灵**(`SpriteManager.setText`) | 随爬坡开关动态变,值域 2–15,用文字精灵最省 |
故每档 **3 个精灵**:按钮底(含角标底)+ 分数数字精灵 + 角标文字精灵。
> **2026-08-26 修订(档位分数的实现方式)**:14 档里 **13 档是两位数**(70…10),而每档只有**一个**分数精灵。
> 多帧图片精灵靠 `setFrame` 切帧、**一次只显示一帧=只能显示一位数**,按原方案这 13 档**根本渲染不出来**(要么显示不全,要么被迫每档再加一个十位精灵、精灵数翻倍)。
> 因此改为**图集叠绘**:`NUM_CALL_SCORE` 出成等宽连续的一张图(§3.6),由框架 `NumberRenderer` 在精灵的绘制回调里按字宽逐位裁源矩形、画在**同一个**精灵内的不同 x 上。
> 于是 `CALL_BTN_NUM_1..14` **保持 14 个不变**,且现在能正确显示两位数。用法:`NumberRenderer.bind(spriteId, style)` → `setValue(spriteId, 70)`,样式取 `EQW_Layout.NUM_STYLE.CALL_SCORE`(§6.8)。
- 可选性规则(design §4.2,**由服务端权威 `currcall` 决定,前端只据其置灰、不自行推导规则**):
- 暂定庄家首叫:5–70 全部可选,**无 `不叫`**;
- 其后:只有**严格低于 `currcall`** 的档位可选,`不叫` 可选。
@@ -579,11 +584,13 @@ EQW_RoomOptions = {
**选主面板规格**(群组 221,号段 1650–1699):
> **2026-08-26 修订**:美术侧实际出图方式与原规格不符——花色按钮改为**一张图 5 帧**(帧1–4=四花色按钮,每帧已含按钮底+花色图标+右上角蓝色角标底;帧5=投降按钮,已含按钮底+「投降」字),花色图标、角标底、投降文字不再单独出精灵。按此改写以下条目,代码同步见 `ImageResources.js`/`Sprites_Action.js`/`Layout_Action.js`。
> **2026-08-26 修订(其一)**:美术侧实际出图方式与原规格不符——花色按钮改为**一张图 5 帧**(帧1–4=四花色按钮,每帧已含按钮底+花色图标+右上角蓝色角标底;帧5=投降按钮,已含按钮底+「投降」字),花色图标、角标底、投降文字不再单独出精灵。按此改写以下条目,代码同步见 `ImageResources.js`/`Sprites_Action.js`/`Layout_Action.js`。
>
> **2026-08-26 修订(其二)**:张数数字改为**图集叠绘**(§3.6)——一个精灵就能画完两位数,故**每花色一个数字精灵**(`MAIN_SUIT_COUNT_1..4` = 1674–1677),当日早前定的「十位/个位各一个精灵」(8 个)作废,空出的 1678–1681 不复用。
- 标题斜角条:`亮主` 金色字 + 倒计时数字。
- **4 个花色按钮**:每个按钮由**一个精灵**呈现,帧 = 花色序号 1–4(服务端 flower 编号),该帧已含「按钮底 + 花色图标 + 右上角蓝色角标底」;角标底之上叠加一个**对数文字精灵**(`N对`)。
- 按钮下方叠加**张数数字图片精灵**:该花色在庄家手中的**总张数**——两副牌单花色最多 26 张(两位数),故每个花色用**十位 + 个位两个数字图片精灵**(十位为 0 时隐藏),资源见 §3.6 新增的 `NUM_MAIN_SUIT_COUNT`
- 按钮下方叠加**张数数字精灵**:该花色在庄家手中的**总张数**——两副牌单花色最多 26 张(两位数),用**图集叠绘**在**一个**精灵上画完(`NumberRenderer`,无需拆十位/个位),资源见 §3.6 的 `NUM_MAIN_SUIT_COUNT`、样式见 §6.8 的 `NUM_STYLE.MAIN_SUIT_COUNT`
- 右上角标文字 = 该花色的**对子数**(design §4/§11 明确要求)
- **张数与对子数都由前端据 `MyCards`/`cards` 自行统计**,协议 §「ChooseMain」明确「服务端不额外下发」。
- **投降按钮**:仅 `touxiang === 1`(即叫分 70)时显示,与 4 个花色按钮并列;按钮为**一个精灵**(`BTN_SUIT_CHOOSE` 帧5,已含底与「投降」字),不再单独出文字精灵。
@@ -1168,18 +1175,24 @@ y≈648–720 深色条。左侧头像/昵称/分数由平台渲染(§0.2)
### 3.6 数字与符号
| ID | 键名 | 用途 | 帧数 | 帧说明 | 尺寸 |
| --- | --- | --- | --- | --- | --- |
| 631 | `NUM_COUNTDOWN` | 倒计时数字 | 10 | 帧1–10 = 0–9 | 44 × 60 |
| 635 | `NUM_CALL_SCORE` | **叫分档位的分数数字**(深棕描边美术字) | 10 | 帧1–10 = 0–9 | 30 × 40 |
| 632 | `NUM_RESULT_WIN` | 结算得分数字(赢,橙) | 12 | 帧1–10 = 0–9,帧11 = `+`,帧12 = `-` | 46 × 62 |
| 633 | `NUM_RESULT_LOSE` | 结算得分数字(输,蓝) | 12 | 同上 | 46 × 62 |
| 634 | `ANI_JUDGE` | **判定结果动画**(大光 / 小光 / 过庄 / 升N级 / 投降)——帧动画资源,播放参数进 `AnimationConfigs`【待设计 D-8】 | 待定 | 待定 | 待定 |
| 636 | `NUM_MAIN_SUIT_COUNT` | **选主面板花色张数数字**(美术单独出图,字号配色与以上几套不同;十位/个位各占一个精灵) | 10 | 帧1–10 = 0–9 | 24 × 32(估值) |
> **2026-08-26 修订(重要,出图方式变更)**:数字类资源 631/632/633/635/636 **不再是编辑器里配好帧数的多帧资源**,改为**等宽连续排列的一张图**——所有字符横向排成一行、每格等宽、格间不留缝。
> 缘由:多帧图片精灵靠 `setFrame` 切帧,**一个精灵一次只显示一帧=只能显示一位数**;两位数就得两个精灵(十位/个位),精灵数迅速膨胀,而 `CALL_BTN_NUM_1..14` 每档只有一个精灵、14 档里 13 档是两位数,**按多帧方案根本渲染不出来**。
> 新方案是**图集叠绘**:在精灵的绘制回调里按字宽裁源矩形,第 `idx` 格的源矩形 = `(idx × 单字宽, 0, 单字宽, 单字高)`,逐位画在**同一个**精灵上,于是**一个多位数只用一个精灵**。实现见框架 `gameabc-framework/ui/NumberRenderer.js`(`bind` / `setValue` / `clear` / `unbind`)与 `SpriteManager.drawImageRegion`;三种显示机制的选用判据见前端文档 `02-渲染与UI组件体系.md`。
> **样式(单字宽高 / 字间距 / 对齐 / 绘制区宽)在 `EQW_Layout.NUM_STYLE.*`(`LayoutConstants.js`)集中配置**,本表只记出图规格。
> **带描边/渐变的美术字用图片多帧精灵**(倒计时、结算得分、叫分分数、选主张数),文字精灵做不出这种效果——631–636 都要出图。
| ID | 键名 | 用途 | 字符排列(等宽连续一行) | 单字符尺寸 | 整图尺寸 |
| --- | --- | --- | --- | --- | --- |
| 631 | `NUM_COUNTDOWN` | 倒计时数字 | `0 1 2 3 4 5 6 7 8 9`(索引 0–9) | 44 × 60 | **440 × 60**(10 格) |
| 635 | `NUM_CALL_SCORE` | **叫分档位的分数数字**(深棕描边美术字) | `0 1 2 3 4 5 6 7 8 9`(索引 0–9) | 30 × 40 | **300 × 40**(10 格) |
| 632 | `NUM_RESULT_WIN` | 结算得分数字(赢,橙) | `0 1 2 3 4 5 6 7 8 9 + -`(索引 0–9 数字、10 = `+`、11 = `-`) | 46 × 62 | **552 × 62**(12 格) |
| 633 | `NUM_RESULT_LOSE` | 结算得分数字(输,蓝) | 与 632 完全一致,仅配色不同 | 46 × 62 | **552 × 62**(12 格) |
| 634 | `ANI_JUDGE` | **判定结果动画**(大光 / 小光 / 过庄 / 升N级 / 投降)——帧动画资源,播放参数进 `AnimationConfigs`【待设计 D-8】。**注意:这是帧动画,不属于本节的等宽数字图** | 待定 | 待定 | 待定 |
| 636 | `NUM_MAIN_SUIT_COUNT` | **选主面板花色张数数字**(美术单独出图,字号配色与以上几套不同;**一个花色一个精灵**) | `0 1 2 3 4 5 6 7 8 9`(索引 0–9) | 24 × 32(估值) | **240 × 32**(10 格) |
> **出图要求**:字符必须**严格等宽对齐格子**(第 N 个字符的左边缘恰好在 `N × 单字宽`),否则裁出来的字会串位;格与格之间**不留间隙**,字间距由代码的 `spacing` 控制(可为负=重叠)。
> **带描边/渐变的美术字用图集叠绘数字精灵**(倒计时、结算得分、叫分分数、选主张数),文字精灵做不出这种效果——631–636 都要出图。
> **纯数据型小字用文字精灵**(`SpriteManager.setText`):叫分角标的子数(值域 2–15,随爬坡开关变)、`主N`/`对N` 角标、局数、亮牌统计等。
> **2026-08-26 新增 636**:选主张数原计划走文字精灵,但美术侧单独出了一套数字图(字号配色不同于既有几套),且张数最多两位(两副牌单花色最多 26 张),故改为图片数字精灵、十位个位各一个。
> **2026-08-26 新增 636**:选主张数原计划走文字精灵,但美术侧单独出了一套数字图(字号配色不同于既有几套),且张数最多两位(两副牌单花色最多 26 张);同日改为图集叠绘后,**十位/个位不再拆两个精灵**,每花色一个精灵即可。
### 3.7 浮层与气泡
@@ -1308,7 +1321,7 @@ design 与协议均**未定义任何音效**。以下是按牌类游戏常规推
| | 1601 | `CALL_HINT_BG` | 图片 | 提示条底 | `BAR_HINT_TOP` |
| | 1602 | `CALL_HINT_TEXT` | 文字 | `70分坐庄才可投降` | —— |
| | 1603–1616 | `CALL_BTN_1..14` | 图片 ×14 | 档位按钮底(**含角标底**,70…5) | `BTN_CALL_SCORE` 帧1–3 按档位位置固定 / 帧4 灰 |
| | 1617–1630 | `CALL_BTN_NUM_1..14` | **图片(数字精灵)** ×14 | 档位分数(70…5) | `NUM_CALL_SCORE` |
| | 1617–1630 | `CALL_BTN_NUM_1..14` | **图集叠绘数字精灵** ×14 | 档位分数(70…5),**一个精灵画完整两位数**(2026-08-26,见 §3.6) | `NUM_CALL_SCORE` + `NUM_STYLE.CALL_SCORE` |
| | 1631–1644 | `CALL_BTN_BADGE_1..14` | **文字** ×14 | 该档 `multiple` 子数(`2子`…`15子`),随爬坡开关变 | —— |
| | 1645 | `CALL_BTN_NONE` | 图片 | `不叫` | `BTN_NO_CALL` |
| | 1646 | `CALL_BTN_NONE_TEXT` | 文字 | —— | —— |
@@ -1316,9 +1329,10 @@ design 与协议均**未定义任何音效**。以下是按牌类游戏常规推
| | 1651 | `MAIN_TITLE_TEXT` | 文字 | `亮主` | —— |
| | 1652–1655 | `MAIN_SUIT_BTN_1..4` | 图片 ×4 | 花色按钮(**已含按钮底+花色图标+角标底**),帧=花色序1–4 | `BTN_SUIT_CHOOSE` 帧1–4 |
| | 1668–1671 | `MAIN_SUIT_PAIR_TEXT_1..4` | 文字 ×4 | `N对`,叠在对应按钮角标底之上 | —— |
| | 1674–1681 | `MAIN_SUIT_COUNT_1..4_TENS/ONES` | **图片(数字精灵)** ×8 | 该花色在庄家手中的**张数**(两位数,十位/个位各一个精灵,十位为 0 隐藏) | `NUM_MAIN_SUIT_COUNT` |
| | 1674–1677 | `MAIN_SUIT_COUNT_1..4` | **图集叠绘数字精灵** ×4 | 该花色在庄家手中的**张数**(最多两位,**一个花色一个精灵**;2026-08-26 由十位/个位 8 个精灵改回 4 个,见 §3.6) | `NUM_MAIN_SUIT_COUNT` + `NUM_STYLE.MAIN_SUIT_COUNT` |
| | 1672 | `MAIN_BTN_SURRENDER` | 图片 | `投降`(**已含按钮底+文字**) | `BTN_SUIT_CHOOSE` 帧5 |
| | ~~1656–1659 / 1660–1663 / 1664–1667 / 1673~~ | ~~`MAIN_SUIT_ICON_1..4` / 原 `MAIN_SUIT_COUNT_1..4` / `MAIN_SUIT_PAIR_BG_1..4` / `MAIN_BTN_SURRENDER_TEXT`~~ | —— | **不需要**:分别并入按钮帧或改为上方 8 个数字精灵,空出的 ID 不复用 | —— |
| | ~~1656–1659 / 1660–1663 / 1664–1667 / 1673~~ | ~~`MAIN_SUIT_ICON_1..4` / 原 `MAIN_SUIT_COUNT_1..4`(文字精灵)/ `MAIN_SUIT_PAIR_BG_1..4` / `MAIN_BTN_SURRENDER_TEXT`~~ | —— | **不需要**:分别并入按钮帧、或改为上方 1674–1677 的数字精灵,空出的 ID 不复用 | —— |
| | ~~1678–1681~~ | ~~`MAIN_SUIT_COUNT_3/4_TENS/ONES`~~ | —— | **不需要**(2026-08-26):改用图集叠绘后一个精灵就能画两位数,十位/个位拆分作废,空出的 4 个 ID 不复用 | —— |
| **222** 埋牌操作条 | 1700 | `BURY_BTN_TIP` | 图片 | `提示` | `BTN_TIP` |
| | 1701 | `BURY_BTN_TIP_TEXT` | 文字 | —— | —— |
| | 1702 | `BURY_BTN_SUBMIT` | 图片 | `埋牌` | `BTN_BURY` 帧1可提交/帧2灰 |
@@ -1701,21 +1715,21 @@ CARD_SIZE: {
### 6.8 阶段操作区配置
> **2026-08-26 修订**:选主面板花色图标、对数角标底已并入按钮帧(见 §1.5/§3.3),对应布局节点删除;花色张数改为十位/个位两个数字精灵,布局节点相应改写。
> **2026-08-26 修订(其一)**:选主面板花色图标、对数角标底已并入按钮帧(见 §1.5/§3.3),对应布局节点删除。
> **2026-08-26 修订(其二)**:数字改为图集叠绘(§3.6)后,一个多位数只占一个精灵——花色张数的「十位 / 个位」两个节点**合并为一个** `MAIN_SUIT_COUNT`;两个数字节点的 `w/h` 不再随位数变化,直接引用 `EQW_Layout.NUM_STYLE.*` 的 `width` / `charHeight`(SSOT:数值只在 `LayoutConstants.js` 定义一处,布局文件不再手抄)。
| 部件 | kind | 参数(实测估值) |
| --- | --- | --- |
| 叫分 · 面板底 | `point` | `x:355 y:140 w:575 h:170` |
| 叫分 · 提示条 | `point` | `x:470 y:100 w:345 h:33` |
| **叫分 · 14 档按钮** | `grid` | `anchorX:640 anchorY:160 cols:7 rows:2 itemWidth:74 itemHeight:56 spacingX:7 spacingY:24 anchor:center fillOrder:row` |
| 叫分 · 档位分数文字 | `attach` | `target:对应档位按钮, hAlign:center, vAlign:middle, offsetY:4` |
| 叫分 · 档位分数数字 | `attach` | `target:对应档位按钮, hAlign:center, vAlign:middle, offsetY:4, w:NUM_STYLE.CALL_SCORE.width(74), h:NUM_STYLE.CALL_SCORE.charHeight(40)` |
| 叫分 · 档位子数角标 | `attach` | `target:对应档位按钮, corner:topRight, offsetX:-30, offsetY:2` |
| 叫分 · `不叫` 按钮 | `point` | `x:553 y:345 w:184 h:57` |
| 叫分 · 自己叫分大字 | `point` | `x:570 y:320 w:140 h:70`,`textStyle{fontSize:52, align:center}` |
| 选主 · 标题斜角条 | `point` | `x:330 y:315 w:425 h:50` |
| **选主 · 4 花色 + 投降** | `line` | `direction:horizontal anchorX:330 anchorY:373 itemWidth:128 itemHeight:55 spacing:9 anchor:left`(共 5 项;投降不显示时只排 4 项,`anchor:left` 保证前 4 个位置不变) |
| 选主 · 张数(十位) | `attach` | `target:对应花色按钮, hAlign:center, vAlign:bottom, offsetX:-12, offsetY:-6, w:24, h:32`(十位为 0 时隐藏) |
| 选主 · 张数(个位) | `attach` | `target:对应花色按钮, hAlign:center, vAlign:bottom, offsetX:12, offsetY:-6, w:24, h:32`(始终显示) |
| 选主 · 张数 | `attach` | `target:对应花色按钮, hAlign:center, vAlign:bottom, offsetY:-6, w:NUM_STYLE.MAIN_SUIT_COUNT.width(48), h:NUM_STYLE.MAIN_SUIT_COUNT.charHeight(32)`(一个花色一个精灵,两位数由叠绘逐位画) |
| 选主 · 对数角标文字 | `attach` | `target:对应花色按钮, corner:topRight, offsetX:-36, offsetY:2`(按钮帧内已含角标底,本节点只定位叠加文字) |
| 埋牌 · 操作条 | `line` + `items` | `direction:horizontal anchorX:640 anchorY:165 itemHeight:63 spacing:45 anchor:center`,`items:[180,60,180]`(提示 / 倒计时 / 埋牌) |
| 出牌 · 操作条 | `line` + `items` | `direction:horizontal anchorX:640 anchorY:345 itemHeight:55 spacing:35 anchor:center`,`items:[75,157]`(倒计时 / 出牌) |
@@ -1723,6 +1737,18 @@ CARD_SIZE: {
| 状态提示条 · 等待类 | `point` | `x:500 y:373 w:320 h:47`(§2.8) |
| 状态提示条 · 即时反馈类 | `point` | `x:462 y:172 w:363 h:50`;另配 `toastDuration:1500` |
**数字样式配置 `EQW_Layout.NUM_STYLE`**(2026-08-26 新增,在 `LayoutConstants.js`,与 `CARD_SIZE`/`TEXT_STYLE` 并列)——图集叠绘数字的**唯一数值出处**,业务代码不许内联这些值:
| 键 | `res`(资源键名) | `charWidth × charHeight` | `spacing` | `align` | `width`(绘制区宽) | 说明 |
| --- | --- | --- | --- | --- | --- | --- |
| `COUNTDOWN` | `NUM_COUNTDOWN` | 44 × 60 | 0 | center | 88 | 秒数最多两位 |
| `RESULT_WIN` | `NUM_RESULT_WIN` | 46 × 62 | 0 | center | 230 | 符号 + 最多四位 |
| `RESULT_LOSE` | `NUM_RESULT_LOSE` | 46 × 62 | 0 | center | 230 | 同上,仅配色不同 |
| `CALL_SCORE` | `NUM_CALL_SCORE` | 30 × 40 | 0 | center | 74 | = 档位按钮宽,居中压在按钮上 |
| `MAIN_SUIT_COUNT` | `NUM_MAIN_SUIT_COUNT` | 24 × 32 | 0 | center | 48 | 单花色最多 26 张 |
> `res` 存的是**资源键名**而非 ID(与 `CARD_SIZE.res` 同一约定):布局文件是纯数据、不引用其他模块,调用方用 `EQW_Images[style.res]` 取真实 ID 再传给 `NumberRenderer.bind`。
### 6.9 结算区配置
| 部件 | kind | 参数(实测估值) |