Merge remote master

This commit is contained in:
yhh
2025-12-30 12:29:24 +08:00
20 changed files with 190 additions and 109 deletions
+2 -10
View File
@@ -24,15 +24,7 @@ cd esengine
pnpm install
```
### 2. Clone Physics Dependencies (Optional)
If you need physics support, clone the rapier.js dependency:
```bash
git clone https://github.com/esengine/rapier.js.git packages/thirdparty/rapier.js
```
### 3. Build Dependencies
### 2. Build Dependencies
From the project root:
@@ -40,7 +32,7 @@ From the project root:
pnpm build:editor
```
### 4. Run Editor
### 3. Run Editor
```bash
cd packages/editor/editor-app
+2 -10
View File
@@ -24,15 +24,7 @@ cd esengine
pnpm install
```
### 2. 克隆物理依赖(可选)
如果需要物理引擎支持,需要克隆 rapier.js 依赖:
```bash
git clone https://github.com/esengine/rapier.js.git packages/thirdparty/rapier.js
```
### 3. 构建依赖
### 2. 构建依赖
在项目根目录执行:
@@ -40,7 +32,7 @@ git clone https://github.com/esengine/rapier.js.git packages/thirdparty/rapier.j
pnpm build:editor
```
### 4. 启动编辑器
### 3. 启动编辑器
```bash
cd packages/editor/editor-app
@@ -30,6 +30,7 @@
"devDependencies": {
"@esengine/ecs-framework": "workspace:*",
"@esengine/engine-core": "workspace:*",
"@esengine/asset-system": "workspace:*",
"@esengine/editor-core": "workspace:*",
"@esengine/editor-runtime": "workspace:*",
"@esengine/node-editor": "workspace:*",
@@ -0,0 +1,45 @@
/**
* @zh ESEngine 行为树运行时模块
* @en ESEngine Behavior Tree Runtime Module
*
* @zh 纯运行时模块,不依赖 asset-system。资产加载由编辑器在 install 时注册。
* @en Pure runtime module, no asset-system dependency. Asset loading is registered by editor during install.
*/
import type { IScene, ServiceContainer, IComponentRegistry } from '@esengine/ecs-framework';
import type { IRuntimeModule, SystemContext } from '@esengine/engine-core';
import {
BehaviorTreeRuntimeComponent,
BehaviorTreeExecutionSystem,
BehaviorTreeAssetManager,
GlobalBlackboardService,
BehaviorTreeSystemToken
} from '@esengine/behavior-tree';
export class BehaviorTreeRuntimeModule implements IRuntimeModule {
registerComponents(registry: IComponentRegistry): void {
registry.register(BehaviorTreeRuntimeComponent);
}
registerServices(services: ServiceContainer): void {
if (!services.isRegistered(GlobalBlackboardService)) {
services.registerSingleton(GlobalBlackboardService);
}
if (!services.isRegistered(BehaviorTreeAssetManager)) {
services.registerSingleton(BehaviorTreeAssetManager);
}
}
createSystems(scene: IScene, context: SystemContext): void {
const ecsServices = (context as { ecsServices?: ServiceContainer }).ecsServices;
const behaviorTreeSystem = new BehaviorTreeExecutionSystem(ecsServices);
if (context.isEditor) {
behaviorTreeSystem.enabled = false;
}
scene.addSystem(behaviorTreeSystem);
context.services.register(BehaviorTreeSystemToken, behaviorTreeSystem);
}
}
@@ -30,8 +30,11 @@ import {
LocaleService,
} from '@esengine/editor-runtime';
// Runtime imports from @esengine/behavior-tree package
import { BehaviorTreeRuntimeComponent, BehaviorTreeRuntimeModule } from '@esengine/behavior-tree';
// Runtime imports
import { BehaviorTreeRuntimeComponent, BehaviorTreeAssetType } from '@esengine/behavior-tree';
import { AssetManagerToken } from '@esengine/asset-system';
import { BehaviorTreeRuntimeModule } from './BehaviorTreeRuntimeModule';
import { BehaviorTreeLoader } from './runtime/BehaviorTreeLoader';
// Editor components and services
import { BehaviorTreeService } from './services/BehaviorTreeService';
@@ -71,6 +74,10 @@ export class BehaviorTreeEditorModule implements IEditorModuleLoader {
// 设置插件上下文
PluginContext.setServices(services);
// 注册行为树资产加载器到 AssetManager
// Register behavior tree asset loader to AssetManager
this.registerAssetLoader();
// 注册服务
this.registerServices(services);
@@ -92,6 +99,22 @@ export class BehaviorTreeEditorModule implements IEditorModuleLoader {
logger.info('BehaviorTree editor module installed');
}
/**
* 注册行为树资产加载器
* Register behavior tree asset loader
*/
private registerAssetLoader(): void {
try {
const assetManager = PluginAPI.resolve(AssetManagerToken);
if (assetManager) {
assetManager.registerLoader(BehaviorTreeAssetType, new BehaviorTreeLoader());
logger.info('BehaviorTree asset loader registered');
}
} catch (error) {
logger.warn('Failed to register asset loader:', error);
}
}
private registerAssetCreationMappings(services: ServiceContainer): void {
try {
const fileActionRegistry = services.resolve<FileActionRegistry>(IFileActionRegistry);
@@ -376,7 +399,7 @@ export const BehaviorTreePlugin: IEditorPlugin = {
editorModule: new BehaviorTreeEditorModule(),
};
export { BehaviorTreeRuntimeModule };
// BehaviorTreeRuntimeModule is internal, not re-exported
// Re-exports for editor functionality
export { PluginContext } from './PluginContext';
@@ -0,0 +1,61 @@
/**
* @zh ESEngine 资产加载器
* @en ESEngine asset loader
* @internal
*/
import { Core } from '@esengine/ecs-framework';
import {
BehaviorTreeAssetManager,
EditorToBehaviorTreeDataConverter,
BehaviorTreeAssetType,
type BehaviorTreeData
} from '@esengine/behavior-tree';
/**
* @zh 行为树资产接口
* @en Behavior tree asset interface
* @internal
*/
export interface IBehaviorTreeAsset {
data: BehaviorTreeData;
path: string;
}
/**
* @zh 行为树加载器
* @en Behavior tree loader implementing IAssetLoader interface
* @internal
*/
export class BehaviorTreeLoader {
readonly supportedType = BehaviorTreeAssetType;
readonly supportedExtensions = ['.btree'];
readonly contentType = 'text' as const;
async parse(content: { text?: string }, context: { metadata: { path: string } }): Promise<IBehaviorTreeAsset> {
if (!content.text) {
throw new Error('Behavior tree content is empty');
}
const treeData = EditorToBehaviorTreeDataConverter.fromEditorJSON(content.text);
const assetPath = context.metadata.path;
treeData.id = assetPath;
const btAssetManager = Core.services.tryResolve(BehaviorTreeAssetManager);
if (btAssetManager) {
btAssetManager.loadAsset(treeData);
}
return {
data: treeData,
path: assetPath
};
}
dispose(asset: IBehaviorTreeAsset): void {
const btAssetManager = Core.services.tryResolve(BehaviorTreeAssetManager);
if (btAssetManager && asset.data) {
btAssetManager.unloadAsset(asset.data.id);
}
}
}
@@ -1,23 +1,15 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"moduleResolution": "bundler",
"lib": ["ES2020", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"composite": false,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"jsx": "react-jsx",
"resolveJsonModule": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
"skipLibCheck": true,
"moduleResolution": "bundler"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
@@ -2,6 +2,8 @@ import { defineConfig } from 'tsup';
import { editorOnlyPreset } from '../../../tools/build-config/src/presets/plugin-tsup';
export default defineConfig({
...editorOnlyPreset(),
...editorOnlyPreset({
external: ['@esengine/asset-system']
}),
tsconfig: 'tsconfig.build.json'
});
@@ -0,0 +1,13 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"composite": false,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"jsx": "react-jsx"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
@@ -1,6 +1,7 @@
{
"extends": "../build-config/tsconfig.json",
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"outDir": "./dist",
"rootDir": "./src",
"jsx": "react-jsx",
@@ -5,6 +5,7 @@ export default defineConfig({
format: ['esm'],
dts: true,
clean: true,
tsconfig: 'tsconfig.build.json',
external: [
'react',
'react-dom',
@@ -121,9 +121,9 @@ export class WeChatRapier2DLoader implements IWasmLibraryLoader<RapierModule> {
// 导入 Rapier2D 标准版
const RAPIER = await import('@esengine/rapier2d');
// 初始化 WASM - 标准版需要提供 WASM 路径
const wasmPath = this._config.minigame?.wasmPath || 'wasm/rapier_wasm2d_bg.wasm';
await RAPIER.init(wasmPath);
// 初始化 WASM - WASM 已经作为 base64 嵌入到包中
// Initialize WASM - WASM is embedded as base64 in the package
await RAPIER.init();
return RAPIER;
} finally {
@@ -53,10 +53,9 @@ export class WebRapier2DLoader implements IWasmLibraryLoader<RapierModule> {
// 动态导入标准版
const RAPIER = await import('@esengine/rapier2d');
// 初始化 WASM - 标准版需要提供 WASM 路径
// 构建时 WASM 文件会被复制到 wasm/ 目录
const wasmPath = this._config.web?.wasmPath || 'wasm/rapier_wasm2d_bg.wasm';
await RAPIER.init(wasmPath);
// 初始化 WASM - WASM 已经作为 base64 嵌入到包中
// Initialize WASM - WASM is embedded as base64 in the package
await RAPIER.init();
console.log(`[${this._config.name}] 加载完成`);
return RAPIER;
+4 -2
View File
@@ -19,11 +19,13 @@
],
"scripts": {
"gen:src": "node scripts/gen-src.mjs",
"build": "pnpm gen:src && tsup",
"clean": "rimraf dist src"
"build": "tsup",
"build:regen": "pnpm gen:src && tsup",
"clean": "rimraf dist"
},
"license": "Apache-2.0",
"devDependencies": {
"base64-js": "^1.5.1",
"rimraf": "^5.0.0",
"tsup": "^8.0.0",
"typescript": "^5.0.0"
@@ -91,9 +91,8 @@ export class KinematicCharacterController {
*/
public setUp(vector: Vector) {
let rawVect = VectorOps.intoRaw(vector);
const result = this.raw.setUp(rawVect);
return this.raw.setUp(rawVect);
rawVect.free();
return result;
}
public applyImpulsesToDynamicBodies(): boolean {
@@ -28,9 +28,6 @@ export class DynamicRayCastVehicleController {
bodies: RigidBodySet,
colliders: ColliderSet,
) {
if (typeof RawDynamicRayCastVehicleController === 'undefined') {
throw new Error('DynamicRayCastVehicleController is not available in 2D mode');
}
this.raw = new RawDynamicRayCastVehicleController(chassis.handle);
this.broadPhase = broadPhase;
this.narrowPhase = narrowPhase;
+5 -53
View File
@@ -1,60 +1,12 @@
/**
* RAPIER initialization module with dynamic WASM loading support.
* RAPIER 初始化模块,支持动态 WASM 加载。
*/
// @ts-ignore
import wasmBase64 from "../pkg/rapier_wasm2d_bg.wasm";
import wasmInit from "../pkg/rapier_wasm2d";
/**
* Input types for WASM initialization.
* WASM 初始化的输入类型。
*/
export type InitInput =
| RequestInfo // URL string or Request object
| URL // URL object
| Response // Fetch Response object
| BufferSource // ArrayBuffer or TypedArray
| WebAssembly.Module; // Pre-compiled module
let initialized = false;
import base64 from "base64-js";
/**
* Initializes RAPIER.
* Has to be called and awaited before using any library methods.
*
* 初始化 RAPIER。
* 必须在使用任何库方法之前调用并等待。
*
* @param input - WASM source (required). Can be URL, Response, ArrayBuffer, etc.
* WASM 源(必需)。可以是 URL、Response、ArrayBuffer 等。
*
* @example
* // Load from URL | 从 URL 加载
* await RAPIER.init('wasm/rapier_wasm2d_bg.wasm');
*
* @example
* // Load from fetch response | 从 fetch 响应加载
* const response = await fetch('wasm/rapier_wasm2d_bg.wasm');
* await RAPIER.init(response);
*
* @example
* // Load from ArrayBuffer | 从 ArrayBuffer 加载
* const buffer = await fetch('wasm/rapier_wasm2d_bg.wasm').then(r => r.arrayBuffer());
* await RAPIER.init(buffer);
*/
export async function init(input?: InitInput): Promise<void> {
if (initialized) {
return;
}
await wasmInit(input);
initialized = true;
}
/**
* Check if RAPIER is already initialized.
* 检查 RAPIER 是否已初始化。
*/
export function isInitialized(): boolean {
return initialized;
export async function init() {
await wasmInit(base64.toByteArray(wasmBase64 as unknown as string).buffer);
}
+2 -2
View File
@@ -28,7 +28,7 @@ export class VectorOps {
}
// FIXME: type ram: RawVector?
public static fromRaw(raw: RawVector): Vector | null {
public static fromRaw(raw: RawVector): Vector {
if (!raw) return null;
let res = VectorOps.new(raw.x, raw.y);
@@ -56,7 +56,7 @@ export class RotationOps {
return 0.0;
}
public static fromRaw(raw: RawRotation): Rotation | null {
public static fromRaw(raw: RawRotation): Rotation {
if (!raw) return null;
let res = raw.angle;
+3
View File
@@ -7,4 +7,7 @@ export default defineConfig({
sourcemap: true,
clean: true,
external: ["../pkg/rapier_wasm2d.js"],
loader: {
".wasm": "base64",
},
});
+6
View File
@@ -569,6 +569,9 @@ importers:
specifier: workspace:*
version: link:../../../framework/behavior-tree
devDependencies:
'@esengine/asset-system':
specifier: workspace:*
version: link:../../../engine/asset-system
'@esengine/build-config':
specifier: workspace:*
version: link:../../../tools/build-config
@@ -1861,6 +1864,9 @@ importers:
packages/physics/rapier2d:
devDependencies:
base64-js:
specifier: ^1.5.1
version: 1.5.1
rimraf:
specifier: ^5.0.0
version: 5.0.10