diff --git a/AGENTS.md b/AGENTS.md index e2c2cd7..f432cd6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,13 +19,13 @@ Git 历史采用 Conventional Commits,且摘要多为中文,例如 `feat: # Memory Context -# [mp-xieyingeng] recent context, 2026-04-24 8:08pm GMT+8 +# [mp-xieyingeng] recent context, 2026-04-24 9:39pm GMT+8 Legend: 🎯session 🔴bugfix 🟣feature 🔄refactor ✅change 🔵discovery ⚖️decision 🚨security_alert 🔐security_note Format: ID TIME TYPE TITLE Fetch details: get_observations([IDs]) | Search: mem-search skill -Stats: 19 obs (4,277t read) | 178,750t work | 98% savings +Stats: 45 obs (10,437t read) | 520,499t work | 98% savings ### Apr 24, 2026 101 8:46a 🟣 Live label display format updated to X/Y format @@ -47,6 +47,32 @@ Stats: 19 obs (4,277t read) | 178,750t work | 98% savings 138 " 🔄 PageLevel 输入方式从单框改为逐字格子 139 " 🔄 谐音梗展示从 Label 改为动态 Block 节点 140 " ✅ PageLevel.prefab 布局位置微调 +165 8:08p 🟣 PageLevel input layout simplified to single-character boxes with auto-distribution +167 8:09p 🔵 PageLevel.ts input block structure and callback stubs discovered +168 " 🟣 Auto-distribute characters across input boxes and auto-submit on completion implemented +169 " 🔴 PageLevel.ts node cleanup now calls removeFromParent before destroy +170 8:10p 🔄 PageLevel.ts full diff: single EditBox replaced with per-character block system +171 " 🔴 distributeInputText() wrapped in try/finally to guarantee flag reset +179 8:23p ✅ Game011_3.ttf font relocated from resources/ to dedicated fonts/ bundle directory +180 " 🟣 PageLoading.ts now loads fonts as a dynamic bundle after level data, before UI +181 " 🔄 AssetManager import switched to type-only import in PageLoading.ts +182 8:31p 🟣 PageLevel input UX improvement: full-string editing support +183 " 🟣 PageLevel input UX: full-string editing implemented +186 8:34p 🔴 PageLevel: clear input text on wrong answer +187 8:35p 🟣 PageLevel: delay pass modal to show punchline +189 8:36p 🔄 PageLevel: level completion reporting made fire-and-forget +191 8:45p 🔴 PageLevel: punchline block cleanup improved +192 8:46p 🟣 PageLevel.ts TitleLevel Label variable reserved +193 8:47p 🔵 PageLevel.ts level number tracking mechanism discovered +195 " 🟣 PageLevel.ts TitleLevel dynamic label update implemented +196 8:53p 🔵 PageLevel punchline not updated from enterLevel API +198 " 🔴 LevelDataManager updateLevelDetails now includes punchline +199 " 🔴 PageLevel punchline flow and empty state layout fixed +200 8:54p 🔴 LevelDataManager preserves existing punchline when enter returns null +203 9:06p 🟣 PageLevel.ts 新增图片描述标签绑定字段 +205 9:07p 🟣 PageLevel.ts 图片描述标签字段对接完成 +206 9:09p ✅ PageLevel.ts 回退图片描述标签字段命名 +208 9:11p 🟣 LevelDataManager 增加图片描述字段存储 -Access 179k tokens of past work via get_observations([IDs]) or mem-search skill. +Access 520k tokens of past work via get_observations([IDs]) or mem-search skill. \ No newline at end of file diff --git a/assets/PageLoading.ts b/assets/PageLoading.ts index 6f7034b..7e598dc 100644 --- a/assets/PageLoading.ts +++ b/assets/PageLoading.ts @@ -1,4 +1,5 @@ -import { _decorator, Component, ProgressBar, Label } from 'cc'; +import { _decorator, Component, ProgressBar, Label, assetManager } from 'cc'; +import type { AssetManager } from 'cc'; import { ViewManager } from './scripts/core/ViewManager'; import { LevelDataManager } from './scripts/utils/LevelDataManager'; import { AuthManager } from './scripts/utils/AuthManager'; @@ -14,6 +15,8 @@ const { ccclass, property } = _decorator; */ @ccclass('PageLoading') export class PageLoading extends Component { + private static readonly FONT_BUNDLE_NAME = 'fonts'; + @property(ProgressBar) progressBar: ProgressBar | null = null; @@ -52,6 +55,12 @@ export class PageLoading extends Component { return; } + const fontSuccess = await this._loadFontBundle(); + if (!fontSuccess) { + this._updateStatusLabel('字体资源加载失败,请重新打开游戏'); + return; + } + // 登录 + 关卡数据都就绪后,用服务端进度覆盖本地进度 if (loginSuccess) { this._syncProgressFromServer(); @@ -77,10 +86,10 @@ export class PageLoading extends Component { console.warn('[PageLoading] 加入分享失败,进入正常模式'); } - // 正常流程:预加载 PageHome (80-100%) + // 正常流程:预加载 PageHome (82-100%) ViewManager.instance.preload('PageHome', (progress) => { - this._updateProgress(0.8 + progress * 0.2); + this._updateProgress(0.82 + progress * 0.18); this._updateStatusLabel('正在加载界面资源...'); }, () => { @@ -112,6 +121,36 @@ export class PageLoading extends Component { }); } + /** + * 加载字体分包,避免字体资源进入小游戏主包 + */ + private _loadFontBundle(): Promise { + const bundleName = PageLoading.FONT_BUNDLE_NAME; + const cachedBundle = assetManager.getBundle(bundleName); + if (cachedBundle) { + console.log(`[PageLoading] 字体分包已加载: ${bundleName}`); + this._updateProgress(0.82); + return Promise.resolve(true); + } + + this._updateStatusLabel('正在加载字体资源...'); + this._updateProgress(0.8); + + return new Promise((resolve) => { + assetManager.loadBundle(bundleName, (err: Error | null, bundle: AssetManager.Bundle | null) => { + if (err || !bundle) { + console.error(`[PageLoading] 字体分包加载失败: ${bundleName}`, err); + resolve(false); + return; + } + + console.log(`[PageLoading] 字体分包加载完成: ${bundleName}`); + this._updateProgress(0.82); + resolve(true); + }); + }); + } + /** * 用服务端通关进度同步本地进度 * 1. 根据 completedLevelIds 标记已通关关卡 diff --git a/assets/resources/font.meta b/assets/fonts.meta similarity index 52% rename from assets/resources/font.meta rename to assets/fonts.meta index 8409868..c299924 100644 --- a/assets/resources/font.meta +++ b/assets/fonts.meta @@ -2,8 +2,10 @@ "ver": "1.2.0", "importer": "directory", "imported": true, - "uuid": "90eed50e-b353-46da-9510-b79d6628f187", + "uuid": "fc1f44af-699f-467c-ba2d-f54994551c4d", "files": [], "subMetas": {}, - "userData": {} + "userData": { + "isBundle": true + } } diff --git a/assets/resources/font/Game011_3.ttf b/assets/fonts/Game011_3.ttf similarity index 100% rename from assets/resources/font/Game011_3.ttf rename to assets/fonts/Game011_3.ttf diff --git a/assets/resources/font/Game011_3.ttf.meta b/assets/fonts/Game011_3.ttf.meta similarity index 100% rename from assets/resources/font/Game011_3.ttf.meta rename to assets/fonts/Game011_3.ttf.meta diff --git a/assets/prefabs/PageLevel.prefab b/assets/prefabs/PageLevel.prefab index 9a93ba9..29bff64 100644 --- a/assets/prefabs/PageLevel.prefab +++ b/assets/prefabs/PageLevel.prefab @@ -2578,8 +2578,8 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 42.255859375, - "height": 50.4 + "width": 100, + "height": 126 }, "_anchorPoint": { "__type__": "cc.Vec2", @@ -2609,22 +2609,25 @@ "_dstBlendFactor": 4, "_color": { "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, + "r": 75, + "g": 75, + "b": 75, "a": 255 }, - "_string": "label", + "_string": "叫", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 20, - "_fontSize": 20, + "_actualFontSize": 100, + "_fontSize": 100, "_fontFamily": "Arial", - "_lineHeight": 40, + "_lineHeight": 100, "_overflow": 0, "_enableWrapText": true, - "_font": null, - "_isSystemFontUsed": true, + "_font": { + "__uuid__": "fb4acba6-6bc7-4eb3-be34-8f2ac9823a80", + "__expectedType__": "cc.TTFFont" + }, + "_isSystemFontUsed": false, "_spacingX": 0, "_isItalic": false, "_isBold": false, @@ -4588,7 +4591,7 @@ }, "_lpos": { "__type__": "cc.Vec3", - "x": -50.312, + "x": -33.411, "y": -660.724, "z": 0 }, @@ -4692,7 +4695,7 @@ }, "_lpos": { "__type__": "cc.Vec3", - "x": -316.3829999999999, + "x": -340.505, "y": 0, "z": 0 }, @@ -4733,7 +4736,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 500, + "width": 464.5749969482422, "height": 500 }, "_anchorPoint": { @@ -4745,7 +4748,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "183qHdLEFC1ZokLeOAXS4v" + "fileId": "22LYNUfANKvbDg/dHkSUwV" }, { "__type__": "cc.Sprite", @@ -4775,7 +4778,7 @@ }, "_type": 0, "_fillType": 0, - "_sizeMode": 2, + "_sizeMode": 0, "_fillCenter": { "__type__": "cc.Vec2", "x": 0, @@ -4790,7 +4793,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "91zu1RBf9Bt4h8bGbBCEf+" + "fileId": "c7gLLXlKVCfYCUhQ+jYwy8" }, { "__type__": "cc.PrefabInfo", @@ -4800,7 +4803,7 @@ "asset": { "__id__": 0 }, - "fileId": "4659kCML1J8pPiZ6SwLi2f", + "fileId": "2b3ocDGTBC97mggTD6nDXO", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -4828,7 +4831,7 @@ }, "_lpos": { "__type__": "cc.Vec3", - "x": -156.798, + "x": 104.254, "y": 0, "z": 0 }, @@ -4869,7 +4872,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 183.05624389648438, + "width": 700, "height": 63 }, "_anchorPoint": { @@ -4881,7 +4884,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "5330d+6qxIN6lu5Q4FQBz1" + "fileId": "b0+JAH+KJFTaX6sbxsby5I" }, { "__type__": "cc.Label", @@ -4905,14 +4908,14 @@ "b": 65, "a": 255 }, - "_string": "提示 1:", + "_string": "提示 1:待解锁", "_horizontalAlign": 0, "_verticalAlign": 1, - "_actualFontSize": 50, + "_actualFontSize": 51, "_fontSize": 50, "_fontFamily": "Arial", "_lineHeight": 50, - "_overflow": 0, + "_overflow": 2, "_enableWrapText": true, "_font": { "__uuid__": "fb4acba6-6bc7-4eb3-be34-8f2ac9823a80", @@ -4952,7 +4955,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "78Pcn0jMBHN424ZHHdibDV" + "fileId": "c7NNDKisZMy5Fsvl8wdC2E" }, { "__type__": "cc.PrefabInfo", @@ -4962,7 +4965,7 @@ "asset": { "__id__": 0 }, - "fileId": "8czInr32FC34vOGyZgOJl/", + "fileId": "664aZaWkNEM5bDQ63gCwvW", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -4993,7 +4996,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "37D6b3G0lPlIkr1GzIzW7S" + "fileId": "b5JmT+IIBO2oALlaZ3XK0f" }, { "__type__": "cc.PrefabInfo", @@ -5003,7 +5006,7 @@ "asset": { "__id__": 0 }, - "fileId": "c16/HqrHNEsam62hN4pFaY", + "fileId": "e2UM2IG6dA9r68xaXEUXjU", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -5085,7 +5088,7 @@ }, "_lpos": { "__type__": "cc.Vec3", - "x": -318.81899999999996, + "x": -340.505, "y": 0, "z": 0 }, @@ -5098,8 +5101,8 @@ }, "_lscale": { "__type__": "cc.Vec3", - "x": 0.517, - "y": 0.517, + "x": 0.2, + "y": 0.2, "z": 0.517 }, "_mobility": 0, @@ -5126,8 +5129,8 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 153, - "height": 176 + "width": 464.5749969482422, + "height": 500 }, "_anchorPoint": { "__type__": "cc.Vec2", @@ -5138,7 +5141,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "24Muu32rZCxahfvEnv6Lh7" + "fileId": "a3hcMCJxVH7qdhcPKmLrrR" }, { "__type__": "cc.Sprite", @@ -5176,14 +5179,14 @@ }, "_fillStart": 0, "_fillRange": 0, - "_isTrimmedMode": true, + "_isTrimmedMode": false, "_useGrayscale": false, "_atlas": null, "_id": "" }, { "__type__": "cc.CompPrefabInfo", - "fileId": "cdz0mOL0lDGqaZ0Fj4ZhzU" + "fileId": "66jwZ+mAlCxrMD28cf4lu8" }, { "__type__": "cc.PrefabInfo", @@ -5193,7 +5196,7 @@ "asset": { "__id__": 0 }, - "fileId": "ddvQFKgzlIN6PN2OGZybmB", + "fileId": "a97AobrwtDRYZeDUxWwe9q", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -5221,7 +5224,7 @@ }, "_lpos": { "__type__": "cc.Vec3", - "x": -70.3203125, + "x": 104.254, "y": 0, "z": 0 }, @@ -5262,7 +5265,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 342.4312438964844, + "width": 700, "height": 63 }, "_anchorPoint": { @@ -5301,11 +5304,11 @@ "_string": "提示 2:待解锁", "_horizontalAlign": 0, "_verticalAlign": 1, - "_actualFontSize": 50, + "_actualFontSize": 51, "_fontSize": 50, "_fontFamily": "Arial", "_lineHeight": 50, - "_overflow": 0, + "_overflow": 2, "_enableWrapText": true, "_font": { "__uuid__": "fb4acba6-6bc7-4eb3-be34-8f2ac9823a80", @@ -5478,8 +5481,8 @@ }, "_lpos": { "__type__": "cc.Vec3", - "x": -314.4599999999999, - "y": 1.1368683772161603e-13, + "x": -340.505, + "y": 0, "z": 0 }, "_lrot": { @@ -5491,8 +5494,8 @@ }, "_lscale": { "__type__": "cc.Vec3", - "x": 0.517, - "y": 0.517, + "x": 0.2, + "y": 0.2, "z": 0.517 }, "_mobility": 0, @@ -5519,8 +5522,8 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 153, - "height": 176 + "width": 464.5749969482422, + "height": 500 }, "_anchorPoint": { "__type__": "cc.Vec2", @@ -5531,7 +5534,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "b1wA0cLKxDJ4h7bFZLjZL4" + "fileId": "50azgTm7pCALtYSMaQYyyF" }, { "__type__": "cc.Sprite", @@ -5569,14 +5572,14 @@ }, "_fillStart": 0, "_fillRange": 0, - "_isTrimmedMode": true, + "_isTrimmedMode": false, "_useGrayscale": false, "_atlas": null, "_id": "" }, { "__type__": "cc.CompPrefabInfo", - "fileId": "26fAcMh7lAaZJs8caeOT/E" + "fileId": "006v9WAKhG/JqM9MRgJiRS" }, { "__type__": "cc.PrefabInfo", @@ -5586,7 +5589,7 @@ "asset": { "__id__": 0 }, - "fileId": "2bZ2Ap1yhIhbzJDDtNSerr", + "fileId": "ad9JJ1JLNHtpnjMaprKGN7", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -5614,8 +5617,8 @@ }, "_lpos": { "__type__": "cc.Vec3", - "x": -70.3203125, - "y": 1.1368683772161603e-13, + "x": 104.254, + "y": 0, "z": 0 }, "_lrot": { @@ -5655,7 +5658,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 343.4078063964844, + "width": 700, "height": 63 }, "_anchorPoint": { @@ -5694,11 +5697,11 @@ "_string": "提示 3:待解锁", "_horizontalAlign": 0, "_verticalAlign": 1, - "_actualFontSize": 50, + "_actualFontSize": 51, "_fontSize": 50, "_fontFamily": "Arial", "_lineHeight": 50, - "_overflow": 0, + "_overflow": 2, "_enableWrapText": true, "_font": { "__uuid__": "fb4acba6-6bc7-4eb3-be34-8f2ac9823a80", @@ -5872,10 +5875,10 @@ "__prefab": { "__id__": 244 }, - "_alignFlags": 4, + "_alignFlags": 44, "_target": null, - "_left": 0, - "_right": 0, + "_left": 106.589, + "_right": 173.411, "_top": 0, "_bottom": 249.27599999999995, "_horizontalCenter": 0, @@ -5886,7 +5889,7 @@ "_isAbsBottom": true, "_isAbsHorizontalCenter": true, "_isAbsVerticalCenter": true, - "_originalWidth": 0, + "_originalWidth": 800, "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, @@ -7933,6 +7936,9 @@ "liveLabel": { "__id__": 158 }, + "titleLevelLabel": { + "__id__": 14 + }, "currentLevelIndex": 0, "clickAudio": { "__uuid__": "a68a6314-fb7c-48a9-bd6c-0a65ef665d50", diff --git a/assets/prefabs/PageLevel.ts b/assets/prefabs/PageLevel.ts index 38a4b0e..9547cb0 100644 --- a/assets/prefabs/PageLevel.ts +++ b/assets/prefabs/PageLevel.ts @@ -24,6 +24,12 @@ export class PageLevel extends BaseView { /** 默认体力上限,服务端未返回 max 时使用 */ private static readonly DEFAULT_STAMINA_MAX = 50; + /** 答案正确后展示包袱答案的停留时间 */ + private static readonly PASS_MODAL_DELAY_MS = 2000; + + /** 答案错误后清空输入的延迟,给失败音效和错误答案留出反馈时间 */ + private static readonly CLEAR_INPUT_DELAY_MS = 500; + // ========== 节点引用 ========== @property(Node) inputLayout: Node | null = null; @@ -80,6 +86,10 @@ export class PageLevel extends BaseView { @property(Label) liveLabel: Label | null = null; + /** 关卡标题标签,显示为“第 N 关” */ + @property(Label) + titleLevelLabel: Label | null = null; + // ========== 配置属性 ========== @property({ min: 0, @@ -263,6 +273,9 @@ export class PageLevel extends BaseView { this.currentLevelIndex, { answer: enterData.answer, + image1Description: enterData.image1Description, + image2Description: enterData.image2Description, + punchline: enterData.punchline, hint1: enterData.hint1, hint2: enterData.hint2, hint3: enterData.hint3, @@ -308,8 +321,11 @@ export class PageLevel extends BaseView { // 设置图片描述 this.setImageDescriptions(config.image1Description, config.image2Description); - // 隐藏谐音梗说明(通关后才显示) - this.setPunchline(null); + // 设置关卡标题 + this.updateTitleLevelLabel(); + + // 隐藏包袱答案,通关后再按 punchline 展示 + this.hidePunchline(); // 设置线索1(默认解锁,如果有的话) if (config.clue1) { @@ -381,6 +397,7 @@ export class PageLevel extends BaseView { editBox.placeholder = ''; editBox.maxLength = chars.length; editBox.string = ''; + editBox.node.on(EditBox.EventType.EDITING_DID_BEGAN, this.onInputEditingBegan, this); editBox.node.on(EditBox.EventType.TEXT_CHANGED, this.onInputTextChanged, this); editBox.node.on(EditBox.EventType.EDITING_DID_ENDED, this.onInputEditingEnded, this); } @@ -404,6 +421,7 @@ export class PageLevel extends BaseView { if (node.isValid) { const editBox = node.getComponent(EditBox); if (editBox) { + editBox.node.off(EditBox.EventType.EDITING_DID_BEGAN, this.onInputEditingBegan, this); editBox.node.off(EditBox.EventType.TEXT_CHANGED, this.onInputTextChanged, this); editBox.node.off(EditBox.EventType.EDITING_DID_ENDED, this.onInputEditingEnded, this); editBox.string = ''; @@ -457,42 +475,58 @@ export class PageLevel extends BaseView { // ========== EditBox 事件回调 ========== /** - * 输入框文本变化回调 + * 输入框开始编辑时,把当前所有格子的内容合并到当前输入框里 */ - private onInputTextChanged(editBox: EditBox): void { + private onInputEditingBegan(editBox: EditBox): void { if (this._isSyncingInputText) return; const inputIndex = this._inputNodes.findIndex(node => node === editBox.node); if (inputIndex < 0) return; - this.distributeInputText(inputIndex, editBox.string); - this.tryAutoSubmitAnswer(); + const answer = this.getAnswer(); + this._isSyncingInputText = true; + + try { + for (let i = 0; i < this._inputNodes.length; i++) { + const itemEditBox = this._inputNodes[i].getComponent(EditBox); + if (itemEditBox) { + itemEditBox.string = i === inputIndex ? answer : ''; + } + } + } finally { + this._isSyncingInputText = false; + } + } + + /** + * 输入框文本变化回调 + */ + private onInputTextChanged(_editBox: EditBox): void { + this._lastAutoSubmittedAnswer = ''; } /** * 输入框编辑结束回调 */ - private onInputEditingEnded(_editBox: EditBox): void { - console.log('[PageLevel] 输入编辑结束'); + private onInputEditingEnded(editBox: EditBox): void { + if (this._isSyncingInputText) return; + + const inputIndex = this._inputNodes.findIndex(node => node === editBox.node); + if (inputIndex < 0) return; + + this.distributeInputText(editBox.string); + this.tryAutoSubmitAnswer(); } - private distributeInputText(startIndex: number, text: string): void { + private distributeInputText(text: string): void { const chars = Array.from(text); this._isSyncingInputText = true; try { - if (chars.length <= 1) { - const editBox = this._inputNodes[startIndex]?.getComponent(EditBox); - if (editBox) { - editBox.string = chars[0] ?? ''; - } - return; - } - - for (let i = startIndex; i < this._inputNodes.length; i++) { + for (let i = 0; i < this._inputNodes.length; i++) { const editBox = this._inputNodes[i].getComponent(EditBox); if (editBox) { - editBox.string = chars[i - startIndex] ?? ''; + editBox.string = chars[i] ?? ''; } } } finally { @@ -500,6 +534,22 @@ export class PageLevel extends BaseView { } } + private clearInputText(): void { + this._isSyncingInputText = true; + + try { + for (const node of this._inputNodes) { + const editBox = node.getComponent(EditBox); + if (editBox) { + editBox.string = ''; + } + } + this._lastAutoSubmittedAnswer = ''; + } finally { + this._isSyncingInputText = false; + } + } + private tryAutoSubmitAnswer(): void { if (!this._currentConfig || this._isTransitioning) return; @@ -575,20 +625,20 @@ export class PageLevel extends BaseView { const tipsItem = this.getTipsItem(index); if (!tipsItem) return; - // 查找 TipsLabel 节点:Content -> TipsLabel - const contentNode = tipsItem.getChildByName('Content'); - if (!contentNode) return; - - const tipsLabelNode = contentNode.getChildByName('TipsLabel'); - if (!tipsLabelNode) return; - - const label = tipsLabelNode.getComponent(Label); + const label = this.getTipsLabel(tipsItem); if (label) { - label.string = `提示 ${index}: ${content}`; + label.string = `提示${index}:${content}`; console.log(`[PageLevel] 设置线索${index}: ${content}`); } } + private getTipsLabel(tipsItem: Node): Label | null { + const directLabel = tipsItem.getChildByName('TipsLabel')?.getComponent(Label); + if (directLabel) return directLabel; + + return tipsItem.getChildByName('Content')?.getChildByName('TipsLabel')?.getComponent(Label) ?? null; + } + /** * 显示线索 */ @@ -748,6 +798,12 @@ export class PageLevel extends BaseView { } } + private updateTitleLevelLabel(): void { + if (!this.titleLevelLabel) return; + + this.titleLevelLabel.string = `第 ${this.currentLevelIndex + 1} 关`; + } + /** * 设置谐音梗说明(通关后逐字展示,未通关时传 null 隐藏) */ @@ -756,8 +812,7 @@ export class PageLevel extends BaseView { const chars = Array.from(punchline ?? ''); if (chars.length === 0) { - this.punchLayout.active = false; - this.clearPunchBlocks(); + this.hidePunchline(); return; } @@ -768,6 +823,7 @@ export class PageLevel extends BaseView { } this.clearPunchBlocks(); + this.removeUnexpectedPunchLayoutChildren(template); this.punchLayout.active = true; for (let i = 0; i < chars.length; i++) { @@ -778,7 +834,12 @@ export class PageLevel extends BaseView { const label = this.getPunchBlockLabel(blockNode); if (label) { + label.node.active = true; + label.enabled = true; label.string = chars[i]; + console.log(`[PageLevel] 设置包袱块${i + 1}: ${chars[i]}`); + } else { + console.warn(`[PageLevel] 包袱块${i + 1} 未找到 Label 组件`); } if (blockNode.parent !== this.punchLayout) { @@ -801,6 +862,7 @@ export class PageLevel extends BaseView { if (node === template) { node.active = false; } else { + node.removeFromParent(); node.destroy(); } } @@ -808,6 +870,42 @@ export class PageLevel extends BaseView { this._punchBlockNodes = []; } + private hidePunchline(): void { + if (!this.punchLayout) return; + + const template = this.getPunchBlockTemplateNode(); + if (!template) { + this.punchLayout.active = false; + return; + } + + this.clearPunchBlocks(); + this.removeUnexpectedPunchLayoutChildren(template); + this.punchLayout.active = false; + template.active = false; + template.name = 'block'; + + const label = this.getPunchBlockLabel(template); + if (label) { + label.node.active = true; + label.enabled = true; + label.string = ''; + } + + this._punchBlockNodes = []; + } + + private removeUnexpectedPunchLayoutChildren(template: Node): void { + if (!this.punchLayout) return; + + for (const child of [...this.punchLayout.children]) { + if (child !== template) { + child.removeFromParent(); + child.destroy(); + } + } + } + private getPunchBlockTemplateNode(): Node | null { if (this._punchBlockTemplateNode?.isValid) return this._punchBlockTemplateNode; @@ -816,7 +914,19 @@ export class PageLevel extends BaseView { } private getPunchBlockLabel(blockNode: Node): Label | null { - return blockNode.getChildByName('Label')?.getComponent(Label) ?? blockNode.getComponent(Label); + return this.findLabelInNode(blockNode); + } + + private findLabelInNode(node: Node): Label | null { + const label = node.getComponent(Label); + if (label) return label; + + for (const child of node.children) { + const childLabel = this.findLabelInNode(child); + if (childLabel) return childLabel; + } + + return null; } // ========== 音效相关方法 ========== @@ -1023,31 +1133,39 @@ export class PageLevel extends BaseView { // 播放成功音效 this.playSuccessSound(); - // 通关后展示谐音梗说明 - if (this._currentConfig?.punchline) { - this.setPunchline(this._currentConfig.punchline); - } + // 通关后根据 punchline 字数重建包袱答案块 + this.setPunchline(this._currentConfig?.punchline ?? null); const levelId = this._currentConfig?.id ?? ''; const timeSpent = Math.max(0, Math.round((Date.now() - this._levelStartTime) / 1000)); - if (!this._isShareMode) { - // 上报通关耗时 - const result = await StaminaManager.instance.completeLevel(levelId, timeSpent); - if (result) { - console.log(`[PageLevel] 通关上报成功,首次通关: ${result.firstClear}`); - } - // 标记关卡为已通关(本地缓存) - LevelDataManager.instance.markLevelCompleted(this.currentLevelIndex); - } else { - // fire-and-forget: errors are logged inside reportLevelProgress - void ShareManager.instance.reportLevelProgress(levelId, true, timeSpent); - } + this.reportLevelCompleted(levelId, timeSpent); + await this.delay(PageLevel.PASS_MODAL_DELAY_MS); // 显示通关弹窗 this._showPassModal(); } + private reportLevelCompleted(levelId: string, timeSpent: number): void { + if (!this._isShareMode) { + // 标记关卡为已通关(本地缓存),通关上报并行执行,不阻塞包袱展示节奏 + LevelDataManager.instance.markLevelCompleted(this.currentLevelIndex); + void StaminaManager.instance.completeLevel(levelId, timeSpent).then((result) => { + if (result) { + console.log(`[PageLevel] 通关上报成功,首次通关: ${result.firstClear}`); + } + }); + return; + } + + // fire-and-forget: errors are logged inside reportLevelProgress + void ShareManager.instance.reportLevelProgress(levelId, true, timeSpent); + } + + private delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } + /** * 显示通关弹窗 * 将弹窗添加到 Canvas 根节点下(而非 PageLevel 子节点) @@ -1125,6 +1243,13 @@ export class PageLevel extends BaseView { // 显示 Toast 提示 ToastManager.show('答案错误,再试试吧!'); + + // 输入识别失败或答案错误后延迟清空,避免错误内容瞬间消失 + void this.delay(PageLevel.CLEAR_INPUT_DELAY_MS).then(() => { + if (!this._isTransitioning) { + this.clearInputText(); + } + }); } /** diff --git a/assets/scripts/utils/LevelDataManager.ts b/assets/scripts/utils/LevelDataManager.ts index 0a70f0a..8a98eec 100644 --- a/assets/scripts/utils/LevelDataManager.ts +++ b/assets/scripts/utils/LevelDataManager.ts @@ -267,7 +267,7 @@ export class LevelDataManager { /** * 用 enter 接口返回的数据更新运行时关卡配置(填充答案和线索) */ - updateLevelDetails(index: number, details: { answer: string; hint1: string | null; hint2: string | null; hint3: string | null }): void { + updateLevelDetails(index: number, details: { answer: string; image1Description: string | null; image2Description: string | null; punchline: string | null; hint1: string | null; hint2: string | null; hint3: string | null }): void { const config = this._levelConfigs.get(index); if (!config) { console.warn(`[LevelDataManager] 关卡 ${index} 配置不存在,无法更新详情`); @@ -277,6 +277,9 @@ export class LevelDataManager { this._levelConfigs.set(index, { ...config, answer: details.answer, + image1Description: details.image1Description ?? config.image1Description, + image2Description: details.image2Description ?? config.image2Description, + punchline: details.punchline ?? config.punchline, clue1: details.hint1 ?? null, clue2: details.hint2 ?? null, clue3: details.hint3 ?? null, diff --git a/settings/v2/packages/builder.json b/settings/v2/packages/builder.json index 7526e40..9c1ae80 100644 --- a/settings/v2/packages/builder.json +++ b/settings/v2/packages/builder.json @@ -1,3 +1,39 @@ { - "__version__": "1.3.9" + "__version__": "1.3.9", + "bundleConfig": { + "custom": { + "default": { + "displayName": "i18n:builder.asset_bundle.defaultConfig", + "configs": { + "native": { + "preferredOptions": { + "isRemote": false, + "compressionType": "merge_dep" + } + }, + "web": { + "preferredOptions": { + "isRemote": false, + "compressionType": "merge_dep" + }, + "fallbackOptions": { + "compressionType": "merge_dep" + } + }, + "miniGame": { + "fallbackOptions": { + "isRemote": false, + "compressionType": "merge_dep" + }, + "configMode": "overwrite", + "overwriteSettings": { + "wechatgame": { + "compressionType": "subpackage" + } + } + } + } + } + } + } }