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:
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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()
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 方法 ====================
|
||||
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
// 移除旧碰撞体
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { createServiceToken } from '@esengine/engine-core';
|
||||
import { createServiceToken } from '@esengine/ecs-framework';
|
||||
import type { Physics2DSystem } from './systems/Physics2DSystem';
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
/** 命中的碰撞体句柄 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user