Files
shooter-demo/client-cocos/assets/scripts/Modules/Pinball/Physics/WasmPhysicsEngine.ts

411 lines
14 KiB
TypeScript
Raw Normal View History

2025-11-28 18:10:10 +08:00
/**
* 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, wasmFactory?: any): Promise<void> {
if (this.state === WasmModuleState.LOADED) {
return;
}
this.state = WasmModuleState.LOADING;
try {
if (wasmFactory) {
// 使用新的 WasmLoader推荐方式
await this.initializeWithWasmLoader(wasmFactory);
} else {
// 回退到旧的加载方式(保持向后兼容)
console.warn('使用旧的 WASM 加载方式,推荐提供 wasmFactory 参数使用 WasmLoader');
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 wasmPath = 'assets/wasm/pinball_physics.wasm';
const wasmResponse = await fetch(wasmPath);
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();
}
}