feat(physics): 集成 Rapier2D 物理引擎并修复预览重置问题 (#244)

* feat(physics): 集成 Rapier2D 物理引擎并修复预览重置问题

* fix: 修复 CI 流程并清理代码
This commit is contained in:
YHH
2025-11-28 10:32:28 +08:00
committed by GitHub
parent cabb625a17
commit 673f5e5855
56 changed files with 4934 additions and 218 deletions

View File

@@ -10,6 +10,7 @@ import { TransformComponent, SpriteComponent, SpriteAnimatorComponent, SpriteAni
import { TilemapComponent, TilemapRenderingSystem } from '@esengine/tilemap';
import { BehaviorTreeExecutionSystem } from '@esengine/behavior-tree';
import { UIRenderDataProvider, invalidateUIRenderCaches, UIInputSystem } from '@esengine/ui';
import { Physics2DSystem } from '@esengine/physics-rapier2d';
import * as esEngine from '@esengine/engine';
import {
AssetManager,
@@ -36,6 +37,7 @@ export class EngineService {
private animatorSystem: SpriteAnimatorSystem | null = null;
private tilemapSystem: TilemapRenderingSystem | null = null;
private behaviorTreeSystem: BehaviorTreeExecutionSystem | null = null;
private physicsSystem: Physics2DSystem | null = null;
private uiRenderProvider: UIRenderDataProvider | null = null;
private uiInputSystem: UIInputSystem | null = null;
private initialized = false;
@@ -244,6 +246,7 @@ export class EngineService {
this.animatorSystem = context.animatorSystem as SpriteAnimatorSystem | undefined ?? null;
this.tilemapSystem = context.tilemapSystem as TilemapRenderingSystem | undefined ?? null;
this.behaviorTreeSystem = context.behaviorTreeSystem as BehaviorTreeExecutionSystem | undefined ?? null;
this.physicsSystem = context.physicsSystem as Physics2DSystem | undefined ?? null;
this.uiRenderProvider = context.uiRenderProvider as UIRenderDataProvider | undefined ?? null;
this.uiInputSystem = context.uiInputSystem as UIInputSystem | undefined ?? null;
@@ -253,14 +256,17 @@ export class EngineService {
this.renderSystem.setUIRenderDataProvider(this.uiRenderProvider);
}
// 在编辑器模式下,动画行为树系统默认禁用
// In editor mode, animation and behavior tree systems are disabled by default
// 在编辑器模式下,动画行为树和物理系统默认禁用
// In editor mode, animation, behavior tree and physics systems are disabled by default
if (this.animatorSystem) {
this.animatorSystem.enabled = false;
}
if (this.behaviorTreeSystem) {
this.behaviorTreeSystem.enabled = false;
}
if (this.physicsSystem) {
this.physicsSystem.enabled = false;
}
this.modulesInitialized = true;
}
@@ -289,6 +295,7 @@ export class EngineService {
this.animatorSystem = null;
this.tilemapSystem = null;
this.behaviorTreeSystem = null;
this.physicsSystem = null;
this.uiRenderProvider = null;
this.uiInputSystem = null;
this.modulesInitialized = false;
@@ -390,6 +397,11 @@ export class EngineService {
if (this.behaviorTreeSystem) {
this.behaviorTreeSystem.enabled = true;
}
// Enable physics system for preview
// 启用物理系统用于预览
if (this.physicsSystem) {
this.physicsSystem.enabled = true;
}
this.startAutoPlayAnimations();
this.gameLoop();
@@ -469,6 +481,14 @@ export class EngineService {
if (this.behaviorTreeSystem) {
this.behaviorTreeSystem.enabled = false;
}
// Disable and reset physics system
// 禁用并重置物理系统
if (this.physicsSystem) {
this.physicsSystem.enabled = false;
// Reset physics world state to prepare for next preview
// 重置物理世界状态,为下次预览做准备
this.physicsSystem.reset();
}
this.stopAllAnimations();
// Note: Don't cancel animationFrameId here, as renderLoop should keep running

View File

@@ -86,12 +86,10 @@ export class RuntimeResolver {
if (await this.hasRuntimeFilesInWorkspace(workspaceRoot)) {
this.baseDir = workspaceRoot;
this.isDev = true;
console.log(`[RuntimeResolver] Using workspace dev files: ${this.baseDir}`);
} else {
// 回退到打包的资源目录(生产模式)
this.baseDir = await TauriAPI.getAppResourceDir();
this.isDev = false;
console.log(`[RuntimeResolver] Using bundled resource dir: ${this.baseDir}`);
}
}
@@ -101,9 +99,7 @@ export class RuntimeResolver {
*/
private async hasRuntimeFilesInWorkspace(workspaceRoot: string): Promise<boolean> {
const runtimePath = `${workspaceRoot}\\packages\\platform-web\\dist\\runtime.browser.js`;
const exists = await TauriAPI.pathExists(runtimePath);
console.log(`[RuntimeResolver] Checking workspace runtime: ${runtimePath} -> ${exists}`);
return exists;
return await TauriAPI.pathExists(runtimePath);
}
/**
@@ -209,9 +205,6 @@ export class RuntimeResolver {
* 生产模式:从编辑器内置资源复制
*/
async prepareRuntimeFiles(targetDir: string): Promise<void> {
console.log(`[RuntimeResolver] Preparing runtime files to: ${targetDir}`);
console.log(`[RuntimeResolver] isDev: ${this.isDev}, baseDir: ${this.baseDir}`);
// Ensure target directory exists
const dirExists = await TauriAPI.pathExists(targetDir);
if (!dirExists) {
@@ -220,16 +213,13 @@ export class RuntimeResolver {
// Copy platform-web runtime
const platformWeb = await this.getModuleFiles('platform-web');
console.log(`[RuntimeResolver] platform-web files:`, platformWeb.files);
for (const srcFile of platformWeb.files) {
const filename = srcFile.split(/[/\\]/).pop() || '';
const dstFile = `${targetDir}\\${filename}`;
const srcExists = await TauriAPI.pathExists(srcFile);
console.log(`[RuntimeResolver] Copying ${srcFile} -> ${dstFile} (src exists: ${srcExists})`);
if (srcExists) {
await TauriAPI.copyFile(srcFile, dstFile);
console.log(`[RuntimeResolver] Copied ${filename}`);
} else {
throw new Error(`Runtime file not found: ${srcFile}`);
}
@@ -237,22 +227,17 @@ export class RuntimeResolver {
// Copy engine WASM files
const engine = await this.getModuleFiles('engine');
console.log(`[RuntimeResolver] engine files:`, engine.files);
for (const srcFile of engine.files) {
const filename = srcFile.split(/[/\\]/).pop() || '';
const dstFile = `${targetDir}\\${filename}`;
const srcExists = await TauriAPI.pathExists(srcFile);
console.log(`[RuntimeResolver] Copying ${srcFile} -> ${dstFile} (src exists: ${srcExists})`);
if (srcExists) {
await TauriAPI.copyFile(srcFile, dstFile);
console.log(`[RuntimeResolver] Copied ${filename}`);
} else {
throw new Error(`Engine file not found: ${srcFile}`);
}
}
console.log(`[RuntimeResolver] Runtime files prepared successfully`);
}
/**