cocos基础工程
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 物理引擎相关类型定义
|
||||
*/
|
||||
|
||||
import { Vector2 } from '../Core/GameData';
|
||||
|
||||
/** WASM 函数签名定义 */
|
||||
export interface WasmExports {
|
||||
// 世界管理
|
||||
pinball_create_world(gravity_x: number, gravity_y: number): number;
|
||||
pinball_step_world(world_id: number): void;
|
||||
|
||||
// 刚体管理
|
||||
pinball_create_dynamic_body(world_id: number, x: number, y: number): number;
|
||||
|
||||
// 位置获取
|
||||
pinball_get_body_x(world_id: number, body_id: number): number;
|
||||
pinball_get_body_y(world_id: number, body_id: number): number;
|
||||
|
||||
// 内存管理
|
||||
memory: WebAssembly.Memory;
|
||||
}
|
||||
|
||||
/** WASM 模块状态 */
|
||||
export enum WasmModuleState {
|
||||
UNLOADED = 'unloaded',
|
||||
LOADING = 'loading',
|
||||
LOADED = 'loaded',
|
||||
ERROR = 'error'
|
||||
}
|
||||
|
||||
/** WASM 配置 */
|
||||
export interface WasmConfig {
|
||||
wasmPath: string;
|
||||
memoryPages?: number;
|
||||
importObject?: WebAssembly.Imports;
|
||||
}
|
||||
|
||||
/** 物理世界配置 */
|
||||
export interface PhysicsWorldConfig {
|
||||
gravity: Vector2;
|
||||
timeStep: number;
|
||||
velocityIterations: number;
|
||||
positionIterations: number;
|
||||
}
|
||||
|
||||
/** 碰撞体类型 */
|
||||
export enum ColliderType {
|
||||
CIRCLE = 'circle',
|
||||
BOX = 'box'
|
||||
}
|
||||
|
||||
/** 碰撞体定义 */
|
||||
export interface ColliderDef {
|
||||
type: ColliderType;
|
||||
radius?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
offset?: Vector2;
|
||||
}
|
||||
|
||||
/** 刚体类型 */
|
||||
export enum BodyType {
|
||||
STATIC = 'static',
|
||||
DYNAMIC = 'dynamic',
|
||||
KINEMATIC = 'kinematic'
|
||||
}
|
||||
|
||||
/** 刚体定义 */
|
||||
export interface BodyDef {
|
||||
type: BodyType;
|
||||
position: Vector2;
|
||||
velocity?: Vector2;
|
||||
colliders: ColliderDef[];
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "6a80be32-a5d9-4b97-ace6-ac2c417a91b3",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,411 @@
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user