Files
esengine/packages/physics-rapier2d/src/PhysicsRuntimeModule.ts
YHH 995fa2d514 refactor(arch): 改进 ServiceToken 设计,统一服务获取模式 (#300)
* 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"原则,统一服务访问模式
2025-12-09 11:07:44 +08:00

208 lines
6.1 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.
/**
* 物理运行时模块
*
* 提供 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 };