feat: 接入关卡配置 API 并支持降级到本地配置
- 新增 LevelDataManager 单例管理关卡数据 - 新增 HttpUtil 封装 XMLHttpRequest 请求 - 新增 LevelTypes 类型定义 - PageLoading 集成 API 数据预加载(0-80% 进度) - PageLevel 支持优先使用 API 数据,失败时降级到本地配置 - 字段映射: hint1/2/3 → clue1/2/3, imageUrl → SpriteFrame
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { _decorator, Component, ProgressBar } from 'cc';
|
import { _decorator, Component, ProgressBar, Label } from 'cc';
|
||||||
import { ViewManager } from './scripts/core/ViewManager';
|
import { ViewManager } from './scripts/core/ViewManager';
|
||||||
|
import { LevelDataManager } from './scripts/utils/LevelDataManager';
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,6 +12,9 @@ export class PageLoading extends Component {
|
|||||||
@property(ProgressBar)
|
@property(ProgressBar)
|
||||||
progressBar: ProgressBar | null = null;
|
progressBar: ProgressBar | null = null;
|
||||||
|
|
||||||
|
@property(Label)
|
||||||
|
statusLabel: Label | null = null;
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
this._startPreload();
|
this._startPreload();
|
||||||
}
|
}
|
||||||
@@ -18,16 +22,22 @@ export class PageLoading extends Component {
|
|||||||
/**
|
/**
|
||||||
* 开始预加载
|
* 开始预加载
|
||||||
*/
|
*/
|
||||||
private _startPreload(): void {
|
private async _startPreload(): Promise<void> {
|
||||||
// 初始化进度条
|
// 初始化进度条
|
||||||
if (this.progressBar) {
|
if (this.progressBar) {
|
||||||
this.progressBar.progress = 0;
|
this.progressBar.progress = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预加载 PageHome
|
// 阶段1: 初始化 LevelDataManager (0-80%)
|
||||||
|
await LevelDataManager.instance.initialize((progress, message) => {
|
||||||
|
this._updateProgress(progress);
|
||||||
|
this._updateStatusLabel(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 阶段2: 预加载 PageHome (80-100%)
|
||||||
ViewManager.instance.preload('PageHome',
|
ViewManager.instance.preload('PageHome',
|
||||||
(progress) => {
|
(progress) => {
|
||||||
this._updateProgress(progress);
|
this._updateProgress(0.8 + progress * 0.2);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this._onPreloadComplete();
|
this._onPreloadComplete();
|
||||||
@@ -44,12 +54,22 @@ export class PageLoading extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新状态标签
|
||||||
|
*/
|
||||||
|
private _updateStatusLabel(message: string): void {
|
||||||
|
if (this.statusLabel) {
|
||||||
|
this.statusLabel.string = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预加载完成回调
|
* 预加载完成回调
|
||||||
*/
|
*/
|
||||||
private _onPreloadComplete(): void {
|
private _onPreloadComplete(): void {
|
||||||
// 确保进度条显示完成
|
// 确保进度条显示完成
|
||||||
this._updateProgress(1);
|
this._updateProgress(1);
|
||||||
|
this._updateStatusLabel('加载完成');
|
||||||
|
|
||||||
// 打开 PageHome
|
// 打开 PageHome
|
||||||
ViewManager.instance.open('PageHome', {
|
ViewManager.instance.open('PageHome', {
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { BaseView } from 'db://assets/scripts/core/BaseView';
|
|||||||
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
||||||
import { StorageManager } from 'db://assets/scripts/utils/StorageManager';
|
import { StorageManager } from 'db://assets/scripts/utils/StorageManager';
|
||||||
import { WxSDK } from 'db://assets/scripts/utils/WxSDK';
|
import { WxSDK } from 'db://assets/scripts/utils/WxSDK';
|
||||||
|
import { LevelDataManager } from 'db://assets/scripts/utils/LevelDataManager';
|
||||||
|
import { RuntimeLevelConfig } from 'db://assets/scripts/types/LevelTypes';
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,6 +106,9 @@ export class PageLevel extends BaseView {
|
|||||||
/** 倒计时是否结束 */
|
/** 倒计时是否结束 */
|
||||||
private _isTimeUp: boolean = false;
|
private _isTimeUp: boolean = false;
|
||||||
|
|
||||||
|
/** 当前关卡配置(API 或本地) */
|
||||||
|
private _currentConfig: RuntimeLevelConfig | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 页面首次加载时调用
|
* 页面首次加载时调用
|
||||||
*/
|
*/
|
||||||
@@ -145,18 +150,67 @@ export class PageLevel extends BaseView {
|
|||||||
* 初始化关卡
|
* 初始化关卡
|
||||||
*/
|
*/
|
||||||
private initLevel(): void {
|
private initLevel(): void {
|
||||||
const config = this.levelConfigs[this.currentLevelIndex];
|
// 优先使用 API 数据
|
||||||
|
if (LevelDataManager.instance.hasApiData()) {
|
||||||
|
this._initFromApiConfig();
|
||||||
|
} else {
|
||||||
|
this._initFromLocalConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 API 配置初始化关卡
|
||||||
|
*/
|
||||||
|
private _initFromApiConfig(): void {
|
||||||
|
const config = LevelDataManager.instance.getLevelConfig(this.currentLevelIndex);
|
||||||
if (!config) {
|
if (!config) {
|
||||||
console.warn('[PageLevel] 没有找到关卡配置');
|
console.warn('[PageLevel] 没有找到 API 关卡配置');
|
||||||
|
// 降级到本地配置
|
||||||
|
this._initFromLocalConfig();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[PageLevel] 使用 API 配置初始化关卡 ${this.currentLevelIndex + 1}: ${config.name}`);
|
||||||
|
this._applyLevelConfig(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从本地配置初始化关卡
|
||||||
|
*/
|
||||||
|
private _initFromLocalConfig(): void {
|
||||||
|
const config = this.levelConfigs[this.currentLevelIndex];
|
||||||
|
if (!config) {
|
||||||
|
console.warn('[PageLevel] 没有找到本地关卡配置');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建 RuntimeLevelConfig 从本地配置
|
||||||
|
const runtimeConfig: RuntimeLevelConfig = {
|
||||||
|
id: this.currentLevelIndex,
|
||||||
|
name: `关卡 ${this.currentLevelIndex + 1}`,
|
||||||
|
spriteFrame: config.mainImage,
|
||||||
|
clue1: config.clue1,
|
||||||
|
clue2: config.clue2,
|
||||||
|
clue3: config.clue3,
|
||||||
|
answer: config.answer
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`[PageLevel] 使用本地配置初始化关卡 ${this.currentLevelIndex + 1}`);
|
||||||
|
this._applyLevelConfig(runtimeConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用关卡配置(通用初始化逻辑)
|
||||||
|
*/
|
||||||
|
private _applyLevelConfig(config: RuntimeLevelConfig): void {
|
||||||
|
this._currentConfig = config;
|
||||||
|
|
||||||
// 重置倒计时状态
|
// 重置倒计时状态
|
||||||
this._isTimeUp = false;
|
this._isTimeUp = false;
|
||||||
this._countdown = 60;
|
this._countdown = 60;
|
||||||
|
|
||||||
// 设置主图
|
// 设置主图
|
||||||
this.setMainImage(config.mainImage);
|
this.setMainImage(config.spriteFrame);
|
||||||
|
|
||||||
// 设置线索1(默认解锁)
|
// 设置线索1(默认解锁)
|
||||||
this.setClue(1, config.clue1);
|
this.setClue(1, config.clue1);
|
||||||
@@ -170,13 +224,12 @@ export class PageLevel extends BaseView {
|
|||||||
this.showUnlockButton(3);
|
this.showUnlockButton(3);
|
||||||
|
|
||||||
// 根据答案长度创建单个输入框
|
// 根据答案长度创建单个输入框
|
||||||
const answerLength = config.answer.length;
|
this.createSingleInput(config.answer.length);
|
||||||
this.createSingleInput(answerLength);
|
|
||||||
|
|
||||||
// 更新倒计时显示
|
// 更新倒计时显示
|
||||||
this.updateClockLabel();
|
this.updateClockLabel();
|
||||||
|
|
||||||
console.log(`[PageLevel] 初始化关卡 ${this.currentLevelIndex + 1}, 答案长度: ${answerLength}`);
|
console.log(`[PageLevel] 初始化关卡 ${this.currentLevelIndex + 1}, 答案长度: ${config.answer.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -455,9 +508,8 @@ export class PageLevel extends BaseView {
|
|||||||
this.showClue(index);
|
this.showClue(index);
|
||||||
|
|
||||||
// 设置线索内容
|
// 设置线索内容
|
||||||
const config = this.levelConfigs[this.currentLevelIndex];
|
if (this._currentConfig) {
|
||||||
if (config) {
|
const clueContent = index === 2 ? this._currentConfig.clue2 : this._currentConfig.clue3;
|
||||||
const clueContent = index === 2 ? config.clue2 : config.clue3;
|
|
||||||
this.setClue(index, clueContent);
|
this.setClue(index, clueContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,13 +667,12 @@ export class PageLevel extends BaseView {
|
|||||||
* 提交答案
|
* 提交答案
|
||||||
*/
|
*/
|
||||||
onSubmitAnswer(): void {
|
onSubmitAnswer(): void {
|
||||||
const config = this.levelConfigs[this.currentLevelIndex];
|
if (!this._currentConfig) return;
|
||||||
if (!config) return;
|
|
||||||
|
|
||||||
const userAnswer = this.getAnswer();
|
const userAnswer = this.getAnswer();
|
||||||
console.log(`[PageLevel] 提交答案: ${userAnswer}, 正确答案: ${config.answer}`);
|
console.log(`[PageLevel] 提交答案: ${userAnswer}, 正确答案: ${this._currentConfig.answer}`);
|
||||||
|
|
||||||
if (userAnswer === config.answer) {
|
if (userAnswer === this._currentConfig.answer) {
|
||||||
// 答案正确
|
// 答案正确
|
||||||
this.playClickSound();
|
this.playClickSound();
|
||||||
this.showSuccess();
|
this.showSuccess();
|
||||||
@@ -671,7 +722,13 @@ export class PageLevel extends BaseView {
|
|||||||
private nextLevel(): void {
|
private nextLevel(): void {
|
||||||
this.currentLevelIndex++;
|
this.currentLevelIndex++;
|
||||||
|
|
||||||
if (this.currentLevelIndex >= this.levelConfigs.length) {
|
// 检查是否还有关卡
|
||||||
|
const manager = LevelDataManager.instance;
|
||||||
|
const totalLevels = manager.hasApiData()
|
||||||
|
? manager.getLevelCount()
|
||||||
|
: this.levelConfigs.length;
|
||||||
|
|
||||||
|
if (this.currentLevelIndex >= totalLevels) {
|
||||||
// 所有关卡完成
|
// 所有关卡完成
|
||||||
console.log('[PageLevel] 恭喜通关!');
|
console.log('[PageLevel] 恭喜通关!');
|
||||||
this.stopCountdown();
|
this.stopCountdown();
|
||||||
|
|||||||
53
assets/scripts/types/LevelTypes.ts
Normal file
53
assets/scripts/types/LevelTypes.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { SpriteFrame } from 'cc';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API 返回的单个关卡数据结构
|
||||||
|
*/
|
||||||
|
export interface ApiLevelData {
|
||||||
|
/** 关卡 ID */
|
||||||
|
id: number;
|
||||||
|
/** 关卡名称 */
|
||||||
|
name: string;
|
||||||
|
/** 主图 URL */
|
||||||
|
imageUrl: string;
|
||||||
|
/** 线索1(映射到 clue1) */
|
||||||
|
hint1: string;
|
||||||
|
/** 线索2(映射到 clue2) */
|
||||||
|
hint2: string;
|
||||||
|
/** 线索3(映射到 clue3) */
|
||||||
|
hint3: string;
|
||||||
|
/** 答案 */
|
||||||
|
answer: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API 响应结构
|
||||||
|
*/
|
||||||
|
export interface ApiResponse {
|
||||||
|
/** 状态码,0 表示成功 */
|
||||||
|
code: number;
|
||||||
|
/** 响应消息 */
|
||||||
|
message: string;
|
||||||
|
/** 关卡数据数组 */
|
||||||
|
data: ApiLevelData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运行时关卡配置(包含已加载的图片)
|
||||||
|
*/
|
||||||
|
export interface RuntimeLevelConfig {
|
||||||
|
/** 关卡 ID */
|
||||||
|
id: number;
|
||||||
|
/** 关卡名称 */
|
||||||
|
name: string;
|
||||||
|
/** 主图 SpriteFrame(可能为 null 如果加载失败) */
|
||||||
|
spriteFrame: SpriteFrame | null;
|
||||||
|
/** 线索1 */
|
||||||
|
clue1: string;
|
||||||
|
/** 线索2 */
|
||||||
|
clue2: string;
|
||||||
|
/** 线索3 */
|
||||||
|
clue3: string;
|
||||||
|
/** 答案 */
|
||||||
|
answer: string;
|
||||||
|
}
|
||||||
75
assets/scripts/utils/HttpUtil.ts
Normal file
75
assets/scripts/utils/HttpUtil.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* HTTP 请求工具类
|
||||||
|
* 封装 XMLHttpRequest,支持 GET/POST 请求
|
||||||
|
*/
|
||||||
|
export class HttpUtil {
|
||||||
|
/**
|
||||||
|
* 发送 GET 请求
|
||||||
|
* @param url 请求 URL
|
||||||
|
* @param timeout 超时时间(毫秒),默认 10000
|
||||||
|
* @returns Promise<Response>
|
||||||
|
*/
|
||||||
|
static get<T>(url: string, timeout: number = 10000): Promise<T> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
xhr.open('GET', url, true);
|
||||||
|
xhr.timeout = timeout;
|
||||||
|
xhr.responseType = 'json';
|
||||||
|
|
||||||
|
xhr.onload = () => {
|
||||||
|
if (xhr.status >= 200 && xhr.status < 300) {
|
||||||
|
resolve(xhr.response as T);
|
||||||
|
} else {
|
||||||
|
reject(new Error(`HTTP 错误: ${xhr.status}`));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = () => {
|
||||||
|
reject(new Error('网络请求失败'));
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.ontimeout = () => {
|
||||||
|
reject(new Error('请求超时'));
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送 POST 请求
|
||||||
|
* @param url 请求 URL
|
||||||
|
* @param data 请求体数据
|
||||||
|
* @param timeout 超时时间(毫秒),默认 10000
|
||||||
|
* @returns Promise<Response>
|
||||||
|
*/
|
||||||
|
static post<T>(url: string, data: object, timeout: number = 10000): Promise<T> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
xhr.open('POST', url, true);
|
||||||
|
xhr.timeout = timeout;
|
||||||
|
xhr.responseType = 'json';
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
|
|
||||||
|
xhr.onload = () => {
|
||||||
|
if (xhr.status >= 200 && xhr.status < 300) {
|
||||||
|
resolve(xhr.response as T);
|
||||||
|
} else {
|
||||||
|
reject(new Error(`HTTP 错误: ${xhr.status}`));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = () => {
|
||||||
|
reject(new Error('网络请求失败'));
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.ontimeout = () => {
|
||||||
|
reject(new Error('请求超时'));
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send(JSON.stringify(data));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
207
assets/scripts/utils/LevelDataManager.ts
Normal file
207
assets/scripts/utils/LevelDataManager.ts
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
import { SpriteFrame, Texture2D, ImageAsset, assetManager } from 'cc';
|
||||||
|
import { HttpUtil } from './HttpUtil';
|
||||||
|
import { ApiLevelData, ApiResponse, RuntimeLevelConfig } from '../types/LevelTypes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进度回调类型
|
||||||
|
* @param progress 进度值 0-1
|
||||||
|
* @param message 进度消息
|
||||||
|
*/
|
||||||
|
export type ProgressCallback = (progress: number, message: string) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关卡数据管理器
|
||||||
|
* 单例模式,负责从 API 获取关卡数据并预加载图片
|
||||||
|
*/
|
||||||
|
export class LevelDataManager {
|
||||||
|
private static _instance: LevelDataManager | null = null;
|
||||||
|
|
||||||
|
/** API 地址 */
|
||||||
|
private readonly API_URL = 'https://ilookai.cn/api/v1/wechat-game/levels';
|
||||||
|
|
||||||
|
/** 请求超时时间(毫秒) */
|
||||||
|
private readonly REQUEST_TIMEOUT = 8000;
|
||||||
|
|
||||||
|
/** 运行时关卡配置缓存 */
|
||||||
|
private _levelConfigs: RuntimeLevelConfig[] = [];
|
||||||
|
|
||||||
|
/** 是否已成功从 API 获取数据 */
|
||||||
|
private _hasApiData: boolean = false;
|
||||||
|
|
||||||
|
/** 图片缓存:URL -> SpriteFrame */
|
||||||
|
private _imageCache: Map<string, SpriteFrame> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取单例实例
|
||||||
|
*/
|
||||||
|
static get instance(): LevelDataManager {
|
||||||
|
if (!this._instance) {
|
||||||
|
this._instance = new LevelDataManager();
|
||||||
|
}
|
||||||
|
return this._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私有构造函数
|
||||||
|
*/
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化:从 API 获取数据并预加载图片
|
||||||
|
* @param onProgress 进度回调
|
||||||
|
* @returns 是否初始化成功
|
||||||
|
*/
|
||||||
|
async initialize(onProgress?: ProgressCallback): Promise<boolean> {
|
||||||
|
console.log('[LevelDataManager] 开始初始化');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 阶段1: 获取 API 数据 (0-20%)
|
||||||
|
onProgress?.(0, '正在获取关卡数据...');
|
||||||
|
const apiData = await this._fetchApiData();
|
||||||
|
|
||||||
|
if (!apiData || apiData.length === 0) {
|
||||||
|
console.warn('[LevelDataManager] API 返回空数据');
|
||||||
|
onProgress?.(0.2, 'API 数据为空,使用本地配置');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[LevelDataManager] 获取到 ${apiData.length} 个关卡数据`);
|
||||||
|
onProgress?.(0.2, `获取到 ${apiData.length} 个关卡`);
|
||||||
|
|
||||||
|
// 阶段2: 预加载所有图片 (20-80%)
|
||||||
|
const configs = await this._preloadImages(apiData, (progress) => {
|
||||||
|
onProgress?.(0.2 + progress * 0.6, '正在加载关卡资源...');
|
||||||
|
});
|
||||||
|
|
||||||
|
this._levelConfigs = configs;
|
||||||
|
this._hasApiData = true;
|
||||||
|
|
||||||
|
console.log('[LevelDataManager] 初始化完成');
|
||||||
|
onProgress?.(0.8, '关卡资源加载完成');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[LevelDataManager] 初始化失败:', error);
|
||||||
|
onProgress?.(0.2, '获取数据失败,使用本地配置');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定关卡配置
|
||||||
|
* @param index 关卡索引
|
||||||
|
*/
|
||||||
|
getLevelConfig(index: number): RuntimeLevelConfig | null {
|
||||||
|
if (index < 0 || index >= this._levelConfigs.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this._levelConfigs[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取关卡总数
|
||||||
|
*/
|
||||||
|
getLevelCount(): number {
|
||||||
|
return this._levelConfigs.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有 API 数据
|
||||||
|
*/
|
||||||
|
hasApiData(): boolean {
|
||||||
|
return this._hasApiData && this._levelConfigs.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 API 获取关卡数据
|
||||||
|
*/
|
||||||
|
private async _fetchApiData(): Promise<ApiLevelData[] | null> {
|
||||||
|
try {
|
||||||
|
const response = await HttpUtil.get<ApiResponse>(this.API_URL, this.REQUEST_TIMEOUT);
|
||||||
|
|
||||||
|
if (response.code !== 0) {
|
||||||
|
console.warn(`[LevelDataManager] API 返回错误码: ${response.code}, 消息: ${response.message}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[LevelDataManager] API 请求失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预加载所有图片
|
||||||
|
* @param apiData API 返回的关卡数据
|
||||||
|
* @param onProgress 进度回调
|
||||||
|
*/
|
||||||
|
private async _preloadImages(
|
||||||
|
apiData: ApiLevelData[],
|
||||||
|
onProgress?: (progress: number) => void
|
||||||
|
): Promise<RuntimeLevelConfig[]> {
|
||||||
|
const configs: RuntimeLevelConfig[] = [];
|
||||||
|
const total = apiData.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < total; i++) {
|
||||||
|
const data = apiData[i];
|
||||||
|
const spriteFrame = await this._loadImage(data.imageUrl);
|
||||||
|
|
||||||
|
configs.push({
|
||||||
|
id: data.id,
|
||||||
|
name: data.name,
|
||||||
|
spriteFrame: spriteFrame,
|
||||||
|
clue1: data.hint1,
|
||||||
|
clue2: data.hint2,
|
||||||
|
clue3: data.hint3,
|
||||||
|
answer: data.answer
|
||||||
|
});
|
||||||
|
|
||||||
|
onProgress?.((i + 1) / total);
|
||||||
|
}
|
||||||
|
|
||||||
|
return configs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载远程图片为 SpriteFrame
|
||||||
|
* @param url 图片 URL
|
||||||
|
*/
|
||||||
|
private async _loadImage(url: string): Promise<SpriteFrame | null> {
|
||||||
|
// 检查缓存
|
||||||
|
if (this._imageCache.has(url)) {
|
||||||
|
return this._imageCache.get(url)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
assetManager.loadRemote<ImageAsset>(url, (err, imageAsset) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(`[LevelDataManager] 加载图片失败: ${url}`, err);
|
||||||
|
resolve(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const texture = new Texture2D();
|
||||||
|
texture.image = imageAsset;
|
||||||
|
|
||||||
|
const spriteFrame = new SpriteFrame();
|
||||||
|
spriteFrame.texture = texture;
|
||||||
|
|
||||||
|
// 缓存
|
||||||
|
this._imageCache.set(url, spriteFrame);
|
||||||
|
|
||||||
|
resolve(spriteFrame);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除缓存
|
||||||
|
*/
|
||||||
|
clearCache(): void {
|
||||||
|
this._levelConfigs = [];
|
||||||
|
this._hasApiData = false;
|
||||||
|
this._imageCache.clear();
|
||||||
|
console.log('[LevelDataManager] 缓存已清除');
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user