feat: 支持新版的关卡页面

This commit is contained in:
richarjiang
2026-04-19 14:19:13 +08:00
parent 34e06480ce
commit 5eef9d8528
9 changed files with 1165 additions and 410 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -43,6 +43,18 @@ export class PageLevel extends BaseView {
@property(Node) @property(Node)
mainImage: Node | null = null; mainImage: Node | null = null;
@property(Node)
mainImage2: Node | null = null;
@property(Label)
image1DescLabel: Label | null = null;
@property(Label)
image2DescLabel: Label | null = null;
@property(Label)
punchlineLabel: Label | null = null;
@property(Node) @property(Node)
tipsItem1: Node | null = null; tipsItem1: Node | null = null;
@@ -268,8 +280,17 @@ export class PageLevel extends BaseView {
this._isTimeUp = false; this._isTimeUp = false;
this._countdown = 60; this._countdown = 60;
// 设置主图 // 设置主图图片1
this.setMainImage(config.spriteFrame); this.setMainImage(config.spriteFrame1);
// 设置图片2
this.setMainImage2(config.spriteFrame2);
// 设置图片描述
this.setImageDescriptions(config.image1Description, config.image2Description);
// 隐藏谐音梗说明(通关后才显示)
this.setPunchline(null);
// 设置线索1默认解锁如果有的话 // 设置线索1默认解锁如果有的话
if (config.clue1) { if (config.clue1) {
@@ -618,7 +639,7 @@ export class PageLevel extends BaseView {
// ========== 主图相关方法 ========== // ========== 主图相关方法 ==========
/** /**
* 设置主图 * 设置主图图片1
*/ */
private setMainImage(spriteFrame: SpriteFrame | null): void { private setMainImage(spriteFrame: SpriteFrame | null): void {
if (!this.mainImage) return; if (!this.mainImage) return;
@@ -626,7 +647,47 @@ export class PageLevel extends BaseView {
const sprite = this.mainImage.getComponent(Sprite); const sprite = this.mainImage.getComponent(Sprite);
if (sprite && spriteFrame) { if (sprite && spriteFrame) {
sprite.spriteFrame = spriteFrame; sprite.spriteFrame = spriteFrame;
console.log('[PageLevel] 设置主图'); console.log('[PageLevel] 设置主图1');
}
}
/**
* 设置图片2
*/
private setMainImage2(spriteFrame: SpriteFrame | null): void {
if (!this.mainImage2) return;
const sprite = this.mainImage2.getComponent(Sprite);
if (sprite && spriteFrame) {
sprite.spriteFrame = spriteFrame;
console.log('[PageLevel] 设置主图2');
}
}
/**
* 设置图片描述文本
*/
private setImageDescriptions(desc1: string | null, desc2: string | null): void {
if (this.image1DescLabel) {
this.image1DescLabel.string = desc1 ?? '';
}
if (this.image2DescLabel) {
this.image2DescLabel.string = desc2 ?? '';
}
}
/**
* 设置谐音梗说明(通关后展示,未通关时传 null 隐藏)
*/
private setPunchline(punchline: string | null): void {
if (!this.punchlineLabel) return;
if (punchline) {
this.punchlineLabel.node.active = true;
this.punchlineLabel.string = punchline;
} else {
this.punchlineLabel.node.active = false;
this.punchlineLabel.string = '';
} }
} }
@@ -819,6 +880,11 @@ export class PageLevel extends BaseView {
// 播放成功音效 // 播放成功音效
this.playSuccessSound(); this.playSuccessSound();
// 通关后展示谐音梗说明
if (this._currentConfig?.punchline) {
this.setPunchline(this._currentConfig.punchline);
}
const levelId = this._currentConfig?.id ?? ''; const levelId = this._currentConfig?.id ?? '';
const timeSpent = Math.max(0, Math.round((Date.now() - this._levelStartTime) / 1000)); const timeSpent = Math.max(0, Math.round((Date.now() - this._levelStartTime) / 1000));

View File

@@ -204,8 +204,8 @@ export class PagePreviewLevels extends BaseView {
const levelCover = item.getChildByName('LevelCover'); const levelCover = item.getChildByName('LevelCover');
if (levelCover) { if (levelCover) {
const sprite = levelCover.getComponent(Sprite); const sprite = levelCover.getComponent(Sprite);
if (sprite && config.spriteFrame) { if (sprite && config.spriteFrame1) {
sprite.spriteFrame = config.spriteFrame; sprite.spriteFrame = config.spriteFrame1;
} }
} }

View File

@@ -326,8 +326,8 @@ export class PageWriteLevels extends BaseView {
const levelCover = item.getChildByName('LevelCover'); const levelCover = item.getChildByName('LevelCover');
if (levelCover) { if (levelCover) {
const sprite = levelCover.getComponent(Sprite); const sprite = levelCover.getComponent(Sprite);
if (sprite && config.spriteFrame) { if (sprite && config.spriteFrame1) {
sprite.spriteFrame = config.spriteFrame; sprite.spriteFrame = config.spriteFrame1;
} }
} }

View File

@@ -14,7 +14,7 @@ export interface ApiEnvelope<T> {
export interface StaminaInfo { export interface StaminaInfo {
/** 当前体力值(已计算恢复) */ /** 当前体力值(已计算恢复) */
current: number; current: number;
/** 体力上限,固定为 5 */ /** 体力上限,固定为 50 */
max: number; max: number;
/** 下一点体力恢复的时间ISO 8601满体力时为 null */ /** 下一点体力恢复的时间ISO 8601满体力时为 null */
nextRecoverAt: string | null; nextRecoverAt: string | null;
@@ -50,8 +50,12 @@ export interface GameData {
export interface LevelListItem { export interface LevelListItem {
id: string; id: string;
level: number; level: number;
imageUrl: string; image1Url: string;
image1Description: string | null;
image2Url: string;
image2Description: string | null;
answer: string | null; answer: string | null;
punchline: string | null;
hint1: string | null; hint1: string | null;
hint2: string | null; hint2: string | null;
hint3: string | null; hint3: string | null;
@@ -69,8 +73,12 @@ export interface LevelListData {
export interface EnterLevelData { export interface EnterLevelData {
id: string; id: string;
level: number; level: number;
imageUrl: string; image1Url: string;
image1Description: string | null;
image2Url: string;
image2Description: string | null;
answer: string; answer: string;
punchline: string | null;
hint1: string | null; hint1: string | null;
hint2: string | null; hint2: string | null;
hint3: string | null; hint3: string | null;
@@ -95,8 +103,12 @@ export interface CreateShareData {
export interface ShareLevelData { export interface ShareLevelData {
id: string; id: string;
level: number; level: number;
imageUrl: string; image1Url: string;
image1Description: string | null;
image2Url: string;
image2Description: string | null;
answer: string; answer: string;
punchline: string | null;
hint1: string | null; hint1: string | null;
hint2: string | null; hint2: string | null;
hint3: string | null; hint3: string | null;

View File

@@ -8,8 +8,16 @@ export interface ApiLevelData {
id: string; id: string;
/** 关卡序号 */ /** 关卡序号 */
level: number; level: number;
/** 图 URL */ /** 图片1 URL */
imageUrl: string; image1Url: string;
/** 图片1 文本说明 */
image1Description: string | null;
/** 图片2 URL */
image2Url: string;
/** 图片2 文本说明 */
image2Description: string | null;
/** 谐音梗说明(仅通关后返回,未通关为 null */
punchline: string | null;
/** 线索1未通关时为 null */ /** 线索1未通关时为 null */
hint1: string | null; hint1: string | null;
/** 线索2未通关时为 null */ /** 线索2未通关时为 null */
@@ -47,8 +55,16 @@ export interface RuntimeLevelConfig {
id: string; id: string;
/** 关卡名称 */ /** 关卡名称 */
name: string; name: string;
/** 图 SpriteFrame可能为 null 如果加载失败) */ /** 图片1 SpriteFrame可能为 null 如果加载失败) */
spriteFrame: SpriteFrame | null; spriteFrame1: SpriteFrame | null;
/** 图片2 SpriteFrame可能为 null 如果加载失败) */
spriteFrame2: SpriteFrame | null;
/** 图片1 文本说明 */
image1Description: string | null;
/** 图片2 文本说明 */
image2Description: string | null;
/** 谐音梗说明 */
punchline: string | null;
/** 线索1未通关时为 null进入关卡后由 enter 接口获取) */ /** 线索1未通关时为 null进入关卡后由 enter 接口获取) */
clue1: string | null; clue1: string | null;
/** 线索2未通关时为 null */ /** 线索2未通关时为 null */

View File

@@ -78,10 +78,11 @@ export class LevelDataManager {
const firstLevel = apiData[0]; const firstLevel = apiData[0];
onProgress?.(0.3, '正在加载游戏必备资源...'); onProgress?.(0.3, '正在加载游戏必备资源...');
const spriteFrame = await this._loadImage(firstLevel.imageUrl); const [spriteFrame1, spriteFrame2] = await Promise.all([
if (spriteFrame) { this._loadImage(firstLevel.image1Url),
this._levelConfigs.set(0, this._createRuntimeConfig(firstLevel, spriteFrame)); this._loadImage(firstLevel.image2Url),
} ]);
this._levelConfigs.set(0, this._createRuntimeConfig(firstLevel, spriteFrame1, spriteFrame2));
console.log('[LevelDataManager] 初始化完成,第一关资源已加载'); console.log('[LevelDataManager] 初始化完成,第一关资源已加载');
onProgress?.(0.8, '游戏资源加载完成'); onProgress?.(0.8, '游戏资源加载完成');
@@ -245,15 +246,18 @@ export class LevelDataManager {
console.log(`[LevelDataManager] 开始加载关卡 ${index} 资源...`); console.log(`[LevelDataManager] 开始加载关卡 ${index} 资源...`);
const data = this._apiData[index]; const data = this._apiData[index];
const spriteFrame = await this._loadImage(data.imageUrl); const [spriteFrame1, spriteFrame2] = await Promise.all([
this._loadImage(data.image1Url),
this._loadImage(data.image2Url),
]);
this._loadingLevels.delete(index); this._loadingLevels.delete(index);
if (!spriteFrame) { if (!spriteFrame1) {
console.error(`[LevelDataManager] 加载关卡 ${index} 图片失败`); console.error(`[LevelDataManager] 加载关卡 ${index} 图片1失败`);
return null; return null;
} }
const config = this._createRuntimeConfig(data, spriteFrame); const config = this._createRuntimeConfig(data, spriteFrame1, spriteFrame2);
this._levelConfigs.set(index, config); this._levelConfigs.set(index, config);
console.log(`[LevelDataManager] 关卡 ${index} 资源加载完成`); console.log(`[LevelDataManager] 关卡 ${index} 资源加载完成`);
@@ -358,13 +362,18 @@ export class LevelDataManager {
/** /**
* 创建运行时关卡配置 * 创建运行时关卡配置
* @param data API 关卡数据 * @param data API 关卡数据
* @param spriteFrame 已加载的精灵帧 * @param spriteFrame1 已加载的图片1精灵帧
* @param spriteFrame2 已加载的图片2精灵帧
*/ */
private _createRuntimeConfig(data: ApiLevelData, spriteFrame: SpriteFrame | null): RuntimeLevelConfig { private _createRuntimeConfig(data: ApiLevelData, spriteFrame1: SpriteFrame | null, spriteFrame2: SpriteFrame | null): RuntimeLevelConfig {
return { return {
id: data.id, id: data.id,
name: `${data.level}`, name: `${data.level}`,
spriteFrame: spriteFrame, spriteFrame1: spriteFrame1,
spriteFrame2: spriteFrame2,
image1Description: data.image1Description,
image2Description: data.image2Description,
punchline: data.punchline,
clue1: data.hint1, clue1: data.hint1,
clue2: data.hint2, clue2: data.hint2,
clue3: data.hint3, clue3: data.hint3,

View File

@@ -23,7 +23,7 @@ export class ShareManager {
/** 分享模式的关卡数据null 表示正常模式) */ /** 分享模式的关卡数据null 表示正常模式) */
private _shareLevels: RuntimeLevelConfig[] | null = null; private _shareLevels: RuntimeLevelConfig[] | null = null;
/** API 返回的原始关卡数据(保留 imageUrl 用于懒加载) */ /** API 返回的原始关卡数据(保留 image1Url/image2Url 用于懒加载) */
private _shareApiLevels: ShareLevelData[] = []; private _shareApiLevels: ShareLevelData[] = [];
private _shareTitle: string = ''; private _shareTitle: string = '';
@@ -91,7 +91,11 @@ export class ShareManager {
const runtimeLevels: RuntimeLevelConfig[] = levels.map((level) => ({ const runtimeLevels: RuntimeLevelConfig[] = levels.map((level) => ({
id: level.id, id: level.id,
name: `${level.level}`, name: `${level.level}`,
spriteFrame: null, spriteFrame1: null,
spriteFrame2: null,
image1Description: level.image1Description,
image2Description: level.image2Description,
punchline: level.punchline,
clue1: level.hint1, clue1: level.hint1,
clue2: level.hint2, clue2: level.hint2,
clue3: level.hint3, clue3: level.hint3,
@@ -99,11 +103,17 @@ export class ShareManager {
completed: false, completed: false,
})); }));
// 预加载首关图片 // 预加载首关图片(两张并行加载)
if (levels.length > 0) { if (levels.length > 0) {
const sf = await this._loadImage(levels[0].imageUrl); const [sf1, sf2] = await Promise.all([
if (sf) { this._loadImage(levels[0].image1Url),
runtimeLevels[0].spriteFrame = sf; this._loadImage(levels[0].image2Url),
]);
if (sf1) {
runtimeLevels[0].spriteFrame1 = sf1;
}
if (sf2) {
runtimeLevels[0].spriteFrame2 = sf2;
} }
} }
@@ -143,15 +153,21 @@ export class ShareManager {
} }
const config = this._shareLevels[index]; const config = this._shareLevels[index];
if (config.spriteFrame) { if (config.spriteFrame1) {
return config; return config;
} }
const apiLevel = this._shareApiLevels[index]; const apiLevel = this._shareApiLevels[index];
if (apiLevel?.imageUrl) { if (apiLevel?.image1Url) {
const sf = await this._loadImage(apiLevel.imageUrl); const [sf1, sf2] = await Promise.all([
if (sf) { this._loadImage(apiLevel.image1Url),
config.spriteFrame = sf; this._loadImage(apiLevel.image2Url),
]);
if (sf1) {
config.spriteFrame1 = sf1;
}
if (sf2) {
config.spriteFrame2 = sf2;
} }
} }

View File

@@ -38,8 +38,8 @@ export class StorageManager {
/** 默认体力值 */ /** 默认体力值 */
private static readonly DEFAULT_STAMINA: StaminaInfo = { private static readonly DEFAULT_STAMINA: StaminaInfo = {
current: 5, current: 50,
max: 5, max: 50,
nextRecoverAt: null, nextRecoverAt: null,
}; };