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"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}