* refactor(arch): 移除全局变量,使用 ServiceToken 模式 - 创建 PluginServiceRegistry 类,提供类型安全的服务注册/获取 - 添加 ProfilerServiceToken 和 CollisionLayerConfigToken - 重构所有 __PROFILER_SERVICE__ 全局变量访问为 getProfilerService() - 重构 __PHYSICS_RAPIER2D__ 全局变量访问为 CollisionLayerConfigToken - 在 Core 类添加 pluginServices 静态属性 - 添加 getService.ts 辅助模块简化服务获取 这是 ServiceToken 模式重构的第一阶段,移除了最常用的两个全局变量。 后续可继续应用到其他模块(Camera/Audio 等)。 * refactor(arch): 改进 ServiceToken 设计,移除重复常量 - tokens.ts: 从 engine-core 导入 createServiceToken(符合规范) - tokens.ts: Token 使用接口 IProfilerService 而非具体类 - 移除 AssetPickerDialog 和 ContentBrowser 中重复的 MANAGED_ASSET_DIRECTORIES - 统一从 editor-core 导入 MANAGED_ASSET_DIRECTORIES * fix(type): 修复 IProfilerService 接口与实现类型不匹配 - 将 ProfilerData 等数据类型移到 tokens.ts 以避免循环依赖 - ProfilerService 显式实现 IProfilerService 接口 - 更新使用方使用 IProfilerService 接口类型而非具体类 * refactor(type): 移除类型重导出,改进类型安全 - 删除 ProfilerService.ts 中的类型重导出,消费方直接从 tokens.ts 导入 - PanelDescriptor 接口添加 titleZh 属性,移除 App.tsx 中的 as any - 改进 useDynamicIcon.ts 的类型安全,使用正确的 Record 类型 * refactor(arch): 为模块添加 ServiceToken 支持 - Material System: 创建 tokens.ts,定义 IMaterialManager 接口和 MaterialManagerToken - Audio: 创建预留 tokens.ts 文件,为未来 AudioManager 服务扩展做准备 - Camera: 创建预留 tokens.ts 文件,为未来 CameraManager 服务扩展做准备 遵循"谁定义接口,谁导出 Token"原则,统一服务访问模式
208 lines
6.1 KiB
TypeScript
208 lines
6.1 KiB
TypeScript
/**
|
||
* 物理运行时模块
|
||
*
|
||
* 提供 Rapier2D 物理引擎的 ECS 集成
|
||
*/
|
||
|
||
import type { IScene, ServiceContainer } from '@esengine/ecs-framework';
|
||
import { ComponentRegistry } from '@esengine/ecs-framework';
|
||
import type { IRuntimeModule, IPlugin, ModuleManifest, SystemContext } from '@esengine/engine-core';
|
||
import { WasmLibraryLoaderFactory } from '@esengine/platform-common';
|
||
import type * as RAPIER from '@esengine/rapier2d';
|
||
|
||
import { Rigidbody2DComponent } from './components/Rigidbody2DComponent';
|
||
import { BoxCollider2DComponent } from './components/BoxCollider2DComponent';
|
||
import { CircleCollider2DComponent } from './components/CircleCollider2DComponent';
|
||
import { CapsuleCollider2DComponent } from './components/CapsuleCollider2DComponent';
|
||
import { PolygonCollider2DComponent } from './components/PolygonCollider2DComponent';
|
||
import { Physics2DSystem } from './systems/Physics2DSystem';
|
||
import { Physics2DService } from './services/Physics2DService';
|
||
import {
|
||
Physics2DQueryToken,
|
||
Physics2DSystemToken,
|
||
Physics2DWorldToken,
|
||
PhysicsConfigToken,
|
||
CollisionLayerConfigToken,
|
||
type IPhysics2DQuery,
|
||
type PhysicsConfig
|
||
} from './tokens';
|
||
import { CollisionLayerConfig } from './services/CollisionLayerConfig';
|
||
|
||
// 注册 Rapier2D 加载器
|
||
import './loaders';
|
||
|
||
// 重新导出 tokens 和类型 | Re-export tokens and types
|
||
export {
|
||
Physics2DQueryToken,
|
||
Physics2DSystemToken,
|
||
Physics2DWorldToken,
|
||
PhysicsConfigToken,
|
||
CollisionLayerConfigToken,
|
||
type IPhysics2DQuery,
|
||
type PhysicsConfig
|
||
} from './tokens';
|
||
|
||
/**
|
||
* 物理运行时模块
|
||
*
|
||
* 负责:
|
||
* 1. 加载并初始化 Rapier2D WASM 模块(跨平台)
|
||
* 2. 注册物理组件
|
||
* 3. 注册物理服务
|
||
* 4. 创建物理系统
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 作为插件使用
|
||
* runtimePluginManager.register(PhysicsPlugin);
|
||
* runtimePluginManager.enable('@esengine/physics-rapier2d');
|
||
*
|
||
* // 插件会自动:
|
||
* // 1. 检测平台并选择合适的加载器
|
||
* // 2. 安装必要的 polyfills(如微信小游戏的 TextDecoder)
|
||
* // 3. 加载 Rapier2D WASM 模块
|
||
* // 4. 注册物理组件和系统
|
||
* ```
|
||
*/
|
||
class PhysicsRuntimeModule implements IRuntimeModule {
|
||
private _rapierModule: typeof RAPIER | null = null;
|
||
private _physicsSystem: Physics2DSystem | null = null;
|
||
|
||
/**
|
||
* 初始化物理模块
|
||
*
|
||
* 使用平台适配的加载器加载 Rapier2D
|
||
*/
|
||
async onInitialize(): Promise<void> {
|
||
// 使用工厂创建平台对应的加载器
|
||
const loader = WasmLibraryLoaderFactory.createLoader<typeof RAPIER>('rapier2d');
|
||
|
||
// 获取平台信息
|
||
const platformInfo = loader.getPlatformInfo();
|
||
console.log(`[Physics] 平台: ${platformInfo.type}`);
|
||
console.log(`[Physics] WASM 支持: ${platformInfo.supportsWasm}`);
|
||
|
||
if (platformInfo.needsPolyfills.length > 0) {
|
||
console.log(`[Physics] 需要 Polyfills: ${platformInfo.needsPolyfills.join(', ')}`);
|
||
}
|
||
|
||
// 检查平台支持
|
||
if (!loader.isSupported()) {
|
||
throw new Error(
|
||
`[Physics] 当前平台不支持 Rapier2D: ${platformInfo.type}。` +
|
||
'请检查 WebAssembly 支持情况。'
|
||
);
|
||
}
|
||
|
||
// 加载 Rapier2D
|
||
this._rapierModule = await loader.load();
|
||
console.log('[Physics] Rapier2D 加载完成');
|
||
}
|
||
|
||
/**
|
||
* 注册物理组件
|
||
*
|
||
* @param registry - 组件注册表
|
||
*/
|
||
registerComponents(registry: typeof ComponentRegistry): void {
|
||
registry.register(Rigidbody2DComponent);
|
||
registry.register(BoxCollider2DComponent);
|
||
registry.register(CircleCollider2DComponent);
|
||
registry.register(CapsuleCollider2DComponent);
|
||
registry.register(PolygonCollider2DComponent);
|
||
}
|
||
|
||
/**
|
||
* 注册物理服务
|
||
*
|
||
* @param services - 服务容器
|
||
*/
|
||
registerServices(services: ServiceContainer): void {
|
||
services.registerSingleton(Physics2DService);
|
||
}
|
||
|
||
/**
|
||
* 创建物理系统
|
||
*
|
||
* @param scene - 目标场景
|
||
* @param context - 系统上下文
|
||
*/
|
||
createSystems(scene: IScene, context: SystemContext): void {
|
||
// 从服务注册表获取配置 | Get config from service registry
|
||
const physicsConfig = context.services.get(PhysicsConfigToken);
|
||
|
||
const physicsSystem = new Physics2DSystem({
|
||
physics: physicsConfig,
|
||
updateOrder: -1000
|
||
});
|
||
|
||
scene.addSystem(physicsSystem);
|
||
this._physicsSystem = physicsSystem;
|
||
|
||
if (this._rapierModule) {
|
||
physicsSystem.initializeWithRapier(this._rapierModule);
|
||
}
|
||
|
||
// 注册服务 | Register services
|
||
context.services.register(Physics2DSystemToken, physicsSystem);
|
||
context.services.register(Physics2DWorldToken, physicsSystem.world);
|
||
context.services.register(Physics2DQueryToken, physicsSystem);
|
||
context.services.register(CollisionLayerConfigToken, CollisionLayerConfig.getInstance());
|
||
}
|
||
|
||
/**
|
||
* 销毁物理模块
|
||
*/
|
||
onDestroy(): void {
|
||
this._physicsSystem = null;
|
||
this._rapierModule = null;
|
||
}
|
||
|
||
/**
|
||
* 获取 Rapier 模块
|
||
*
|
||
* @returns Rapier 模块,如果未加载则返回 null
|
||
*/
|
||
getRapierModule(): typeof RAPIER | null {
|
||
return this._rapierModule;
|
||
}
|
||
|
||
/**
|
||
* 获取物理系统
|
||
*
|
||
* @returns 物理系统,如果未创建则返回 null
|
||
*/
|
||
getPhysicsSystem(): Physics2DSystem | null {
|
||
return this._physicsSystem;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 模块清单
|
||
*/
|
||
const manifest: ModuleManifest = {
|
||
id: 'physics-rapier2d',
|
||
name: '@esengine/physics-rapier2d',
|
||
displayName: 'Physics 2D (Rapier)',
|
||
version: '1.0.0',
|
||
description: '基于 Rapier2D 的确定性 2D 物理引擎(支持跨平台)',
|
||
category: 'Physics',
|
||
icon: 'Atom',
|
||
isCore: false,
|
||
defaultEnabled: false,
|
||
isEngineModule: true,
|
||
dependencies: ['core', 'math'],
|
||
exports: { components: ['RigidBody2D'] },
|
||
requiresWasm: true
|
||
};
|
||
|
||
/**
|
||
* 物理插件
|
||
*/
|
||
export const PhysicsPlugin: IPlugin = {
|
||
manifest,
|
||
runtimeModule: new PhysicsRuntimeModule()
|
||
};
|
||
|
||
export { PhysicsRuntimeModule };
|