# 微信小游戏适配器 ## 概述 微信小游戏平台适配器专为微信小游戏环境设计,处理微信小游戏的特殊限制和API。 ## 特性支持 - ✅ **Worker**: 支持(通过 `wx.createWorker` 创建,需要配置 game.json) - ❌ **SharedArrayBuffer**: 不支持 - ❌ **Transferable Objects**: 不支持(只支持可序列化对象) - ✅ **高精度时间**: 使用 `Date.now()` 或 `wx.getPerformance()` - ✅ **设备信息**: 完整的微信小游戏设备信息 ## 完整实现 ```typescript import type { IPlatformAdapter, PlatformWorker, WorkerCreationOptions, PlatformConfig, WeChatDeviceInfo } from '@esengine/ecs-framework'; /** * 微信小游戏平台适配器 * 支持微信小游戏环境 */ export class WeChatMiniGameAdapter implements IPlatformAdapter { public readonly name = 'wechat-minigame'; public readonly version: string; private systemInfo: any; constructor() { // 获取微信小游戏版本信息 this.systemInfo = this.getSystemInfo(); this.version = this.systemInfo.version || 'unknown'; } /** * 检查是否支持Worker */ public isWorkerSupported(): boolean { // 微信小游戏支持Worker,通过wx.createWorker创建 return typeof wx !== 'undefined' && typeof wx.createWorker === 'function'; } /** * 检查是否支持SharedArrayBuffer(不支持) */ public isSharedArrayBufferSupported(): boolean { return false; // 微信小游戏不支持SharedArrayBuffer } /** * 获取硬件并发数 */ public getHardwareConcurrency(): number { // 微信小游戏官方限制:最多只能创建 1 个 Worker return 1; } /** * 创建Worker * @param script 脚本内容或文件路径 * @param options Worker创建选项 */ public createWorker(script: string, options: WorkerCreationOptions = {}): PlatformWorker { if (!this.isWorkerSupported()) { throw new Error('微信小游戏不支持Worker'); } try { return new WeChatWorker(script, options); } catch (error) { throw new Error(`创建微信Worker失败: ${(error as Error).message}`); } } /** * 创建SharedArrayBuffer(不支持) */ public createSharedArrayBuffer(length: number): SharedArrayBuffer | null { return null; // 微信小游戏不支持SharedArrayBuffer } /** * 获取高精度时间戳 */ public getHighResTimestamp(): number { // 尝试使用微信的性能API,否则使用Date.now() if (typeof wx !== 'undefined' && wx.getPerformance) { const performance = wx.getPerformance(); return performance.now(); } return Date.now(); } /** * 获取平台配置 */ public getPlatformConfig(): PlatformConfig { return { maxWorkerCount: 1, // 微信小游戏最多支持 1 个 Worker supportsModuleWorker: false, // 不支持模块Worker supportsTransferableObjects: this.checkTransferableObjectsSupport(), maxSharedArrayBufferSize: 0, workerScriptPrefix: '', limitations: { noEval: true, // 微信小游戏限制eval使用 requiresWorkerInit: false, memoryLimit: this.getMemoryLimit(), workerNotSupported: false, workerLimitations: [ '最多只能创建 1 个 Worker', '创建新Worker前必须先调用 Worker.terminate()', 'Worker脚本必须为项目内相对路径', '需要在 game.json 中配置 workers 路径', '使用 worker.onMessage() 而不是 self.onmessage', '需要基础库 1.9.90 及以上版本' ] }, extensions: { platform: 'wechat-minigame', systemInfo: this.systemInfo, appId: this.systemInfo.host?.appId || 'unknown' } }; } /** * 获取微信小游戏设备信息 */ public getDeviceInfo(): WeChatDeviceInfo { return { // 设备基础信息 brand: this.systemInfo.brand, model: this.systemInfo.model, platform: this.systemInfo.platform, system: this.systemInfo.system, benchmarkLevel: this.systemInfo.benchmarkLevel, cpuType: this.systemInfo.cpuType, memorySize: this.systemInfo.memorySize, deviceAbi: this.systemInfo.deviceAbi, abi: this.systemInfo.abi, // 窗口信息 screenWidth: this.systemInfo.screenWidth, screenHeight: this.systemInfo.screenHeight, screenTop: this.systemInfo.screenTop, windowWidth: this.systemInfo.windowWidth, windowHeight: this.systemInfo.windowHeight, pixelRatio: this.systemInfo.pixelRatio, statusBarHeight: this.systemInfo.statusBarHeight, safeArea: this.systemInfo.safeArea, // 应用信息 version: this.systemInfo.version, language: this.systemInfo.language, theme: this.systemInfo.theme, SDKVersion: this.systemInfo.SDKVersion, enableDebug: this.systemInfo.enableDebug, fontSizeSetting: this.systemInfo.fontSizeSetting, host: this.systemInfo.host }; } /** * 异步获取完整的平台配置 */ public async getPlatformConfigAsync(): Promise { // 可以在这里添加异步获取设备性能信息的逻辑 const baseConfig = this.getPlatformConfig(); // 尝试获取设备性能信息 try { const benchmarkLevel = await this.getBenchmarkLevel(); baseConfig.extensions = { ...baseConfig.extensions, benchmarkLevel }; } catch (error) { console.warn('获取性能基准失败:', error); } return baseConfig; } /** * 检查是否支持Transferable Objects */ private checkTransferableObjectsSupport(): boolean { // 微信小游戏不支持 Transferable Objects // 基础库 2.20.2 之前只支持可序列化的 key-value 对象 // 2.20.2 之后支持任意类型数据,但仍然不支持 Transferable Objects return false; } /** * 获取系统信息 */ private getSystemInfo(): any { if (typeof wx !== 'undefined' && wx.getSystemInfoSync) { try { return wx.getSystemInfoSync(); } catch (error) { console.warn('获取微信系统信息失败:', error); return {}; } } return {}; } /** * 获取内存限制 */ private getMemoryLimit(): number { // 微信小游戏通常有内存限制 const memorySize = this.systemInfo.memorySize; if (memorySize) { // 解析内存大小字符串(如 "4GB") const match = memorySize.match(/(\d+)([GM]B)?/i); if (match) { const value = parseInt(match[1], 10); const unit = match[2]?.toUpperCase(); if (unit === 'GB') { return value * 1024 * 1024 * 1024; } else if (unit === 'MB') { return value * 1024 * 1024; } } } // 默认限制为512MB return 512 * 1024 * 1024; } /** * 异步获取设备性能基准 */ private async getBenchmarkLevel(): Promise { return new Promise((resolve) => { if (typeof wx !== 'undefined' && wx.getDeviceInfo) { wx.getDeviceInfo({ success: (res: any) => { resolve(res.benchmarkLevel || 0); }, fail: () => { resolve(0); } }); } else { resolve(this.systemInfo.benchmarkLevel || 0); } }); } } /** * 微信Worker封装 */ class WeChatWorker implements PlatformWorker { private _state: 'running' | 'terminated' = 'running'; private worker: any; private scriptPath: string; private isTemporaryFile: boolean = false; constructor(script: string, options: WorkerCreationOptions = {}) { if (typeof wx === 'undefined' || typeof wx.createWorker !== 'function') { throw new Error('微信小游戏不支持Worker'); } try { // 判断 script 是文件路径还是脚本内容 if (this.isFilePath(script)) { // 直接使用文件路径 this.scriptPath = script; this.isTemporaryFile = false; this.worker = wx.createWorker(this.scriptPath); } else { // 将脚本内容写入文件系统 this.scriptPath = this.writeScriptToFile(script, options.name); this.isTemporaryFile = true; this.worker = wx.createWorker(this.scriptPath); } } catch (error) { throw new Error(`创建微信Worker失败: ${(error as Error).message}`); } } /** * 判断是否为文件路径 */ private isFilePath(script: string): boolean { // 简单判断:如果包含 .js 后缀且不包含换行符或分号,认为是文件路径 return script.endsWith('.js') && !script.includes('\n') && !script.includes(';') && script.length < 200; // 文件路径通常不会太长 } /** * 将脚本内容写入文件系统 */ private writeScriptToFile(script: string, name?: string): string { const fs = wx.getFileSystemManager(); const fileName = name ? `worker-${name}.js` : `worker-${Date.now()}.js`; const filePath = `${wx.env.USER_DATA_PATH}/${fileName}`; try { fs.writeFileSync(filePath, script, 'utf8'); return filePath; } catch (error) { throw new Error(`写入Worker脚本文件失败: ${(error as Error).message}`); } } public get state(): 'running' | 'terminated' { return this._state; } public postMessage(message: any, transfer?: Transferable[]): void { if (this._state === 'terminated') { throw new Error('Worker已被终止'); } try { // 微信小游戏 Worker 只支持可序列化对象,忽略 transfer 参数 this.worker.postMessage(message); } catch (error) { throw new Error(`发送消息到微信Worker失败: ${(error as Error).message}`); } } public onMessage(handler: (event: { data: any }) => void): void { // 微信小游戏使用 onMessage 方法,不是 onmessage 属性 this.worker.onMessage((res: any) => { handler({ data: res }); }); } public onError(handler: (error: ErrorEvent) => void): void { // 注意:微信小游戏 Worker 的错误处理可能与标准不同 if (this.worker.onError) { this.worker.onError(handler); } } public terminate(): void { if (this._state === 'running') { try { this.worker.terminate(); this._state = 'terminated'; // 清理临时脚本文件 this.cleanupScriptFile(); } catch (error) { console.error('终止微信Worker失败:', error); } } } /** * 清理临时脚本文件 */ private cleanupScriptFile(): void { // 只清理临时创建的文件,不清理用户提供的文件路径 if (this.scriptPath && this.isTemporaryFile) { try { const fs = wx.getFileSystemManager(); fs.unlinkSync(this.scriptPath); } catch (error) { console.warn('清理Worker脚本文件失败:', error); } } } } ``` ## 使用方法 ### 1. 复制代码 将上述代码复制到你的项目中,例如 `src/platform/WeChatMiniGameAdapter.ts`。 ### 2. 注册适配器 ```typescript import { PlatformManager } from '@esengine/ecs-framework'; import { WeChatMiniGameAdapter } from './platform/WeChatMiniGameAdapter'; // 检查是否在微信小游戏环境 if (typeof wx !== 'undefined') { const wechatAdapter = new WeChatMiniGameAdapter(); PlatformManager.getInstance().registerAdapter(wechatAdapter); } ``` ### 3. Worker 使用方式 微信小游戏适配器支持两种 Worker 使用方式: #### 方式一:使用脚本文件路径(推荐) ```typescript // 1. 在 game.json 中配置 Worker 路径 /* { "workers": "workers" } */ // 2. 创建 Worker 脚本文件 workers/physics.js // workers/physics.js 内容: /* // 微信小游戏 Worker 中使用 worker 对象,不是 self worker.onMessage(function(data) { const { entities, deltaTime, systemConfig } = data; // 处理物理计算 const results = entities.map(entity => { entity.vy += systemConfig.gravity * deltaTime; entity.x += entity.vx * deltaTime; entity.y += entity.vy * deltaTime; return entity; }); worker.postMessage({ result: results }); }); */ // 2. 在 WorkerEntitySystem 中使用 class PhysicsSystem extends WorkerEntitySystem { constructor() { super(Matcher.all(Transform, Velocity), { enableWorker: true, workerCount: 1, // 微信小游戏限制 systemConfig: { gravity: 100 } }); } // 重写创建 Worker 脚本的方法,返回文件路径 private createWorkerScript(): string { return 'workers/physics.js'; // 微信小游戏使用相对路径 } } ``` #### 方式二:动态脚本内容(自动处理) ```typescript // WorkerEntitySystem 会自动将用户的 workerProcess 函数序列化 class PhysicsSystem extends WorkerEntitySystem { protected workerProcess(entities, deltaTime, config) { return entities.map(entity => { entity.vy += config.gravity * deltaTime; entity.x += entity.vx * deltaTime; entity.y += entity.vy * deltaTime; return entity; }); } } // 框架会自动将此函数内容写入临时文件并创建 Worker ``` ### 4. 获取设备信息 ```typescript const manager = PlatformManager.getInstance(); if (manager.hasAdapter()) { const adapter = manager.getAdapter(); console.log('微信设备信息:', adapter.getDeviceInfo()); } ``` ## 官方文档参考 在使用微信小游戏 Worker 之前,建议先阅读官方文档: - [wx.createWorker API](https://developers.weixin.qq.com/minigame/dev/api/worker/wx.createWorker.html) - [Worker.postMessage API](https://developers.weixin.qq.com/minigame/dev/api/worker/Worker.postMessage.html) - [Worker.onMessage API](https://developers.weixin.qq.com/minigame/dev/api/worker/Worker.onMessage.html) - [Worker.terminate API](https://developers.weixin.qq.com/minigame/dev/api/worker/Worker.terminate.html) ## 重要注意事项 ### Worker 限制和配置 微信小游戏的 Worker 有以下限制: - **数量限制**: 最多只能创建 1 个 Worker - **版本要求**: 需要基础库 1.9.90 及以上版本 - **配置要求**: 必须在 `game.json` 中配置 workers 路径 - **文件路径**: Worker 脚本必须为项目内的相对路径 - **生命周期**: 创建新 Worker 前必须先调用 `Worker.terminate()` 终止当前 Worker - **API 差异**: 使用 `worker.onMessage()` 而不是 `self.onmessage` #### 必要配置 在 `game.json` 中添加 workers 配置: ```json { "deviceOrientation": "portrait", "showStatusBar": false, "workers": "workers", "subpackages": [] } ``` ### 内存限制 微信小游戏有严格的内存限制: - 通常限制在 256MB - 512MB - 需要及时释放不用的资源 - 避免内存泄漏 ### API 限制 - 不支持 `eval()` 函数 - 不支持 `Function` 构造器 - DOM API 受限 - 文件系统 API 受限 ## 性能优化建议 ### 1. 分帧处理 ```typescript class FramedProcessor { private tasks: (() => void)[] = []; private isProcessing = false; public addTask(task: () => void): void { this.tasks.push(task); if (!this.isProcessing) { this.processNextFrame(); } } private processNextFrame(): void { this.isProcessing = true; const startTime = Date.now(); const frameTime = 16; // 16ms per frame while (this.tasks.length > 0 && Date.now() - startTime < frameTime) { const task = this.tasks.shift(); if (task) task(); } if (this.tasks.length > 0) { setTimeout(() => this.processNextFrame(), 0); } else { this.isProcessing = false; } } } ``` ### 2. 内存管理 ```typescript class MemoryManager { private static readonly MAX_MEMORY = 256 * 1024 * 1024; // 256MB public static checkMemoryUsage(): void { if (typeof wx !== 'undefined' && wx.getPerformance) { const performance = wx.getPerformance(); const memoryInfo = performance.getEntries().find( (entry: any) => entry.entryType === 'memory' ); if (memoryInfo && memoryInfo.usedJSHeapSize > this.MAX_MEMORY * 0.8) { console.warn('内存使用率过高,建议清理资源'); // 触发垃圾回收或资源清理 } } } } ``` ## 调试技巧 ```typescript // 检查微信小游戏环境 if (typeof wx !== 'undefined') { const adapter = new WeChatMiniGameAdapter(); console.log('微信版本:', adapter.version); console.log('设备信息:', adapter.getDeviceInfo()); console.log('平台配置:', adapter.getPlatformConfig()); // 检查功能支持 console.log('Worker支持:', adapter.isWorkerSupported()); console.log('SharedArrayBuffer支持:', adapter.isSharedArrayBufferSupported()); } ``` ## 微信小游戏特殊API ```typescript // 获取设备性能等级 if (typeof wx !== 'undefined' && wx.getDeviceInfo) { wx.getDeviceInfo({ success: (res) => { console.log('设备性能等级:', res.benchmarkLevel); } }); } // 监听内存警告 if (typeof wx !== 'undefined' && wx.onMemoryWarning) { wx.onMemoryWarning(() => { console.warn('收到内存警告,开始清理资源'); // 清理不必要的资源 }); } ```