From 9cfc11fd2691d7422ba19efe0bd94ebf0cef8736 Mon Sep 17 00:00:00 2001 From: guanchao <18612113337@163.com> Date: Mon, 19 Oct 2026 17:01:55 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=AF=8F=E6=97=A5=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=88=86=E6=94=AF=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BC=BA=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../action_bundle/prefab/dailyQuests.prefab | 105 ++++++++++++++++++ assets/action_bundle/script/DailyQuests.ts | 74 +++++++++++- tools/daily-count-test-utils.cjs | 16 ++- tools/test-daily-reward-animation.cjs | 16 ++- tools/test-weekly-quests.cjs | 8 +- 5 files changed, 201 insertions(+), 18 deletions(-) diff --git a/assets/action_bundle/prefab/dailyQuests.prefab b/assets/action_bundle/prefab/dailyQuests.prefab index 79023ae6..83409e3d 100644 --- a/assets/action_bundle/prefab/dailyQuests.prefab +++ b/assets/action_bundle/prefab/dailyQuests.prefab @@ -11998,6 +11998,111 @@ }, "rewardHours": { "__uuid__": "a30964ac-908f-46f8-ade5-f5b987643f4e" + }, + "countDigits": [ + { + "__uuid__": "e9fbe63d-d11b-48ab-b60f-6cddbbe26df0" + }, + { + "__uuid__": "468c687e-b161-4a91-b257-d66cb6d11eaa" + }, + { + "__uuid__": "c09a6ad9-172d-48f1-924e-21a8768f09c0" + }, + { + "__uuid__": "3d0db06a-8634-41b4-bef3-82a795065496" + }, + { + "__uuid__": "7052389a-a4a8-4947-b68c-7fbd4a030d40" + }, + { + "__uuid__": "b057bda0-9352-4c28-99a8-2eae260195f4" + }, + { + "__uuid__": "4146d0fe-f5d3-42ca-a7d3-1129026c4d88" + }, + { + "__uuid__": "6b254656-5b82-4cb3-8eb0-8ba25ad9cb23" + }, + { + "__uuid__": "87f0dfbe-399a-4f4c-be85-862d33c43678" + }, + { + "__uuid__": "3335805a-3829-4041-83e0-a55557d60f85" + } + ], + "weeklyDigits": [ + { + "__uuid__": "b9f95770-d594-41da-b106-f620b88c4eb3" + }, + { + "__uuid__": "25d61f1b-bb4e-4a13-ba88-2452a0418489" + }, + { + "__uuid__": "c8918990-0fd0-4ca3-8459-37236b9cf54d" + }, + { + "__uuid__": "ff941762-7142-46ac-80d2-fef5c0dc2623" + }, + { + "__uuid__": "02fdfef7-2e71-421e-b5da-1fda5e3c75cf" + }, + { + "__uuid__": "0783a277-bb41-4632-ae1d-638d91e479fa" + }, + { + "__uuid__": "d920d392-bed5-4812-b04c-54eb5caa89b8" + }, + { + "__uuid__": "14007ec1-6ce7-4de9-9776-b3fe516dadaf" + }, + { + "__uuid__": "71969ac6-4207-41c2-8cd4-1699391686a0" + }, + { + "__uuid__": "e1a14164-45a8-45b8-b295-4772a1629884" + } + ], + "taskDigits": [ + { + "__uuid__": "15971cf2-b96f-45c2-9c65-f1f4321eedc5" + }, + { + "__uuid__": "4d83c3d0-343e-4c5e-bf25-b8c7567ca664" + }, + { + "__uuid__": "a6ab3a21-a200-4356-830e-88db8281c607" + }, + { + "__uuid__": "232c7624-5016-4c8f-a221-9fe61544f63b" + }, + { + "__uuid__": "a5b67720-4006-4b08-b6bc-bb5ede3736f5" + }, + { + "__uuid__": "ce8dfee3-2f18-429c-b48a-bd0e697d2c27" + }, + { + "__uuid__": "dadc09ce-c919-4eed-b4b8-59568d8e935c" + }, + { + "__uuid__": "e3a812ba-0f71-448f-a2f7-891ef3d4657c" + }, + { + "__uuid__": "e6271735-4665-49c2-a545-58a4f5900770" + }, + { + "__uuid__": "37d118ec-14ad-4033-8832-94d7f94e730c" + } + ], + "taskSlash": { + "__uuid__": "cd19b7f8-cbf2-4087-864f-43436ab573f1" + }, + "countMinutes": { + "__uuid__": "7b7922e5-327e-44c0-b3cc-060808b27e8c" + }, + "countPlus": { + "__uuid__": "8595bc55-9288-4ca6-b1c7-5c6df0855e61" } }, { diff --git a/assets/action_bundle/script/DailyQuests.ts b/assets/action_bundle/script/DailyQuests.ts index 8e776443..1d7c965a 100644 --- a/assets/action_bundle/script/DailyQuests.ts +++ b/assets/action_bundle/script/DailyQuests.ts @@ -45,6 +45,26 @@ export default class DailyQuests extends cc.Component { @property(cc.SpriteFrame) rewardHours: cc.SpriteFrame = null; + @property([cc.SpriteFrame]) + countDigits: cc.SpriteFrame[] = []; + + @property([cc.SpriteFrame]) + weeklyDigits: cc.SpriteFrame[] = []; + + @property([cc.SpriteFrame]) + taskDigits: cc.SpriteFrame[] = []; + + @property(cc.SpriteFrame) + taskSlash: cc.SpriteFrame = null; + + @property(cc.SpriteFrame) + countMinutes: cc.SpriteFrame = null; + + @property(cc.SpriteFrame) + countPlus: cc.SpriteFrame = null; + + private countSignatures = new WeakMap(); + public juwai = false; btn_Touch: boolean = false; @@ -444,6 +464,52 @@ export default class DailyQuests extends cc.Component { if (richText) richText.string = text; } + private setCountText(node: cc.Node, text: string, digits: cc.SpriteFrame[] = this.countDigits, maxHeight: number = 40): void { + const signature = `${text}:${node.width}:${node.height}:${maxHeight}`; + if (this.countSignatures.get(node) === signature) return; + this.countSignatures.set(node, signature); + node.children.slice().forEach(child => { child.removeFromParent(); child.destroy(); }); + const height = Math.min(maxHeight, node.height); + const tokens = text.match(/分钟|./g) || []; + const frames = tokens.map(token => /^\d$/.test(token) ? digits[Number(token)] + : token === "分钟" ? this.countMinutes : token === "+" ? this.countPlus : token === "/" ? this.taskSlash : null); + const widths = tokens.map((token, index) => { + const rect = frames[index] && frames[index].getRect(); + return rect ? height * rect.width / rect.height : height * 0.5; + }); + const totalWidth = widths.reduce((sum, width) => sum + width, 0); + const scale = Math.min(1, node.width / Math.max(1, totalWidth)); + let left = -totalWidth / 2; + tokens.forEach((token, index) => { + const glyph = new cc.Node(`count_${token}`); + node.addChild(glyph); + if (frames[index]) { + const sprite = glyph.addComponent(cc.Sprite); + sprite.spriteFrame = frames[index]; + sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM; + } else if (token === "×" || token === ".") { + // num 目录只有数字;乘号和小数点保留文字,数字始终使用图片。 + const label = glyph.addComponent(cc.Label); + label.string = token; + label.fontSize = height; + label.lineHeight = height; + label.horizontalAlign = cc.Label.HorizontalAlign.CENTER; + label.verticalAlign = cc.Label.VerticalAlign.CENTER; + label.overflow = cc.Label.Overflow.SHRINK; + label.enableBold = true; + const outline = glyph.addComponent(cc.LabelOutline); + outline.color = cc.color(150, 83, 44); + outline.width = 3; + } else { + cc.error(`DailyQuests: 缺少数量图片 ${token}`); + } + glyph.setContentSize(widths[index], height); + glyph.scale = scale; + glyph.setPosition((left + widths[index] / 2) * scale, 0); + left += widths[index]; + }); + } + private setRewardCount(node: cc.Node, text: string): void { if (!node) return; const label = node.getComponent(cc.Label); @@ -458,8 +524,12 @@ export default class DailyQuests extends cc.Component { if (label) { label.string = text; label.enabled = !ready; } if (content) content.active = ready; if (!ready) return; - if (!content) { content = new cc.Node("RewardCountGlyphs"); node.addChild(content); } - const height = label ? label.fontSize : node.height; + if (!content) { + // The merged prefab includes an editor-preview digit; replace its visible output. + node.children.forEach(child => child.active = false); + content = new cc.Node("RewardCountGlyphs"); node.addChild(content); + } + const height = Math.min(label ? label.fontSize : 40, node.height); const scales = tokens.map(token => token === "×" || token === "x" ? 0.6 : 1); const widths = tokens.map((token, index) => { const frame = frames[index]; diff --git a/tools/daily-count-test-utils.cjs b/tools/daily-count-test-utils.cjs index 2e4373e2..d9fe5df5 100644 --- a/tools/daily-count-test-utils.cjs +++ b/tools/daily-count-test-utils.cjs @@ -4,7 +4,8 @@ const assert = require('node:assert/strict'); const directory = path.resolve(__dirname, '../assets/action_bundle/img/dailyQuest'); function frame(name, character) { const meta = Object.values(JSON.parse(fs.readFileSync(path.join(directory, name + '.png.meta'))).subMetas)[0]; - return { character, uuid: meta.uuid, getRect: () => ({ width: meta.width, height: meta.height }) }; + return { character, uuid: meta.uuid, getRect: () => ({ width: meta.width, height: meta.height }), + getOriginalSize: () => ({ width: meta.rawWidth, height: meta.rawHeight }) }; } function installCountFrames(daily) { daily.countDigits = Array.from({ length: 10 }, (_, index) => frame('num/' + index, String(index))); @@ -13,15 +14,22 @@ function installCountFrames(daily) { daily.taskSlash = frame('task_digits/xiegan', '/'); daily.countMinutes = frame('fenzhong', '分钟'); daily.countPlus = frame('jiahao', '+'); + daily.rewardDigits = daily.countDigits; + const font = JSON.parse(fs.readFileSync(path.resolve(directory, '../../../common/publicFont.plist.meta'))).subMetas; + for (const [field, name, character] of [['rewardMultiply', 'big_cheng.png', '×'], ['rewardMinutes', 'big_fenzhong.png', '分钟'], ['rewardHours', 'big_xiaoshi.png', '小时']]) { + const meta = font[name]; + daily[field] = { character, uuid: meta.uuid, getOriginalSize: () => ({ width: meta.rawWidth, height: meta.rawHeight }) }; + } } function countText(node) { if (!node) return undefined; assert.equal(node.getComponent('cc.Label'), null, 'count is a sprite container, not a Label'); - return node.children.map(child => { + const content = node.getChildByName('RewardCountGlyphs') || node; + return content.children.filter(child => child.active).map(child => { const sprite = child.getComponent('cc.Sprite'); - if (sprite) return sprite.spriteFrame.character; + if (sprite && sprite.enabled !== false) return sprite.spriteFrame.character; const text = child.getComponent('cc.Label').string; - assert.match(text, /^[×.]$/, 'digits must use supplied sprite frames'); + assert.match(text, /^[×.+]$/, 'digits must use supplied sprite frames'); return text; }).join(''); } diff --git a/tools/test-daily-reward-animation.cjs b/tools/test-daily-reward-animation.cjs index c987d94e..50d88fe7 100644 --- a/tools/test-daily-reward-animation.cjs +++ b/tools/test-daily-reward-animation.cjs @@ -30,14 +30,17 @@ test('reward number bindings use dailyQuest digits and publicFont big_cheng', () test('daily reward counts render the requested digit art, fit the count box and use the multiply sprite', () => { const h = setup([{ type: 'coin', count: 1023456789 }, { type: 'hammer', count: 2 }]); const slots = h.row.getChildByName('rewards').children; - for (const [index, text] of [[0, '6'], [1, '1023456789'], [2, '×2']]) { + for (const [index, text] of [[0, '1023456789'], [1, '×2'], [2, '6']]) { const count = slots[index].getChildByName('count'), glyphs = count.getChildByName('RewardCountGlyphs'); - assert.equal(count.getComponent('cc.Label').enabled, false); + assert.equal(count.getComponent('cc.Label'), null); + assert(count.children.filter(node => node !== glyphs).every(node => !node.active), 'editor-preview digits must not overlap live quantities'); const visible = glyphs.children.filter(node => node.active); assert.deepEqual(visible.map(node => node.getComponent('cc.Sprite').spriteFrame.uuid), [...text].map(char => char === '×' ? h.daily.rewardMultiply.uuid : h.daily.rewardDigits[Number(char)].uuid)); for (const node of visible) { assert(Number.isFinite(node.x)); + assert(node.height <= 40, 'label-free counts retain the original maximum digit height'); + assert.equal(node.scale, node.getComponent('cc.Sprite').spriteFrame === h.daily.rewardMultiply ? 0.6 : 1); assert((Math.abs(node.x) + node.width * node.scale / 2) * glyphs.scale <= count.width / 2 + 1e-8); } } @@ -45,7 +48,7 @@ test('daily reward counts render the requested digit art, fit the count box and test('count updates preserve fractional minutes and hide old glyphs when switching to shorter rewards', () => { const h = setup([{ type: 'infinite_health', count: 90 }]); - const count = h.row.getChildByName('rewards').children[1].getChildByName('count'); + const count = h.row.getChildByName('rewards').children[0].getChildByName('count'); const content = count.getChildByName('RewardCountGlyphs'); assert.deepEqual(content.children.filter(node => node.active).map(node => node.getComponent('cc.Sprite')?.enabled ? node.getComponent('cc.Sprite').spriteFrame.uuid : node.getComponent('cc.Label').string), @@ -58,6 +61,7 @@ test('count updates preserve fractional minutes and hide old glyphs when switchi assert.equal(content.children[0].getComponent('cc.Sprite').spriteFrame.uuid, h.daily.rewardMultiply.uuid); assert.equal(content.children[1].getComponent('cc.Label').enabled, false, 'old decimal label is disabled'); h.daily.setRewardCount(count, '+30'); + assert.equal(content.children[0].scale, 1, 'reusing the multiply slot resets its scale'); assert.equal(content.children[0].getComponent('cc.Sprite').enabled, false); assert.equal(content.children[0].getComponent('cc.Label').string, '+'); assert.equal(content.children[1].getComponent('cc.Sprite').spriteFrame.uuid, h.daily.rewardDigits[3].uuid); @@ -225,12 +229,6 @@ function setup(rewards = [{ type: 'coin', count: 50 }, { type: 'hammer', count: const daily = new context.DailyQuests(); installCountFrames(daily); daily.node = build(1); canvas.addChild(daily.node); daily.content = daily.node.getChildByName('bg').getChildByName('content'); daily.taskNormal = 'normal-frame'; daily.taskClaimable = 'claimable-frame'; - const numberFrame = meta => ({ uuid: meta.uuid, getOriginalSize: () => ({ width: meta.rawWidth, height: meta.rawHeight }) }); - daily.rewardDigits = Array.from({ length: 10 }, (_, digit) => numberFrame(JSON.parse(fs.readFileSync( - path.join(root, `assets/action_bundle/img/dailyQuest/num/${digit}.png.meta`), 'utf8')).subMetas[digit])); - daily.rewardMultiply = numberFrame(JSON.parse(fs.readFileSync(path.join(root, 'assets/common/publicFont.plist.meta'), 'utf8')).subMetas['big_cheng.png']); - daily.rewardMinutes = numberFrame(JSON.parse(fs.readFileSync(path.join(root, 'assets/common/publicFont.plist.meta'), 'utf8')).subMetas['big_fenzhong.png']); - daily.rewardHours = numberFrame(JSON.parse(fs.readFileSync(path.join(root, 'assets/common/publicFont.plist.meta'), 'utf8')).subMetas['big_xiaoshi.png']); daily.rewardIcons = ['xinCoin', 'ice', 'hammer', 'magic', 'skyLine'].map(name => { const meta = JSON.parse(fs.readFileSync(path.join(root, `assets/res/public/${name}.png.meta`), 'utf8')).subMetas[name]; return { getRect: () => ({ width: meta.width, height: meta.height }) }; diff --git a/tools/test-weekly-quests.cjs b/tools/test-weekly-quests.cjs index b3f663f8..88fc30b7 100644 --- a/tools/test-weekly-quests.cjs +++ b/tools/test-weekly-quests.cjs @@ -458,11 +458,11 @@ test('coin and infinite health icons use the configured 0.75 scale while other r assert.equal(icon.height,coinRect.height); }); -test('preview icons match the outer coin height and retain count labels without action buttons',()=>{ +test('preview icons match the outer coin height and retain image counts without action buttons',()=>{ const {daily,preview}=setup(0);daily.selectWeeklyReward(null,'weekly_35'); const rewards=preview.getChildByName('rewards');assert.equal(rewards.scaleX,1); assert.equal(preview.width,344);assert.equal(preview.height,180); - for(const slot of rewards.children){ + for(const slot of rewards.children.filter(slot=>slot.active)){ assert.equal(slot.scaleX,1); const icon=slot.getChildByName('icon'); assert.equal(icon.scaleX,icon.scaleY); @@ -471,7 +471,9 @@ test('preview icons match the outer coin height and retain count labels without assert.ok(Math.abs(icon.height*icon.scaleY-daily.rewardIcons[0].getRect().height*([daily.rewardIcons[0],daily.rewardIcons[4]].includes(icon.getComponent('cc.Sprite').spriteFrame)?0.75:1))<1e-8); assert.equal(icon.width/icon.height,frame.getRect().width/frame.getRect().height); } - assert.ok(slot.getChildByName('count').children.every(glyph=>glyph.height===40)); + const count=slot.getChildByName('count'),glyphs=count.getChildByName('RewardCountGlyphs'); + assert.ok(glyphs.children.filter(glyph=>glyph.active).every(glyph=>glyph.height===40)); + assert.ok(count.children.filter(child=>child!==glyphs).every(child=>!child.active)); assert.equal(slot.getChildByName('plus').active,false); } assert.equal(preview.getChildByName('btnClaim'),undefined);assert.equal(preview.getChildByName('close'),undefined);