feat: 预制体系统与架构改进 (#303)

* feat(prefab): 实现预制体系统和编辑器 UX 改进

## 预制体系统
- 新增 PrefabSerializer: 预制体序列化/反序列化
- 新增 PrefabInstanceComponent: 追踪预制体实例来源和修改
- 新增 PrefabService: 预制体核心服务
- 新增 PrefabLoader: 预制体资产加载器
- 新增预制体命令: Create/Instantiate/Apply/Revert/BreakLink

## 预制体编辑模式
- 支持双击 .prefab 文件进入编辑模式
- 预制体编辑模式工具栏 (保存/退出)
- 预制体实例指示器和操作菜单

## 编辑器 UX 改进
- SceneHierarchy 快捷键: F2 重命名, Ctrl+D 复制, ↑↓ 导航
- 支持双击实体名称内联编辑
- 删除实体时显示子节点数量警告
- 右键菜单添加重命名/复制选项及快捷键提示
- 布局持久化和重置功能

## Bug 修复
- 修复 editor-runtime 组件类重复导致的 TransformComponent 不识别问题
- 修复 .prefab-name 样式覆盖导致预制体工具栏文字不可见
- 修复 Inspector 资源字段高度不正确问题

* feat(editor): 改进编辑器 UX 交互体验

- ContentBrowser: 加载动画 spinner、搜索高亮、改进空状态设计
- SceneHierarchy: 选中项自动滚动到视图、搜索清除按钮
- PropertyInspector: 输入框本地状态管理、Enter/Escape 键处理
- EntityInspector: 组件折叠状态持久化、属性搜索清除按钮
- Viewport: 变换操作实时数值显示
- 国际化: 添加相关文本 (en/zh)

* fix(build): 修复 Web 构建资产加载和编辑器 UX 改进

构建系统修复:
- 修复 asset-catalog.json 字段名不匹配 (entries vs assets)
- 修复 BrowserFileSystemService 支持两种目录格式
- 修复 bundle 策略检测逻辑 (空对象判断)
- 修复 module.json 中 assetExtensions 声明和类型推断

行为树修复:
- 修复 BehaviorTreeExecutionSystem 使用 loadAsset 替代 loadAssetByPath
- 修复 BehaviorTreeAssetType 常量与 module.json 类型名一致 (behavior-tree)

编辑器 UX 改进:
- 构建完成对话框添加"打开文件夹"按钮
- 构建完成对话框样式优化 (圆形图标背景、按钮布局)
- SceneHierarchy 响应式布局 (窄窗口自动隐藏 Type 列)
- SceneHierarchy 隐藏滚动条

错误追踪:
- 添加全局错误处理器写入日志文件 (%TEMP%/esengine-editor-crash.log)
- 添加 append_to_log Tauri 命令

* feat(render): 修复 UI 渲染和点击特效系统

## UI 渲染修复
- 修复 GUID 验证 bug,使用统一的 isValidGUID() 函数
- 修复 UI 渲染顺序随机问题,Rust 端使用 IndexMap 替代 HashMap
- Web 运行时添加 assetPathResolver 支持 GUID 解析
- UIInteractableComponent.blockEvents 默认值改为 false

## 点击特效系统
- 新增 ClickFxComponent 和 ClickFxSystem
- 支持在点击位置播放粒子效果
- 支持多种触发模式和粒子轮换

## Camera 系统重构
- CameraSystem 从 ecs-engine-bindgen 移至 camera 包
- 新增 CameraManager 统一管理相机

## 编辑器改进
- 改进属性面板 UI 交互
- 粒子编辑器面板优化
- Transform 命令系统

* feat(render): 实现 Sorting Layer 系统和 Overlay 渲染层

- 新增 SortingLayerManager 管理排序层级 (Background, Default, Foreground, UI, Overlay)
- 实现 ISortable 接口,统一 Sprite、UI、Particle 的排序属性
- 修复粒子 Overlay 层被 UI 遮挡问题:添加独立的 Overlay Pass 在 UI 之后渲染
- 更新粒子资产格式:从 sortingOrder 改为 sortingLayer + orderInLayer
- 更新粒子编辑器面板支持新的排序属性
- 优化 UI 渲染系统使用新的排序层级

* feat(ci): 集成 SignPath 代码签名服务

- 添加 SignPath 自动签名工作流(Windows)
- 配置 release-editor.yml 支持代码签名
- 将构建改为草稿模式,等待签名完成后发布
- 添加证书文件到 .gitignore 防止泄露

* fix(asset): 修复 Web 构建资产路径解析和全局单例移除

## 资产路径修复
- 修复 Tauri 本地服务器 `/asset?path=...` 路径解析,正确与 root 目录连接
- BrowserPathResolver 支持两种模式:
  - 'proxy': 使用 /asset?path=... 格式(编辑器 Run in Browser)
  - 'direct': 使用直接路径 /assets/path.png(独立 Web 构建)
- BrowserRuntime 使用 'direct' 模式,无需 Tauri 代理

## 架构改进 - 移除全局单例
- 移除 globalAssetManager 导出,改用 AssetManagerToken 依赖注入
- 移除 globalPathResolver 导出,改用 PathResolutionService
- 移除 globalPathResolutionService 导出
- ParticleUpdateSystem/ClickFxSystem 通过 setAssetManager() 注入依赖
- EngineService 使用 new AssetManager() 替代全局实例

## 新增服务
- PathResolutionService: 统一路径解析接口
- RuntimeModeService: 运行时模式查询服务
- SerializationContext: EntityRef 序列化上下文

## 其他改进
- 完善 ServiceToken 注释说明本地定义的意图
- 导出 BrowserPathResolveMode 类型

* fix(build): 添加 world-streaming composite 设置修复类型检查

* fix(build): 移除 world-streaming 引用避免 composite 冲突

* fix(build): 将 const enum 改为 enum 兼容 isolatedModules

* fix(build): 添加缺失的 IAssetManager 导入
This commit is contained in:
YHH
2025-12-13 19:44:08 +08:00
committed by GitHub
parent a716d8006c
commit beaa1d09de
258 changed files with 17725 additions and 3030 deletions

View File

@@ -1,6 +1,7 @@
{
"id": "physics-rapier2d",
"name": "@esengine/physics-rapier2d",
"globalKey": "physicsRapier2d",
"displayName": "Physics 2D (Rapier)",
"description": "2D physics using Rapier engine | 使用 Rapier 引擎的 2D 物理",
"version": "1.0.0",

View File

@@ -46,8 +46,8 @@
},
"devDependencies": {
"@esengine/ecs-framework": "workspace:*",
"@esengine/ecs-framework-math": "workspace:*",
"@esengine/engine-core": "workspace:*",
"@esengine/editor-core": "workspace:*",
"@esengine/build-config": "workspace:*",
"rimraf": "^5.0.0",
"tsup": "^8.0.0",

View File

@@ -8,7 +8,7 @@
* 运行时使用 PhysicsPlugin from '@esengine/physics-rapier2d/runtime'
*/
import type { IPlugin, ModuleManifest } from '@esengine/engine-core';
import type { IRuntimePlugin, ModuleManifest } from '@esengine/engine-core';
const manifest: ModuleManifest = {
id: '@esengine/physics-rapier2d',
@@ -35,7 +35,7 @@ const manifest: ModuleManifest = {
*
* 编辑器使用此版本注册插件,运行时使用带 WASM 的完整版本。
*/
export const Physics2DPlugin: IPlugin = {
export const Physics2DPlugin: IRuntimePlugin = {
manifest
// No runtime module - editor doesn't need physics simulation
};

View File

@@ -6,7 +6,7 @@
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 type { IRuntimeModule, IRuntimePlugin, ModuleManifest, SystemContext } from '@esengine/engine-core';
import { WasmLibraryLoaderFactory } from '@esengine/platform-common';
import type * as RAPIER from '@esengine/rapier2d';
@@ -199,7 +199,7 @@ const manifest: ModuleManifest = {
/**
* 物理插件
*/
export const PhysicsPlugin: IPlugin = {
export const PhysicsPlugin: IRuntimePlugin = {
manifest,
runtimeModule: new PhysicsRuntimeModule()
};

View File

@@ -4,8 +4,8 @@
*/
import { Property, Serialize, Serializable, ECSComponent } from '@esengine/ecs-framework';
import type { IVector2 } from '@esengine/ecs-framework-math';
import { Collider2DBase } from './Collider2DBase';
import type { Vector2 } from '../types/Physics2DTypes';
/**
* 2D 矩形碰撞体
@@ -80,7 +80,7 @@ export class BoxCollider2DComponent extends Collider2DBase {
return this.width * this.height;
}
public override calculateAABB(): { min: Vector2; max: Vector2 } {
public override calculateAABB(): { min: IVector2; max: IVector2 } {
const hw = this.halfWidth;
const hh = this.halfHeight;

View File

@@ -4,8 +4,8 @@
*/
import { Property, Serialize, Serializable, ECSComponent } from '@esengine/ecs-framework';
import type { IVector2 } from '@esengine/ecs-framework-math';
import { Collider2DBase } from './Collider2DBase';
import type { Vector2 } from '../types/Physics2DTypes';
/**
* 胶囊方向
@@ -112,7 +112,7 @@ export class CapsuleCollider2DComponent extends Collider2DBase {
return rectArea + circleArea;
}
public override calculateAABB(): { min: Vector2; max: Vector2 } {
public override calculateAABB(): { min: IVector2; max: IVector2 } {
if (this.direction === CapsuleDirection2D.Vertical) {
return {
min: { x: this.offset.x - this.radius, y: this.offset.y - this.height / 2 },

View File

@@ -4,8 +4,8 @@
*/
import { Property, Serialize, Serializable, ECSComponent } from '@esengine/ecs-framework';
import type { IVector2 } from '@esengine/ecs-framework-math';
import { Collider2DBase } from './Collider2DBase';
import type { Vector2 } from '../types/Physics2DTypes';
/**
* 2D 圆形碰撞体
@@ -48,7 +48,7 @@ export class CircleCollider2DComponent extends Collider2DBase {
return Math.PI * this.radius * this.radius;
}
public override calculateAABB(): { min: Vector2; max: Vector2 } {
public override calculateAABB(): { min: IVector2; max: IVector2 } {
return {
min: { x: this.offset.x - this.radius, y: this.offset.y - this.radius },
max: { x: this.offset.x + this.radius, y: this.offset.y + this.radius }

View File

@@ -4,8 +4,8 @@
*/
import { Component, Property, Serialize } from '@esengine/ecs-framework';
import type { IVector2 } from '@esengine/ecs-framework-math';
import { CollisionLayer2D } from '../types/Physics2DTypes';
import type { Vector2 } from '../types/Physics2DTypes';
/**
* 2D 碰撞体基类
@@ -73,7 +73,7 @@ export abstract class Collider2DBase extends Component {
*/
@Serialize()
@Property({ type: 'vector2', label: 'Offset' })
public offset: Vector2 = { x: 0, y: 0 };
public offset: IVector2 = { x: 0, y: 0 };
/**
* 相对于实体 Transform 的旋转偏移(度)
@@ -117,7 +117,7 @@ export abstract class Collider2DBase extends Component {
/**
* 计算碰撞体的 AABB轴对齐包围盒
*/
public abstract calculateAABB(): { min: Vector2; max: Vector2 };
public abstract calculateAABB(): { min: IVector2; max: IVector2 };
// ==================== API 方法 ====================

View File

@@ -4,8 +4,8 @@
*/
import { Serialize, Serializable, ECSComponent } from '@esengine/ecs-framework';
import type { IVector2 } from '@esengine/ecs-framework-math';
import { Collider2DBase } from './Collider2DBase';
import type { Vector2 } from '../types/Physics2DTypes';
/**
* 2D 多边形碰撞体
@@ -32,7 +32,7 @@ export class PolygonCollider2DComponent extends Collider2DBase {
* 最少3个最多不超过引擎限制通常是 8-16 个)
*/
@Serialize()
public vertices: Vector2[] = [
public vertices: IVector2[] = [
{ x: -5, y: -5 },
{ x: 5, y: -5 },
{ x: 5, y: 5 },
@@ -59,7 +59,7 @@ export class PolygonCollider2DComponent extends Collider2DBase {
return Math.abs(area) / 2;
}
public override calculateAABB(): { min: Vector2; max: Vector2 } {
public override calculateAABB(): { min: IVector2; max: IVector2 } {
if (this.vertices.length === 0) {
return {
min: { x: this.offset.x, y: this.offset.y },
@@ -89,7 +89,7 @@ export class PolygonCollider2DComponent extends Collider2DBase {
* 设置顶点
* @param vertices 顶点数组(逆时针顺序)
*/
public setVertices(vertices: Vector2[]): void {
public setVertices(vertices: IVector2[]): void {
if (vertices.length < 3) {
console.warn('PolygonCollider2D: 至少需要3个顶点');
return;
@@ -109,7 +109,7 @@ export class PolygonCollider2DComponent extends Collider2DBase {
return;
}
const vertices: Vector2[] = [];
const vertices: IVector2[] = [];
const angleStep = (Math.PI * 2) / sides;
for (let i = 0; i < sides; i++) {

View File

@@ -4,8 +4,8 @@
*/
import { Component, Property, Serialize, Serializable, ECSComponent } from '@esengine/ecs-framework';
import type { IVector2 } from '@esengine/ecs-framework-math';
import { RigidbodyType2D, CollisionDetectionMode2D } from '../types/Physics2DTypes';
import type { Vector2 } from '../types/Physics2DTypes';
/**
* 刚体约束配置
@@ -171,7 +171,7 @@ export class Rigidbody2DComponent extends Component {
/**
* 当前线速度
*/
public velocity: Vector2 = { x: 0, y: 0 };
public velocity: IVector2 = { x: 0, y: 0 };
/**
* 当前角速度(弧度/秒)
@@ -196,7 +196,7 @@ export class Rigidbody2DComponent extends Component {
* 上一帧的位置(用于插值)
* @internal
*/
public _previousPosition: Vector2 = { x: 0, y: 0 };
public _previousPosition: IVector2 = { x: 0, y: 0 };
/**
* 上一帧的旋转角度
@@ -210,7 +210,7 @@ export class Rigidbody2DComponent extends Component {
* 添加力(在下一个物理步进中应用)
* 这是一个标记方法,实际力的应用由 Physics2DSystem 处理
*/
public addForce(force: Vector2): void {
public addForce(force: IVector2): void {
this._pendingForce.x += force.x;
this._pendingForce.y += force.y;
}
@@ -218,7 +218,7 @@ export class Rigidbody2DComponent extends Component {
/**
* 添加冲量(立即改变速度)
*/
public addImpulse(impulse: Vector2): void {
public addImpulse(impulse: IVector2): void {
this._pendingImpulse.x += impulse.x;
this._pendingImpulse.y += impulse.y;
}
@@ -240,7 +240,7 @@ export class Rigidbody2DComponent extends Component {
/**
* 设置线速度
*/
public setVelocity(velocity: Vector2): void {
public setVelocity(velocity: IVector2): void {
this._targetVelocity = { ...velocity };
this._hasTargetVelocity = true;
}
@@ -277,15 +277,15 @@ export class Rigidbody2DComponent extends Component {
// ==================== 待处理的力和冲量 ====================
/** @internal */
public _pendingForce: Vector2 = { x: 0, y: 0 };
public _pendingForce: IVector2 = { x: 0, y: 0 };
/** @internal */
public _pendingImpulse: Vector2 = { x: 0, y: 0 };
public _pendingImpulse: IVector2 = { x: 0, y: 0 };
/** @internal */
public _pendingTorque: number = 0;
/** @internal */
public _pendingAngularImpulse: number = 0;
/** @internal */
public _targetVelocity: Vector2 = { x: 0, y: 0 };
public _targetVelocity: IVector2 = { x: 0, y: 0 };
/** @internal */
public _hasTargetVelocity: boolean = false;
/** @internal */

View File

@@ -9,7 +9,6 @@
export {
RigidbodyType2D,
CollisionDetectionMode2D,
type Vector2,
type Physics2DConfig,
DEFAULT_PHYSICS_CONFIG,
CollisionLayer2D,
@@ -22,6 +21,9 @@ export {
JointType2D
} from './types/Physics2DTypes';
// Re-export IVector2 from math package
export type { IVector2 } from '@esengine/ecs-framework-math';
export {
type CollisionEventType,
type TriggerEventType,

View File

@@ -6,7 +6,8 @@
*/
import type { IService } from '@esengine/ecs-framework';
import type { Vector2, Physics2DConfig, RaycastHit2D, OverlapResult2D } from '../types/Physics2DTypes';
import type { IVector2 } from '@esengine/ecs-framework-math';
import type { Physics2DConfig, RaycastHit2D, OverlapResult2D } from '../types/Physics2DTypes';
import { DEFAULT_PHYSICS_CONFIG, CollisionLayer2D } from '../types/Physics2DTypes';
import type { Physics2DSystem } from '../systems/Physics2DSystem';
@@ -59,7 +60,7 @@ export class Physics2DService implements IService {
/**
* 设置重力
*/
public setGravity(gravity: Vector2): void {
public setGravity(gravity: IVector2): void {
this._config.gravity = { ...gravity };
this._physicsSystem?.setGravity(gravity);
}
@@ -67,7 +68,7 @@ export class Physics2DService implements IService {
/**
* 获取重力
*/
public getGravity(): Vector2 {
public getGravity(): IVector2 {
return this._physicsSystem?.getGravity() ?? { ...this._config.gravity };
}
@@ -95,8 +96,8 @@ export class Physics2DService implements IService {
* @param collisionMask 碰撞掩码(默认所有层)
*/
public raycast(
origin: Vector2,
direction: Vector2,
origin: IVector2,
direction: IVector2,
maxDistance: number,
collisionMask: number = CollisionLayer2D.All
): RaycastHit2D | null {
@@ -111,8 +112,8 @@ export class Physics2DService implements IService {
* @param collisionMask 碰撞掩码(默认所有层)
*/
public raycastAll(
origin: Vector2,
direction: Vector2,
origin: IVector2,
direction: IVector2,
maxDistance: number,
collisionMask: number = CollisionLayer2D.All
): RaycastHit2D[] {
@@ -124,7 +125,7 @@ export class Physics2DService implements IService {
* @param point 检测点
* @param collisionMask 碰撞掩码
*/
public overlapPoint(point: Vector2, collisionMask: number = CollisionLayer2D.All): OverlapResult2D {
public overlapPoint(point: IVector2, collisionMask: number = CollisionLayer2D.All): OverlapResult2D {
return this._physicsSystem?.overlapPoint(point, collisionMask) ?? { entityIds: [], colliderHandles: [] };
}
@@ -135,7 +136,7 @@ export class Physics2DService implements IService {
* @param collisionMask 碰撞掩码
*/
public overlapCircle(
center: Vector2,
center: IVector2,
radius: number,
collisionMask: number = CollisionLayer2D.All
): OverlapResult2D {
@@ -150,8 +151,8 @@ export class Physics2DService implements IService {
* @param collisionMask 碰撞掩码
*/
public overlapBox(
center: Vector2,
halfExtents: Vector2,
center: IVector2,
halfExtents: IVector2,
rotation: number = 0,
collisionMask: number = CollisionLayer2D.All
): OverlapResult2D {
@@ -168,7 +169,7 @@ export class Physics2DService implements IService {
/**
* 归一化向量
*/
public normalize(v: Vector2): Vector2 {
public normalize(v: IVector2): IVector2 {
const length = Math.sqrt(v.x * v.x + v.y * v.y);
if (length === 0) return { x: 0, y: 0 };
return { x: v.x / length, y: v.y / length };
@@ -177,7 +178,7 @@ export class Physics2DService implements IService {
/**
* 计算两点之间的距离
*/
public distance(a: Vector2, b: Vector2): number {
public distance(a: IVector2, b: IVector2): number {
const dx = b.x - a.x;
const dy = b.y - a.y;
return Math.sqrt(dx * dx + dy * dy);
@@ -186,21 +187,21 @@ export class Physics2DService implements IService {
/**
* 计算向量长度
*/
public magnitude(v: Vector2): number {
public magnitude(v: IVector2): number {
return Math.sqrt(v.x * v.x + v.y * v.y);
}
/**
* 向量点积
*/
public dot(a: Vector2, b: Vector2): number {
public dot(a: IVector2, b: IVector2): number {
return a.x * b.x + a.y * b.y;
}
/**
* 向量叉积返回标量2D 特有)
*/
public cross(a: Vector2, b: Vector2): number {
public cross(a: IVector2, b: IVector2): number {
return a.x * b.y - a.y * b.x;
}

View File

@@ -14,7 +14,8 @@ import { BoxCollider2DComponent } from '../components/BoxCollider2DComponent';
import { CircleCollider2DComponent } from '../components/CircleCollider2DComponent';
import { CapsuleCollider2DComponent } from '../components/CapsuleCollider2DComponent';
import { PolygonCollider2DComponent } from '../components/PolygonCollider2DComponent';
import type { Physics2DConfig, Vector2 } from '../types/Physics2DTypes';
import type { IVector2 } from '@esengine/ecs-framework-math';
import type { Physics2DConfig } from '../types/Physics2DTypes';
import { PHYSICS_EVENTS, type CollisionEvent2D, type TriggerEvent2D } from '../types/Physics2DEvents';
/**
@@ -226,49 +227,49 @@ export class Physics2DSystem extends EntitySystem {
* 设置重力
* @param gravity 重力向量
*/
public setGravity(gravity: Vector2): void {
public setGravity(gravity: IVector2): void {
this._world.setGravity(gravity);
}
/**
* 获取重力
*/
public getGravity(): Vector2 {
public getGravity(): IVector2 {
return this._world.getGravity();
}
/**
* 射线检测
*/
public raycast(origin: Vector2, direction: Vector2, maxDistance: number, collisionMask?: number) {
public raycast(origin: IVector2, direction: IVector2, maxDistance: number, collisionMask?: number) {
return this._world.raycast(origin, direction, maxDistance, collisionMask);
}
/**
* 射线检测所有
*/
public raycastAll(origin: Vector2, direction: Vector2, maxDistance: number, collisionMask?: number) {
public raycastAll(origin: IVector2, direction: IVector2, maxDistance: number, collisionMask?: number) {
return this._world.raycastAll(origin, direction, maxDistance, collisionMask);
}
/**
* 点重叠检测
*/
public overlapPoint(point: Vector2, collisionMask?: number) {
public overlapPoint(point: IVector2, collisionMask?: number) {
return this._world.overlapPoint(point, collisionMask);
}
/**
* 圆形重叠检测
*/
public overlapCircle(center: Vector2, radius: number, collisionMask?: number) {
public overlapCircle(center: IVector2, radius: number, collisionMask?: number) {
return this._world.overlapCircle(center, radius, collisionMask);
}
/**
* 矩形重叠检测
*/
public overlapBox(center: Vector2, halfExtents: Vector2, rotation?: number, collisionMask?: number) {
public overlapBox(center: IVector2, halfExtents: IVector2, rotation?: number, collisionMask?: number) {
return this._world.overlapBox(center, halfExtents, rotation, collisionMask);
}
@@ -298,7 +299,7 @@ export class Physics2DSystem extends EntitySystem {
}
// 获取位置和旋转
const position: Vector2 = {
const position: IVector2 = {
x: transform.position.x,
y: transform.position.y
};
@@ -314,7 +315,7 @@ export class Physics2DSystem extends EntitySystem {
// 收集并创建碰撞体
const colliderHandles: number[] = [];
const colliders = this._getColliders(entity);
const scale: Vector2 = { x: transform.scale.x, y: transform.scale.y };
const scale: IVector2 = { x: transform.scale.x, y: transform.scale.y };
for (const collider of colliders) {
const colliderHandle = this._world.createCollider(entity.id, collider, bodyHandle, scale);
@@ -368,7 +369,7 @@ export class Physics2DSystem extends EntitySystem {
// 只有当需要同步时才更新物理世界
if (rigidbody._needsSync) {
const position: Vector2 = {
const position: IVector2 = {
x: transform.position.x,
y: transform.position.y
};
@@ -380,7 +381,7 @@ export class Physics2DSystem extends EntitySystem {
// 检查碰撞体是否需要重建
const colliders = this._getColliders(entity);
const scale: Vector2 = { x: transform.scale.x, y: transform.scale.y };
const scale: IVector2 = { x: transform.scale.x, y: transform.scale.y };
for (const collider of colliders) {
if (collider._needsRebuild) {
// 移除旧碰撞体

View File

@@ -21,7 +21,7 @@
* ```
*/
import { createServiceToken } from '@esengine/engine-core';
import { createServiceToken } from '@esengine/ecs-framework';
import type { Physics2DSystem } from './systems/Physics2DSystem';
// ============================================================================

View File

@@ -3,7 +3,7 @@
* 2D 物理事件定义
*/
import type { Vector2 } from './Physics2DTypes';
import type { IVector2 } from '@esengine/ecs-framework-math';
/**
* 碰撞事件类型
@@ -20,9 +20,9 @@ export type TriggerEventType = 'enter' | 'stay' | 'exit';
*/
export interface ContactPoint2D {
/** 接触点位置 */
point: Vector2;
point: IVector2;
/** 接触点法线 */
normal: Vector2;
normal: IVector2;
/** 穿透深度 */
penetration: number;
/** 冲量大小 */
@@ -46,7 +46,7 @@ export interface CollisionEvent2D {
/** 接触点列表(仅在 enter 和 stay 时有效) */
contacts: ContactPoint2D[];
/** 相对速度 */
relativeVelocity: Vector2;
relativeVelocity: IVector2;
/** 总冲量大小 */
totalImpulse: number;
}

View File

@@ -25,20 +25,15 @@ export enum CollisionDetectionMode2D {
Continuous = 1
}
/**
* 2D 向量
*/
export interface Vector2 {
x: number;
y: number;
}
// 使用 IVector2 接口 | Use IVector2 interface
import type { IVector2 } from '@esengine/ecs-framework-math';
/**
* 物理配置
*/
export interface Physics2DConfig {
/** 重力向量 */
gravity: Vector2;
gravity: IVector2;
/** 固定时间步长(秒) */
timestep: number;
/** 每帧最大子步数 */
@@ -98,9 +93,9 @@ export interface RaycastHit2D {
/** 命中的实体 ID */
entityId: number;
/** 命中点 */
point: Vector2;
point: IVector2;
/** 命中面的法线 */
normal: Vector2;
normal: IVector2;
/** 射线起点到命中点的距离 */
distance: number;
/** 命中的碰撞体句柄 */

View File

@@ -5,7 +5,6 @@
export {
RigidbodyType2D,
CollisionDetectionMode2D,
type Vector2,
type Physics2DConfig,
DEFAULT_PHYSICS_CONFIG,
CollisionLayer2D,
@@ -18,6 +17,9 @@ export {
JointType2D
} from './Physics2DTypes';
// Re-export IVector2 for type compatibility
export type { IVector2 } from '@esengine/ecs-framework-math';
export {
type CollisionEventType,
type TriggerEventType,

View File

@@ -6,9 +6,9 @@
*/
import type RAPIER from '@esengine/rapier2d';
import type { IVector2 } from '@esengine/ecs-framework-math';
import type {
Physics2DConfig,
Vector2,
RaycastHit2D,
OverlapResult2D
} from '../types/Physics2DTypes';
@@ -202,7 +202,7 @@ export class Physics2DWorld {
public createBody(
entityId: number,
rigidbody: Rigidbody2DComponent,
position: Vector2,
position: IVector2,
rotation: number
): number | null {
if (!this._world || !this._rapier) return null;
@@ -299,7 +299,7 @@ export class Physics2DWorld {
* @param position 位置
* @param rotation 旋转
*/
public setBodyTransform(handle: number, position: Vector2, rotation: number): void {
public setBodyTransform(handle: number, position: IVector2, rotation: number): void {
if (!this._world || !this._rapier) return;
const body = this._world.getRigidBody(handle);
@@ -313,7 +313,7 @@ export class Physics2DWorld {
* 获取刚体位置
* @param handle 刚体句柄
*/
public getBodyPosition(handle: number): Vector2 | null {
public getBodyPosition(handle: number): IVector2 | null {
if (!this._world) return null;
const body = this._world.getRigidBody(handle);
@@ -340,7 +340,7 @@ export class Physics2DWorld {
* 获取刚体速度
* @param handle 刚体句柄
*/
public getBodyVelocity(handle: number): Vector2 | null {
public getBodyVelocity(handle: number): IVector2 | null {
if (!this._world) return null;
const body = this._world.getRigidBody(handle);
@@ -368,7 +368,7 @@ export class Physics2DWorld {
* @param handle 刚体句柄
* @param force 力向量
*/
public applyForce(handle: number, force: Vector2): void {
public applyForce(handle: number, force: IVector2): void {
if (!this._world || !this._rapier) return;
const body = this._world.getRigidBody(handle);
@@ -382,7 +382,7 @@ export class Physics2DWorld {
* @param handle 刚体句柄
* @param impulse 冲量向量
*/
public applyImpulse(handle: number, impulse: Vector2): void {
public applyImpulse(handle: number, impulse: IVector2): void {
if (!this._world || !this._rapier) return;
const body = this._world.getRigidBody(handle);
@@ -424,7 +424,7 @@ export class Physics2DWorld {
* @param handle 刚体句柄
* @param velocity 速度向量
*/
public setVelocity(handle: number, velocity: Vector2): void {
public setVelocity(handle: number, velocity: IVector2): void {
if (!this._world || !this._rapier) return;
const body = this._world.getRigidBody(handle);
@@ -482,7 +482,7 @@ export class Physics2DWorld {
* @param bodyHandle 关联的刚体句柄(可选)
* @param scale Transform 的缩放值(可选)
*/
public createCollider(entityId: number, collider: Collider2DBase, bodyHandle?: number, scale?: Vector2): number | null {
public createCollider(entityId: number, collider: Collider2DBase, bodyHandle?: number, scale?: IVector2): number | null {
if (!this._world || !this._rapier) return null;
// 创建碰撞体描述
@@ -524,8 +524,8 @@ export class Physics2DWorld {
*/
public createStaticCollider(
entityId: number,
position: Vector2,
halfExtents: Vector2,
position: IVector2,
halfExtents: IVector2,
collisionLayer: number,
collisionMask: number,
friction: number,
@@ -635,8 +635,8 @@ export class Physics2DWorld {
* @param collisionMask 碰撞掩码
*/
public raycast(
origin: Vector2,
direction: Vector2,
origin: IVector2,
direction: IVector2,
maxDistance: number,
collisionMask: number = 0xffff
): RaycastHit2D | null {
@@ -687,8 +687,8 @@ export class Physics2DWorld {
* @param collisionMask 碰撞掩码
*/
public raycastAll(
origin: Vector2,
direction: Vector2,
origin: IVector2,
direction: IVector2,
maxDistance: number,
collisionMask: number = 0xffff
): RaycastHit2D[] {
@@ -731,7 +731,7 @@ export class Physics2DWorld {
* @param point 检测点
* @param collisionMask 碰撞掩码
*/
public overlapPoint(point: Vector2, collisionMask: number = 0xffff): OverlapResult2D {
public overlapPoint(point: IVector2, collisionMask: number = 0xffff): OverlapResult2D {
if (!this._world || !this._rapier) {
return { entityIds: [], colliderHandles: [] };
}
@@ -757,7 +757,7 @@ export class Physics2DWorld {
* @param radius 半径
* @param collisionMask 碰撞掩码
*/
public overlapCircle(center: Vector2, radius: number, collisionMask: number = 0xffff): OverlapResult2D {
public overlapCircle(center: IVector2, radius: number, collisionMask: number = 0xffff): OverlapResult2D {
if (!this._world || !this._rapier) {
return { entityIds: [], colliderHandles: [] };
}
@@ -788,8 +788,8 @@ export class Physics2DWorld {
* @param collisionMask 碰撞掩码
*/
public overlapBox(
center: Vector2,
halfExtents: Vector2,
center: IVector2,
halfExtents: IVector2,
rotation: number = 0,
collisionMask: number = 0xffff
): OverlapResult2D {
@@ -847,7 +847,7 @@ export class Physics2DWorld {
* 设置重力
* @param gravity 重力向量
*/
public setGravity(gravity: Vector2): void {
public setGravity(gravity: IVector2): void {
if (!this._world || !this._rapier) return;
this._config.gravity = gravity;
@@ -857,7 +857,7 @@ export class Physics2DWorld {
/**
* 获取重力
*/
public getGravity(): Vector2 {
public getGravity(): IVector2 {
return { ...this._config.gravity };
}
@@ -971,7 +971,7 @@ export class Physics2DWorld {
/**
* 创建碰撞体描述
*/
private _createColliderDesc(collider: Collider2DBase, scale?: Vector2): RAPIER.ColliderDesc | null {
private _createColliderDesc(collider: Collider2DBase, scale?: IVector2): RAPIER.ColliderDesc | null {
if (!this._rapier) return null;
const sx = scale?.x ?? 1;