二七王:新增图集叠绘数字能力(SpriteManager 源矩形 API + NumberRenderer)

多帧图片精灵一次只显示一帧=只能显示一位数,两位数就得两个精灵,
14 个叫分档位(13 档两位数)每档只配一个精灵时根本渲染不出来。

- SpriteManager.drawImageRegion:把 GameABCUtils.Draw.drawImage 的源矩形
  能力暴露到业务层(drawImage 只透传了整图版),校验/日志/返回值风格照现有
  drawImage 一致;注明必须在精灵绘制回调中调用。
- gameabc-framework/ui/NumberRenderer.js:游戏中立,bind/setValue/clear/unbind,
  绘制回调里按字宽逐位裁源矩形画在同一个精灵上;layout 抽为纯函数可脱离引擎单测;
  字符映射不到显式抛错,不静默跳过。
- EQW_Layout.NUM_STYLE:5 套数字的字宽/字高/字间距/对齐/绘制区宽集中配置(零裸值),
  资源只存键名(与 CARD_SIZE.res 同一约定)。
- client/tests/test_numberrenderer.js:43 checks(多位/单位/正负号/三种对齐/负字间距/
  空值/自定义 charMap/非法字符与非法样式抛错/5 套真实样式画得下)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-26 23:52:56 +08:00
co-authored by Claude Opus 5
parent b81e800f22
commit bc75e40acc
5 changed files with 602 additions and 1 deletions
@@ -717,7 +717,83 @@ var SpriteManager = (function() {
return false;
}
},
/**
* 在精灵上绘制图片的【指定源矩形】(图集裁切绘制)
*
* 与 drawImage 的区别:
* - drawImage = 整图绘制,源矩形固定为图片资源的完整尺寸,一次只能画“一张完整的图”
* - drawImageRegion = 可指定源矩形 (srcX, srcY, srcW, srcH),从一张图里裁出任意一格再画
*
* 典型用途:
* - 从等宽排列的图集里裁出第 N 格画上去(如把多位数字逐位画在【同一个】精灵上,
* 见 gameabc-framework/ui/NumberRenderer.js)
* - 九宫格拉伸、进度条按比例裁切、图集小图标叠绘
*
* ⚠️ 前提:必须在【精灵的绘制回调中】调用(SpriteEventController.registerDraw 注册的
* handler 里,或 registerGlobalDraw 的全局钩子里)。引擎的叠绘只在该精灵绘制的那一
* 帧生效,在回调之外调用不会留下任何画面。
*
* @param {Number} spriteId - 目标精灵ID(在哪个精灵上绘制)
* @param {Number} imageId - 要绘制的图片资源ID
* @param {Number} destX - 目标X坐标(在精灵内的相对坐标)
* @param {Number} destY - 目标Y坐标(在精灵内的相对坐标)
* @param {Number} destWidth - 目标绘制宽度
* @param {Number} destHeight - 目标绘制高度
* @param {Number} srcX - 源图片截取区域的X坐标
* @param {Number} srcY - 源图片截取区域的Y坐标
* @param {Number} srcWidth - 源图片截取区域的宽度
* @param {Number} srcHeight - 源图片截取区域的高度
* @returns {Boolean} 是否成功
*
* @example
* // 数字图集单字宽 30、高 40,'7' 是第 8 格(索引 7):
* // 把它画到精灵 1617 内的 (0, 0) 处,原样大小
* SpriteManager.drawImageRegion(1617, 635, 0, 0, 30, 40, 7 * 30, 0, 30, 40);
*/
drawImageRegion: function(spriteId, imageId, destX, destY, destWidth, destHeight,
srcX, srcY, srcWidth, srcHeight) {
if (!_validateSpriteId(spriteId)) return false;
// 参数验证
if (typeof imageId !== 'number' || imageId <= 0) {
console.error('❌ SpriteManager.drawImageRegion: 图片资源ID必须是正数:', imageId);
return false;
}
if (typeof destX !== 'number' || typeof destY !== 'number') {
console.error('❌ SpriteManager.drawImageRegion: 目标坐标必须是数字');
return false;
}
if (typeof destWidth !== 'number' || destWidth <= 0 ||
typeof destHeight !== 'number' || destHeight <= 0) {
console.error('❌ SpriteManager.drawImageRegion: 目标宽度和高度必须是正数');
return false;
}
if (typeof srcX !== 'number' || typeof srcY !== 'number') {
console.error('❌ SpriteManager.drawImageRegion: 源矩形坐标必须是数字');
return false;
}
if (typeof srcWidth !== 'number' || srcWidth <= 0 ||
typeof srcHeight !== 'number' || srcHeight <= 0) {
console.error('❌ SpriteManager.drawImageRegion: 源矩形宽度和高度必须是正数');
return false;
}
try {
// 调用 GameABCUtils.Draw.drawImage(带源矩形的完整版绘图接口)
GameABCUtils.Draw.drawImage(spriteId, imageId, destX, destY, destWidth, destHeight,
srcX, srcY, srcWidth, srcHeight);
return true;
} catch (error) {
console.error('❌ SpriteManager.drawImageRegion 失败:', error);
return false;
}
},
// ==================== 交互控制 ====================
/**