Files
shooter-demo/client-cocos/assets/scripts/Modules/Pinball/Physics/WasmPhysicsEngine.ts
2025-11-29 01:42:48 +08:00

413 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* WASM 物理引擎实现
* 提供与 pinball-physics WASM 模块的接口
*/
import { wasmLoader } from '../../../Utils/WasmLoader';
import { BodyId, PhysicsBodyData, PhysicsSettings, Vector2, WorldId } from '../Core/GameData';
import { IPhysicsEngine } from '../Core/IPhysicsEngine';
import { WasmExports, WasmModuleState } from './PhysicsTypes';
export class WasmPhysicsEngine implements IPhysicsEngine {
private wasmModule: WebAssembly.Module | null = null;
private wasmInstance: WebAssembly.Instance | null = null;
private wasmExports: WasmExports | null = null;
private state: WasmModuleState = WasmModuleState.UNLOADED;
private worlds: Map<WorldId, boolean> = new Map();
private bodies: Map<WorldId, Map<BodyId, boolean>> = new Map();
/**
* 初始化 WASM 物理引擎
* @param settings 物理设置
* @param wasmFactory WASM工厂函数可选推荐使用
*/
async initialize(settings: PhysicsSettings ): Promise<void> {
if (this.state === WasmModuleState.LOADED) {
return;
}
this.state = WasmModuleState.LOADING;
try {
await this.initializeLegacy();
this.state = WasmModuleState.LOADED;
console.log('WASM Physics Engine initialized successfully');
} catch (error) {
this.state = WasmModuleState.ERROR;
console.error('Failed to initialize WASM Physics Engine:', error);
throw error;
}
}
/**
* 创建 WASM 导入对象
*/
private createImportObject(): WebAssembly.Imports {
return {
env: {
// 内存管理
memory: new WebAssembly.Memory({
initial: 10,
maximum: 100
}),
// 日志函数
console_log: (ptr: number, len: number) => {
// 可以实现 WASM 的日志输出
},
// 错误处理
abort: (msg: number, file: number, line: number, col: number) => {
console.error('WASM abort:', { msg, file, line, col });
}
}
};
}
/**
* 使用 WasmLoader 初始化(推荐方式)
*/
private async initializeWithWasmLoader(wasmFactory: any): Promise<void> {
// 初始化 WASM 加载器
wasmLoader.initialize();
// 检查平台支持
if (!wasmLoader.isWasmSupported()) {
throw new Error('当前平台不支持 WASM需要提供 ASM 回退选项');
}
// 加载 WASM 模块
// 注意:在实际使用时需要提供正确的 editorUuid
const instance = await wasmLoader.loadSimpleWasm(
wasmFactory,
'pinball_physics.wasm',
undefined, // editorUuid需要在实际使用时提供
'wasm' // bundleName
);
this.wasmExports = instance as WasmExports;
}
/**
* 旧的初始化方法(保持向后兼容)
*/
private async initializeLegacy(): Promise<void> {
// 加载 WASM 文件
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);
if (!wasmResponse.ok) {
throw new Error(`Failed to fetch WASM file: ${wasmResponse.statusText}`);
}
const wasmBytes = await wasmResponse.arrayBuffer();
// 编译 WASM 模块
this.wasmModule = await WebAssembly.compile(wasmBytes);
// 创建实例
const importObject = this.createImportObject();
this.wasmInstance = await WebAssembly.instantiate(this.wasmModule, importObject);
this.wasmExports = this.wasmInstance.exports as unknown as WasmExports;
}
/**
* 检查 WASM 是否已加载
*/
private ensureLoaded(): void {
if (this.state !== WasmModuleState.LOADED || !this.wasmExports) {
throw new Error('WASM Physics Engine not loaded. Call initialize() first.');
}
}
/**
* 创建物理世界
*/
async createWorld(gravity: Vector2): Promise<WorldId> {
this.ensureLoaded();
const worldId = this.wasmExports!.pinball_create_world(gravity.x, gravity.y);
this.worlds.set(worldId, true);
this.bodies.set(worldId, new Map());
console.log(`Created physics world ${worldId} with gravity (${gravity.x}, ${gravity.y})`);
return worldId;
}
/**
* 销毁物理世界
*/
async destroyWorld(worldId: WorldId): Promise<void> {
if (this.worlds.has(worldId)) {
this.worlds.delete(worldId);
this.bodies.delete(worldId);
console.log(`Destroyed physics world ${worldId}`);
}
}
/**
* 执行物理步进
*/
async step(worldId: WorldId): Promise<void> {
this.ensureLoaded();
if (!this.worlds.has(worldId)) {
throw new Error(`World ${worldId} does not exist`);
}
this.wasmExports!.pinball_step_world(worldId);
}
/**
* 创建动态刚体
*/
async createDynamicBody(worldId: WorldId, position: Vector2, radius: number): Promise<BodyId> {
this.ensureLoaded();
if (!this.worlds.has(worldId)) {
throw new Error(`World ${worldId} does not exist`);
}
const bodyId = this.wasmExports!.pinball_create_dynamic_body(worldId, position.x, position.y);
// 记录刚体
const worldBodies = this.bodies.get(worldId)!;
worldBodies.set(bodyId, true);
console.log(`Created dynamic body ${bodyId} at (${position.x}, ${position.y}) with radius ${radius}`);
return bodyId;
}
/**
* 创建静态刚体(暂时使用动态刚体实现)
*/
async createStaticBody(worldId: WorldId, position: Vector2, radius: number): Promise<BodyId> {
// 目前 WASM 中只有 pinball_create_dynamic_body后续可以扩展
return this.createDynamicBody(worldId, position, radius);
}
/**
* 销毁刚体(目前 WASM 中没有此函数,仅从记录中移除)
*/
async destroyBody(worldId: WorldId, bodyId: BodyId): Promise<void> {
const worldBodies = this.bodies.get(worldId);
if (worldBodies && worldBodies.has(bodyId)) {
worldBodies.delete(bodyId);
console.log(`Removed body ${bodyId} from tracking (WASM destroy not implemented yet)`);
}
}
/**
* 获取刚体位置
*/
async getBodyPosition(worldId: WorldId, bodyId: BodyId): Promise<Vector2 | null> {
this.ensureLoaded();
const worldBodies = this.bodies.get(worldId);
if (!worldBodies || !worldBodies.has(bodyId)) {
return null;
}
const x = this.wasmExports!.pinball_get_body_x(worldId, bodyId);
const y = this.wasmExports!.pinball_get_body_y(worldId, bodyId);
return { x, y };
}
/**
* 设置刚体位置(目前 WASM 中没有此函数)
*/
async setBodyPosition(worldId: WorldId, bodyId: BodyId, position: Vector2): Promise<void> {
// 目前 WASM 中没有 pinball_set_body_position 函数
console.warn('setBodyPosition not implemented in WASM yet');
}
/**
* 获取刚体速度(目前 WASM 中没有此函数)
*/
async getBodyVelocity(worldId: WorldId, bodyId: BodyId): Promise<Vector2 | null> {
// 目前 WASM 中没有速度获取函数
console.warn('getBodyVelocity not implemented in WASM yet');
return { x: 0, y: 0 };
}
/**
* 设置刚体速度(目前 WASM 中没有此函数)
*/
async setBodyVelocity(worldId: WorldId, bodyId: BodyId, velocity: Vector2): Promise<void> {
// 目前 WASM 中没有 pinball_set_body_velocity 函数
console.warn('setBodyVelocity not implemented in WASM yet');
}
/**
* 获取所有物理体数据
*/
async getAllBodies(worldId: WorldId): Promise<PhysicsBodyData[]> {
const worldBodies = this.bodies.get(worldId);
if (!worldBodies) {
return [];
}
const bodiesData: PhysicsBodyData[] = [];
for (const bodyId of worldBodies.keys()) {
const position = await this.getBodyPosition(worldId, bodyId);
const velocity = await this.getBodyVelocity(worldId, bodyId);
if (position && velocity) {
bodiesData.push({
id: bodyId,
position,
velocity,
rotation: 0,
angularVelocity: 0,
bodyType: 'circle',
radius: 0.5, // 默认半径,可以后续改进
isStatic: false // 可以根据需要区分静态和动态
});
}
}
return bodiesData;
}
/**
* 创建圆形刚体 (新接口方法)
*/
createCircle(options: import('../Core/IPhysicsEngine').CreateCircleOptions): BodyId {
this.ensureLoaded();
// 由于目前使用第一个世界获取第一个世界ID
const firstWorldId = this.worlds.keys().next().value;
if (firstWorldId === undefined) {
throw new Error('No physics world created');
}
// 目前WASM只支持动态刚体创建
const bodyId = this.wasmExports!.pinball_create_dynamic_body(firstWorldId, options.position.x, options.position.y);
this.bodies.get(firstWorldId)!.set(bodyId, true);
return bodyId;
}
/**
* 创建矩形刚体 (新接口方法)
*/
createBox(options: import('../Core/IPhysicsEngine').CreateBoxOptions): BodyId {
this.ensureLoaded();
const firstWorldId = this.worlds.keys().next().value;
if (firstWorldId === undefined) {
throw new Error('No physics world created');
}
// 目前WASM只支持动态刚体创建矩形用动态刚体模拟
const bodyId = this.wasmExports!.pinball_create_dynamic_body(firstWorldId, options.position.x, options.position.y);
this.bodies.get(firstWorldId)!.set(bodyId, true);
return bodyId;
}
/**
* 移除刚体 (新接口方法)
*/
removeBody(bodyId: BodyId): void {
// 查找包含此bodyId的世界
for (const [worldId, worldBodies] of this.bodies) {
if (worldBodies.has(bodyId)) {
worldBodies.delete(bodyId);
// 注意这里需要WASM支持删除函数
// this.wasmExports!.pinball_remove_body(worldId, bodyId);
console.log(`Body ${bodyId} removed from world ${worldId}`);
return;
}
}
}
/**
* 获取刚体数据 (新接口方法)
*/
getBodyData(bodyId: BodyId): PhysicsBodyData | null {
// 查找包含此bodyId的世界
for (const [worldId, worldBodies] of this.bodies) {
if (worldBodies.has(bodyId)) {
// 这里需要同步调用WASM函数获取数据
try {
const x = this.wasmExports!.pinball_get_body_x(worldId, bodyId);
const y = this.wasmExports!.pinball_get_body_y(worldId, bodyId);
// WASM暂时不支持速度获取使用默认值
const vx = 0;
const vy = 0;
return {
id: bodyId,
position: { x, y },
velocity: { x: vx, y: vy },
rotation: 0, // 暂时固定值
angularVelocity: 0, // 暂时固定值
bodyType: 'circle',
radius: 0.5, // 暂时固定值
isStatic: false // 暂时固定值
};
} catch (error) {
console.error(`Failed to get body data for body ${bodyId}:`, error);
return null;
}
}
}
return null;
}
/**
* 清理资源 (新接口方法)
*/
cleanup(): void {
this.dispose(); // 异步转同步
}
/**
* 清理资源
*/
async dispose(): Promise<void> {
this.worlds.clear();
this.bodies.clear();
this.wasmInstance = null;
this.wasmExports = null;
this.wasmModule = null;
this.state = WasmModuleState.UNLOADED;
console.log('WASM Physics Engine disposed');
}
/**
* 获取当前状态
*/
getState(): WasmModuleState {
return this.state;
}
/**
* 检查平台是否支持WASM使用WasmLoader
*/
isWasmSupported(): boolean {
return wasmLoader.isWasmSupported();
}
/**
* 获取推荐的加载策略
*/
getRecommendedStrategy(): 'wasm' | 'asm' | 'unsupported' {
return wasmLoader.getRecommendedStrategy();
}
}