refactor: reorganize package structure and decouple framework packages (#338)
* refactor: reorganize package structure and decouple framework packages ## Package Structure Reorganization - Reorganized 55 packages into categorized subdirectories: - packages/framework/ - Generic framework (Laya/Cocos compatible) - packages/engine/ - ESEngine core modules - packages/rendering/ - Rendering modules (WASM dependent) - packages/physics/ - Physics modules - packages/streaming/ - World streaming - packages/network-ext/ - Network extensions - packages/editor/ - Editor framework and plugins - packages/rust/ - Rust WASM engine - packages/tools/ - Build tools and SDK ## Framework Package Decoupling - Decoupled behavior-tree and blueprint packages from ESEngine dependencies - Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent) - ESEngine-specific code moved to esengine/ subpath exports - Framework packages now usable with Cocos/Laya without ESEngine ## CI Configuration - Updated CI to only type-check and lint framework packages - Added type-check:framework and lint:framework scripts ## Breaking Changes - Package import paths changed due to directory reorganization - ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine') * fix: update es-engine file path after directory reorganization * docs: update README to focus on framework over engine * ci: only build framework packages, remove Rust/WASM dependencies * fix: remove esengine subpath from behavior-tree and blueprint builds ESEngine integration code will only be available in full engine builds. Framework packages are now purely engine-agnostic. * fix: move network-protocols to framework, build both in CI * fix: update workflow paths from packages/core to packages/framework/core * fix: exclude esengine folder from type-check in behavior-tree and blueprint * fix: update network tsconfig references to new paths * fix: add test:ci:framework to only test framework packages in CI * fix: only build core and math npm packages in CI * fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* Collision Layer Configuration Service
|
||||
* 碰撞层配置服务
|
||||
*
|
||||
* 管理碰撞层的定义和碰撞矩阵配置
|
||||
*/
|
||||
|
||||
/**
|
||||
* 碰撞层定义
|
||||
*/
|
||||
export interface CollisionLayerDefinition {
|
||||
/** 层索引 (0-15) */
|
||||
index: number;
|
||||
/** 层名称 */
|
||||
name: string;
|
||||
/** 层描述 */
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 碰撞层配置
|
||||
*/
|
||||
export interface CollisionLayerSettings {
|
||||
/** 层定义列表 */
|
||||
layers: CollisionLayerDefinition[];
|
||||
/** 碰撞矩阵 (16x16 位图,matrix[i] 表示第 i 层可以与哪些层碰撞) */
|
||||
collisionMatrix: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认碰撞层配置
|
||||
*/
|
||||
export const DEFAULT_COLLISION_LAYERS: CollisionLayerDefinition[] = [
|
||||
{ index: 0, name: 'Default', description: '默认层' },
|
||||
{ index: 1, name: 'Player', description: '玩家' },
|
||||
{ index: 2, name: 'Enemy', description: '敌人' },
|
||||
{ index: 3, name: 'Projectile', description: '投射物' },
|
||||
{ index: 4, name: 'Ground', description: '地面' },
|
||||
{ index: 5, name: 'Platform', description: '平台' },
|
||||
{ index: 6, name: 'Trigger', description: '触发器' },
|
||||
{ index: 7, name: 'Item', description: '物品' },
|
||||
{ index: 8, name: 'Layer8', description: '自定义层8' },
|
||||
{ index: 9, name: 'Layer9', description: '自定义层9' },
|
||||
{ index: 10, name: 'Layer10', description: '自定义层10' },
|
||||
{ index: 11, name: 'Layer11', description: '自定义层11' },
|
||||
{ index: 12, name: 'Layer12', description: '自定义层12' },
|
||||
{ index: 13, name: 'Layer13', description: '自定义层13' },
|
||||
{ index: 14, name: 'Layer14', description: '自定义层14' },
|
||||
{ index: 15, name: 'Layer15', description: '自定义层15' },
|
||||
];
|
||||
|
||||
/**
|
||||
* 默认碰撞矩阵 - 所有层都可以互相碰撞
|
||||
*/
|
||||
export const DEFAULT_COLLISION_MATRIX: number[] = Array(16).fill(0xFFFF);
|
||||
|
||||
/**
|
||||
* 碰撞层配置管理器
|
||||
*/
|
||||
export class CollisionLayerConfig {
|
||||
private static instance: CollisionLayerConfig | null = null;
|
||||
|
||||
private layers: CollisionLayerDefinition[] = [...DEFAULT_COLLISION_LAYERS];
|
||||
private collisionMatrix: number[] = [...DEFAULT_COLLISION_MATRIX];
|
||||
private listeners: Set<() => void> = new Set();
|
||||
|
||||
private constructor() {}
|
||||
|
||||
public static getInstance(): CollisionLayerConfig {
|
||||
if (!CollisionLayerConfig.instance) {
|
||||
CollisionLayerConfig.instance = new CollisionLayerConfig();
|
||||
}
|
||||
return CollisionLayerConfig.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有层定义
|
||||
*/
|
||||
public getLayers(): readonly CollisionLayerDefinition[] {
|
||||
return this.layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取层名称
|
||||
*/
|
||||
public getLayerName(index: number): string {
|
||||
if (index < 0 || index >= 16) return `Layer${index}`;
|
||||
return this.layers[index]?.name ?? `Layer${index}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置层名称
|
||||
*/
|
||||
public setLayerName(index: number, name: string): void {
|
||||
if (index < 0 || index >= 16) return;
|
||||
if (this.layers[index]) {
|
||||
this.layers[index].name = name;
|
||||
this.notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置层描述
|
||||
*/
|
||||
public setLayerDescription(index: number, description: string): void {
|
||||
if (index < 0 || index >= 16) return;
|
||||
if (this.layers[index]) {
|
||||
this.layers[index].description = description;
|
||||
this.notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取碰撞矩阵
|
||||
*/
|
||||
public getCollisionMatrix(): readonly number[] {
|
||||
return this.collisionMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查两个层是否可以碰撞
|
||||
*/
|
||||
public canLayersCollide(layerA: number, layerB: number): boolean {
|
||||
if (layerA < 0 || layerA >= 16 || layerB < 0 || layerB >= 16) {
|
||||
return false;
|
||||
}
|
||||
return (this.collisionMatrix[layerA] & (1 << layerB)) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置两个层是否可以碰撞
|
||||
*/
|
||||
public setLayersCanCollide(layerA: number, layerB: number, canCollide: boolean): void {
|
||||
if (layerA < 0 || layerA >= 16 || layerB < 0 || layerB >= 16) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canCollide) {
|
||||
this.collisionMatrix[layerA] |= (1 << layerB);
|
||||
this.collisionMatrix[layerB] |= (1 << layerA);
|
||||
} else {
|
||||
this.collisionMatrix[layerA] &= ~(1 << layerB);
|
||||
this.collisionMatrix[layerB] &= ~(1 << layerA);
|
||||
}
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定层的碰撞掩码
|
||||
*/
|
||||
public getLayerMask(layerIndex: number): number {
|
||||
if (layerIndex < 0 || layerIndex >= 16) return 0xFFFF;
|
||||
return this.collisionMatrix[layerIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据层索引获取层位值
|
||||
*/
|
||||
public getLayerBit(layerIndex: number): number {
|
||||
if (layerIndex < 0 || layerIndex >= 16) return 1;
|
||||
return 1 << layerIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从层位值获取层索引
|
||||
*/
|
||||
public getLayerIndex(layerBit: number): number {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
if (layerBit === (1 << i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// 如果是多层位值,返回第一个设置的位
|
||||
for (let i = 0; i < 16; i++) {
|
||||
if ((layerBit & (1 << i)) !== 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载配置
|
||||
*/
|
||||
public loadSettings(settings: Partial<CollisionLayerSettings>): void {
|
||||
if (settings.layers) {
|
||||
this.layers = settings.layers.map((layer, i) => ({
|
||||
index: layer.index ?? i,
|
||||
name: layer.name ?? `Layer${i}`,
|
||||
description: layer.description
|
||||
}));
|
||||
// 确保有16个层
|
||||
while (this.layers.length < 16) {
|
||||
const idx = this.layers.length;
|
||||
this.layers.push({ index: idx, name: `Layer${idx}` });
|
||||
}
|
||||
}
|
||||
if (settings.collisionMatrix) {
|
||||
this.collisionMatrix = [...settings.collisionMatrix];
|
||||
while (this.collisionMatrix.length < 16) {
|
||||
this.collisionMatrix.push(0xFFFF);
|
||||
}
|
||||
}
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出配置
|
||||
*/
|
||||
public exportSettings(): CollisionLayerSettings {
|
||||
return {
|
||||
layers: [...this.layers],
|
||||
collisionMatrix: [...this.collisionMatrix]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置为默认配置
|
||||
*/
|
||||
public resetToDefault(): void {
|
||||
this.layers = [...DEFAULT_COLLISION_LAYERS];
|
||||
this.collisionMatrix = [...DEFAULT_COLLISION_MATRIX];
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加监听器
|
||||
*/
|
||||
public addListener(listener: () => void): void {
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除监听器
|
||||
*/
|
||||
public removeListener(listener: () => void): void {
|
||||
this.listeners.delete(listener);
|
||||
}
|
||||
|
||||
private notifyListeners(): void {
|
||||
for (const listener of this.listeners) {
|
||||
listener();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* Physics2DService
|
||||
* 2D 物理服务
|
||||
*
|
||||
* 提供全局物理配置和实用方法
|
||||
*/
|
||||
|
||||
import type { IService } from '@esengine/ecs-framework';
|
||||
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';
|
||||
|
||||
/**
|
||||
* 2D 物理服务
|
||||
*
|
||||
* 提供场景级别的物理配置和全局查询方法。
|
||||
* 作为服务注册到 ServiceContainer 中。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 从服务容器获取
|
||||
* const physicsService = scene.services.resolve(Physics2DService);
|
||||
*
|
||||
* // 使用射线检测
|
||||
* const hit = physicsService.raycast(origin, direction, 100);
|
||||
* if (hit) {
|
||||
* console.log('Hit entity:', hit.entityId);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class Physics2DService implements IService {
|
||||
private _config: Physics2DConfig = { ...DEFAULT_PHYSICS_CONFIG };
|
||||
private _physicsSystem: Physics2DSystem | null = null;
|
||||
|
||||
/**
|
||||
* 设置物理系统引用
|
||||
* @internal
|
||||
*/
|
||||
public setPhysicsSystem(system: Physics2DSystem): void {
|
||||
this._physicsSystem = system;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取物理系统
|
||||
*/
|
||||
public getPhysicsSystem(): Physics2DSystem | null {
|
||||
return this._physicsSystem;
|
||||
}
|
||||
|
||||
// ==================== 配置 ====================
|
||||
|
||||
/**
|
||||
* 获取物理配置
|
||||
*/
|
||||
public getConfig(): Readonly<Physics2DConfig> {
|
||||
return this._config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置重力
|
||||
*/
|
||||
public setGravity(gravity: IVector2): void {
|
||||
this._config.gravity = { ...gravity };
|
||||
this._physicsSystem?.setGravity(gravity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取重力
|
||||
*/
|
||||
public getGravity(): IVector2 {
|
||||
return this._physicsSystem?.getGravity() ?? { ...this._config.gravity };
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置时间步长
|
||||
*/
|
||||
public setTimestep(timestep: number): void {
|
||||
this._config.timestep = timestep;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取时间步长
|
||||
*/
|
||||
public getTimestep(): number {
|
||||
return this._config.timestep;
|
||||
}
|
||||
|
||||
// ==================== 查询 ====================
|
||||
|
||||
/**
|
||||
* 射线检测(第一个命中)
|
||||
* @param origin 起点
|
||||
* @param direction 方向(归一化)
|
||||
* @param maxDistance 最大距离
|
||||
* @param collisionMask 碰撞掩码(默认所有层)
|
||||
*/
|
||||
public raycast(
|
||||
origin: IVector2,
|
||||
direction: IVector2,
|
||||
maxDistance: number,
|
||||
collisionMask: number = CollisionLayer2D.All
|
||||
): RaycastHit2D | null {
|
||||
return this._physicsSystem?.raycast(origin, direction, maxDistance, collisionMask) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 射线检测(所有命中)
|
||||
* @param origin 起点
|
||||
* @param direction 方向(归一化)
|
||||
* @param maxDistance 最大距离
|
||||
* @param collisionMask 碰撞掩码(默认所有层)
|
||||
*/
|
||||
public raycastAll(
|
||||
origin: IVector2,
|
||||
direction: IVector2,
|
||||
maxDistance: number,
|
||||
collisionMask: number = CollisionLayer2D.All
|
||||
): RaycastHit2D[] {
|
||||
return this._physicsSystem?.raycastAll(origin, direction, maxDistance, collisionMask) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 点重叠检测
|
||||
* @param point 检测点
|
||||
* @param collisionMask 碰撞掩码
|
||||
*/
|
||||
public overlapPoint(point: IVector2, collisionMask: number = CollisionLayer2D.All): OverlapResult2D {
|
||||
return this._physicsSystem?.overlapPoint(point, collisionMask) ?? { entityIds: [], colliderHandles: [] };
|
||||
}
|
||||
|
||||
/**
|
||||
* 圆形重叠检测
|
||||
* @param center 圆心
|
||||
* @param radius 半径
|
||||
* @param collisionMask 碰撞掩码
|
||||
*/
|
||||
public overlapCircle(
|
||||
center: IVector2,
|
||||
radius: number,
|
||||
collisionMask: number = CollisionLayer2D.All
|
||||
): OverlapResult2D {
|
||||
return this._physicsSystem?.overlapCircle(center, radius, collisionMask) ?? { entityIds: [], colliderHandles: [] };
|
||||
}
|
||||
|
||||
/**
|
||||
* 矩形重叠检测
|
||||
* @param center 中心点
|
||||
* @param halfExtents 半宽高
|
||||
* @param rotation 旋转角度
|
||||
* @param collisionMask 碰撞掩码
|
||||
*/
|
||||
public overlapBox(
|
||||
center: IVector2,
|
||||
halfExtents: IVector2,
|
||||
rotation: number = 0,
|
||||
collisionMask: number = CollisionLayer2D.All
|
||||
): OverlapResult2D {
|
||||
return (
|
||||
this._physicsSystem?.overlapBox(center, halfExtents, rotation, collisionMask) ?? {
|
||||
entityIds: [],
|
||||
colliderHandles: []
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 工具方法 ====================
|
||||
|
||||
/**
|
||||
* 归一化向量
|
||||
*/
|
||||
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 };
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两点之间的距离
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算向量长度
|
||||
*/
|
||||
public magnitude(v: IVector2): number {
|
||||
return Math.sqrt(v.x * v.x + v.y * v.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向量点积
|
||||
*/
|
||||
public dot(a: IVector2, b: IVector2): number {
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向量叉积(返回标量,2D 特有)
|
||||
*/
|
||||
public cross(a: IVector2, b: IVector2): number {
|
||||
return a.x * b.y - a.y * b.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放资源
|
||||
*/
|
||||
public dispose(): void {
|
||||
this._physicsSystem = null;
|
||||
}
|
||||
}
|
||||
14
packages/physics/physics-rapier2d/src/services/index.ts
Normal file
14
packages/physics/physics-rapier2d/src/services/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Physics 2D Services exports
|
||||
*/
|
||||
|
||||
export { Physics2DService } from './Physics2DService';
|
||||
export {
|
||||
CollisionLayerConfig,
|
||||
DEFAULT_COLLISION_LAYERS,
|
||||
DEFAULT_COLLISION_MATRIX
|
||||
} from './CollisionLayerConfig';
|
||||
export type {
|
||||
CollisionLayerDefinition,
|
||||
CollisionLayerSettings
|
||||
} from './CollisionLayerConfig';
|
||||
Reference in New Issue
Block a user