cocos基础工程

This commit is contained in:
janing
2025-11-28 18:10:10 +08:00
parent 4e4863c7e6
commit 9742bf21fa
88 changed files with 4626 additions and 0 deletions

View File

@@ -0,0 +1,321 @@
/**
* WASM加载器
* 基于Cocos Creator官方文档的WASM/ASM加载最佳实践
* https://docs.cocos.com/creator/3.8/manual/zh/advanced-topics/wasm-asm-load.html
*/
import { Asset, assetManager, sys } from 'cc';
import { EDITOR } from 'cc/env';
// ASM模块内存配置常量
const PAGESIZE = 65536; // 64KiB
const PAGECOUNT = 32 * 16; // 可根据需要调整
const MEMORYSIZE = PAGESIZE * PAGECOUNT; // 32 MiB
export interface WasmLoadOptions {
bundleName?: string;
fileName: string;
editorUuid?: string; // 编辑器内的资源UUID
supportAsm?: boolean; // 是否支持ASM回退
asmFileName?: string; // ASM文件名
asmEditorUuid?: string; // ASM文件的编辑器UUID
wasmFactory?: any; // WASM工厂函数
asmFactory?: any; // ASM工厂函数
}
export interface WasmLoadResult {
instance: any;
isWasm: boolean; // true为WASMfalse为ASM
}
export class WasmLoader {
private static instance: WasmLoader;
private initialized = false;
private constructor() { }
public static getInstance(): WasmLoader {
if (!WasmLoader.instance) {
WasmLoader.instance = new WasmLoader();
}
return WasmLoader.instance;
}
/**
* 初始化WASM加载器
* 注册WASM/ASM文件的下载器和解析器
*/
public initialize(): void {
if (this.initialized) {
return;
}
// 判断当前平台是否支持加载WASM文件
// Cocos引擎目前暂不支持在iOS平台加载WASM文件
if (sys.hasFeature(sys.Feature.WASM) || (sys.isNative && sys.os !== sys.OS.IOS)) {
if (sys.isNative) {
// 原生平台
//@ts-ignore
assetManager.downloader.register('.wasm', assetManager.downloader._downloaders[".bin"]);
//@ts-ignore
assetManager.parser.register('.wasm', assetManager.parser._parsers[".bin"]);
} else if (sys.isBrowser || sys.platform === sys.Platform.WECHAT_GAME) {
// 浏览器或微信小游戏平台
//@ts-ignore
assetManager.downloader.register('.wasm', assetManager.downloader._downloadArrayBuffer);
//@ts-ignore
assetManager.downloader.register('.mem', assetManager.downloader._downloadArrayBuffer);
}
} else {
// 不支持WASM的平台注册ASM相关文件
if (sys.isNative) {
//@ts-ignore
assetManager.downloader.register('.mem', assetManager.downloader._downloaders[".bin"]);
//@ts-ignore
assetManager.parser.register('.mem', assetManager.parser._parsers[".bin"]);
}
}
this.initialized = true;
console.log('[WasmLoader] 初始化完成');
}
/**
* 加载WASM模块
* @param options 加载配置选项(必须包含工厂函数)
* @returns Promise<WasmLoadResult>
*/
public async loadWasmModule(options: WasmLoadOptions): Promise<WasmLoadResult> {
if (!this.initialized) {
this.initialize();
}
// 判断是否支持WASM
if (sys.hasFeature(sys.Feature.WASM) || (sys.isNative && sys.os !== sys.OS.IOS)) {
// 加载WASM
return this.loadWasm(options);
} else if (options.supportAsm && options.asmFileName && options.asmFactory) {
// 回退到ASM
return this.loadAsm(options);
} else {
throw new Error('当前平台不支持WASM且未配置ASM回退选项');
}
}
/**
* 加载WASM
*/
private async loadWasm(options: WasmLoadOptions): Promise<WasmLoadResult> {
if (!options.wasmFactory) {
throw new Error('WASM工厂函数未提供');
}
try {
const wasmFactory = options.wasmFactory;
// 加载WASM二进制文件
const wasmFile = await this.loadWasmFile(
options.bundleName || '',
options.fileName,
options.editorUuid
);
// 初始化WASM
const instance = await this.initWasm(wasmFactory, wasmFile);
return {
instance,
isWasm: true
};
} catch (error) {
console.error('[WasmLoader] WASM加载失败:', error);
throw error;
}
}
/**
* 加载ASM
*/
private async loadAsm(options: WasmLoadOptions): Promise<WasmLoadResult> {
if (!options.asmFactory) {
throw new Error('ASM工厂函数未提供');
}
try {
const asmFactory = options.asmFactory;
// 加载ASM内存文件
const asmFile = await this.loadWasmFile(
options.bundleName || '',
options.asmFileName || options.fileName,
options.asmEditorUuid
);
// 初始化ASM
const instance = await this.initAsm(asmFactory, asmFile);
return {
instance,
isWasm: false
};
} catch (error) {
console.error('[WasmLoader] ASM加载失败:', error);
throw error;
}
}
/**
* 加载WASM/ASM二进制文件
*/
private loadWasmFile(bundleName: string, fileName: string, editorUuid?: string): Promise<Asset> {
return new Promise<Asset>((resolve, reject) => {
if (EDITOR) {
// 编辑器内通过UUID加载资源
if (editorUuid) {
assetManager.loadAny(editorUuid, (err, file: Asset) => {
if (!err) {
resolve(file);
} else {
reject(err);
}
});
} else {
reject(new Error('编辑器环境下需要提供editorUuid'));
}
} else {
// 运行时通过Bundle加载
if (bundleName && fileName) {
assetManager.loadBundle(bundleName, (err, bundle) => {
if (!err) {
bundle.load(fileName, Asset, (err2: any, file: Asset) => {
if (!err2) {
resolve(file);
} else {
reject(err2);
}
});
} else {
reject(err);
}
});
} else {
reject(new Error('运行时环境下需要提供bundleName和fileName'));
}
}
});
}
/**
* 初始化WASM模块
*/
private initWasm(wasmFactory: any, wasmFile: Asset): Promise<any> {
const self = this;
return new Promise<any>((resolve, reject) => {
wasmFactory({
instantiateWasm(importObject: WebAssembly.Imports, receiveInstance: any) {
self.instantiateWasm(wasmFile, importObject).then((result) => {
receiveInstance(result.instance, result.module);
}).catch((err) => reject(err));
}
}).then((instance: any) => {
resolve(instance);
}).catch((err: any) => reject(err));
});
}
/**
* 实例化WASM
*/
private instantiateWasm(wasmFile: Asset, importObject: WebAssembly.Imports): Promise<any> {
if (sys.isBrowser || sys.isNative) {
//@ts-ignore
return WebAssembly.instantiate(wasmFile._file, importObject);
} else if (sys.platform === sys.Platform.WECHAT_GAME) {
//@ts-ignore
return CCWebAssembly.instantiate(wasmFile.nativeUrl, importObject);
} else {
return Promise.reject(new Error('不支持的平台'));
}
}
/**
* 初始化ASM模块
*/
private initAsm(asmFactory: any, asmFile: Asset): Promise<any> {
const asmMemory: any = {};
asmMemory.buffer = new ArrayBuffer(MEMORYSIZE);
const module = {
asmMemory,
memoryInitializerRequest: {
//@ts-ignore
response: asmFile._file,
status: 200,
} as Partial<XMLHttpRequest>,
};
return asmFactory(module);
}
/**
* 便捷方法加载简单的WASM模块仅WASM不支持ASM回退
*/
public async loadSimpleWasm(wasmFactory: any, fileName: string, editorUuid?: string, bundleName?: string): Promise<any> {
const result = await this.loadWasmModule({
fileName,
editorUuid,
bundleName,
supportAsm: false,
wasmFactory
});
return result.instance;
}
/**
* 便捷方法加载支持ASM回退的WASM模块
*/
public async loadWasmWithAsmFallback(
wasmFactory: any,
asmFactory: any,
fileName: string,
asmFileName: string,
editorUuid?: string,
asmEditorUuid?: string,
bundleName?: string
): Promise<WasmLoadResult> {
return this.loadWasmModule({
fileName,
editorUuid,
bundleName,
supportAsm: true,
asmFileName,
asmEditorUuid,
wasmFactory,
asmFactory
});
}
/**
* 检查当前平台是否支持WASM
*/
public isWasmSupported(): boolean {
return sys.hasFeature(sys.Feature.WASM) || (sys.isNative && sys.os !== sys.OS.IOS);
}
/**
* 获取推荐的加载策略
*/
public getRecommendedStrategy(): 'wasm' | 'asm' | 'unsupported' {
if (this.isWasmSupported()) {
return 'wasm';
} else if (sys.isNative || sys.isBrowser) {
return 'asm';
} else {
return 'unsupported';
}
}
}
// 导出单例实例
export const wasmLoader = WasmLoader.getInstance();

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "1a752554-ae7a-4ac3-b468-31ad25757d52",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,128 @@
/**
* WasmLoader 使用示例
*
* 这个文件展示如何使用 WasmLoader 来加载 WASM 模块
*/
import { wasmLoader } from './WasmLoader';
// 假设我们有一个 WASM 工厂函数(需要从 JS 胶水代码中导入)
// import wasmFactory from '../path/to/your-wasm-module.js';
export class WasmLoaderExample {
/**
* 示例1: 加载简单的 WASM 模块
*/
async loadSimpleWasmExample(wasmFactory: any) {
try {
// 首先需要初始化加载器(通常在应用启动时执行一次)
wasmLoader.initialize();
// 假设你有一个 WASM 工厂函数
// const wasmFactory = (await import('../wasm/pinball_physics.js')).default;
// 加载 WASM 模块
const instance = await wasmLoader.loadSimpleWasm(
wasmFactory, // WASM 工厂函数
'pinball_physics.wasm', // WASM 文件名
'44cacb3c-e901-455d-b3e1-1c38a69718e1', // 编辑器中的 UUID可选
'wasmFiles' // Bundle 名称(可选)
);
// 现在可以使用 WASM 模块的函数
console.log('WASM 模块加载成功:', instance);
return instance;
} catch (error) {
console.error('WASM 加载失败:', error);
throw error;
}
}
/**
* 示例2: 加载支持 ASM 回退的 WASM 模块
*/
async loadWasmWithFallbackExample(wasmFactory: any, asmFactory: any) {
try {
wasmLoader.initialize();
// 假设你有 WASM 和 ASM 工厂函数
// const wasmFactory = (await import('../wasm/pinball_physics.js')).default;
// const asmFactory = (await import('../wasm/pinball_physics.asm.js')).default;
const result = await wasmLoader.loadWasmWithAsmFallback(
wasmFactory, // WASM 工厂函数
asmFactory, // ASM 工厂函数
'pinball_physics.wasm', // WASM 文件名
'pinball_physics.asm.mem', // ASM 内存文件名
'44cacb3c-e901-455d-b3e1-1c38a69718e1', // WASM UUID
'3400003e-dc3c-43c1-8757-3e082429125a', // ASM UUID
'wasmFiles' // Bundle 名称
);
console.log('模块加载成功,使用的是:', result.isWasm ? 'WASM' : 'ASM');
console.log('模块实例:', result.instance);
return result;
} catch (error) {
console.error('模块加载失败:', error);
throw error;
}
}
/**
* 示例3: 检查平台支持情况
*/
checkPlatformSupport() {
const isWasmSupported = wasmLoader.isWasmSupported();
const strategy = wasmLoader.getRecommendedStrategy();
console.log('WASM 支持:', isWasmSupported);
console.log('推荐策略:', strategy);
return { isWasmSupported, strategy };
}
/**
* 示例4: 高级用法 - 自定义配置
*/
async loadCustomWasm(wasmFactory: any, asmFactory: any) {
try {
wasmLoader.initialize();
// 使用完整配置
const result = await wasmLoader.loadWasmModule({
fileName: 'pinball_physics.wasm',
bundleName: 'wasmFiles',
editorUuid: '44cacb3c-e901-455d-b3e1-1c38a69718e1',
supportAsm: true,
asmFileName: 'pinball_physics.asm.mem',
asmEditorUuid: '3400003e-dc3c-43c1-8757-3e082429125a',
wasmFactory: wasmFactory,
asmFactory: asmFactory
});
return result;
} catch (error) {
console.error('自定义 WASM 加载失败:', error);
throw error;
}
}
}
/**
* 使用说明:
*
* 1. 首先将 WASM 文件和对应的 JS 胶水代码放到项目中
* 2. 将 .wasm 文件导入到编辑器的资源管理器中
* 3. 在编辑器中获取 .wasm 文件的 UUID
* 4. 导入 JS 胶水代码并获取工厂函数
* 5. 调用 wasmLoader 的方法加载 WASM 模块
*
* 注意事项:
* - 在编辑器环境下需要提供资源的 UUID
* - 在运行时环境下需要提供 Bundle 名称和文件名
* - iOS 平台不支持 WASM需要提供 ASM 回退
* - 确保在使用前调用 initialize() 方法
*/

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "2ecfafcc-6d3c-443e-8e24-3207988f4742",
"files": [],
"subMetas": {},
"userData": {}
}