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:
YHH
2025-12-26 14:50:35 +08:00
committed by GitHub
parent a84ff902e4
commit 155411e743
1936 changed files with 4147 additions and 11578 deletions

View File

@@ -0,0 +1,388 @@
import type { Particle, ParticlePool } from './Particle';
/**
* 发射形状类型
* Emission shape type
*/
export enum EmissionShape {
/** 点发射 | Point emission */
Point = 'point',
/** 圆形发射(填充)| Circle emission (filled) */
Circle = 'circle',
/** 矩形发射 | Rectangle emission */
Rectangle = 'rectangle',
/** 线段发射 | Line emission */
Line = 'line',
/** 圆锥/扇形发射 | Cone/fan emission */
Cone = 'cone',
/** 圆环发射(边缘)| Ring emission (edge only) */
Ring = 'ring',
/** 矩形边缘发射 | Rectangle edge emission */
Edge = 'edge'
}
/**
* 数值范围
* Value range for randomization
*/
export interface ValueRange {
min: number;
max: number;
}
/**
* 颜色值
* Color value (RGBA)
*/
export interface ColorValue {
r: number;
g: number;
b: number;
a: number;
}
/**
* 发射器配置
* Emitter configuration
*/
export interface EmitterConfig {
/** 每秒发射数量 | Particles per second */
emissionRate: number;
/** 单次爆发数量0表示持续发射| Burst count (0 for continuous) */
burstCount: number;
/** 粒子生命时间范围(秒)| Particle lifetime range (seconds) */
lifetime: ValueRange;
/** 发射形状 | Emission shape */
shape: EmissionShape;
/** 形状半径(用于圆形/圆锥)| Shape radius (for circle/cone) */
shapeRadius: number;
/** 形状宽度(用于矩形/线段)| Shape width (for rectangle/line) */
shapeWidth: number;
/** 形状高度(用于矩形)| Shape height (for rectangle) */
shapeHeight: number;
/** 圆锥角度(弧度,用于圆锥发射)| Cone angle (radians, for cone shape) */
coneAngle: number;
/** 发射方向弧度0=右)| Emission direction (radians, 0=right) */
direction: number;
/** 发射方向随机范围(弧度)| Direction random spread (radians) */
directionSpread: number;
/** 初始速度范围 | Initial speed range */
speed: ValueRange;
/** 初始角速度范围 | Initial angular velocity range */
angularVelocity: ValueRange;
/** 初始缩放范围 | Initial scale range */
startScale: ValueRange;
/** 初始旋转范围(弧度)| Initial rotation range (radians) */
startRotation: ValueRange;
/** 初始颜色 | Initial color */
startColor: ColorValue;
/** 初始颜色变化范围 | Initial color variance */
startColorVariance: ColorValue;
/** 重力X | Gravity X */
gravityX: number;
/** 重力Y | Gravity Y */
gravityY: number;
}
/**
* 创建默认发射器配置
* Create default emitter configuration
*/
export function createDefaultEmitterConfig(): EmitterConfig {
return {
emissionRate: 10,
burstCount: 0,
lifetime: { min: 1, max: 2 },
shape: EmissionShape.Point,
shapeRadius: 0,
shapeWidth: 0,
shapeHeight: 0,
coneAngle: Math.PI / 6,
direction: -Math.PI / 2,
directionSpread: 0,
speed: { min: 50, max: 100 },
angularVelocity: { min: 0, max: 0 },
startScale: { min: 1, max: 1 },
startRotation: { min: 0, max: 0 },
startColor: { r: 1, g: 1, b: 1, a: 1 },
startColorVariance: { r: 0, g: 0, b: 0, a: 0 },
gravityX: 0,
gravityY: 0
};
}
/**
* 粒子发射器
* Particle emitter - handles particle spawning
*/
export class ParticleEmitter {
public config: EmitterConfig;
private _emissionAccumulator: number = 0;
private _isEmitting: boolean = true;
constructor(config?: Partial<EmitterConfig>) {
this.config = { ...createDefaultEmitterConfig(), ...config };
}
/** 是否正在发射 | Whether emitter is active */
get isEmitting(): boolean {
return this._isEmitting;
}
set isEmitting(value: boolean) {
this._isEmitting = value;
}
/**
* 发射粒子
* Emit particles
*
* @param pool - Particle pool
* @param dt - Delta time in seconds
* @param worldX - World position X
* @param worldY - World position Y
* @param worldRotation - World rotation in radians (applied to emission direction)
* @param worldScaleX - World scale X (applied to emission offset and speed)
* @param worldScaleY - World scale Y (applied to emission offset and speed)
* @returns Number of particles emitted
*/
emit(
pool: ParticlePool,
dt: number,
worldX: number,
worldY: number,
worldRotation: number = 0,
worldScaleX: number = 1,
worldScaleY: number = 1
): number {
if (!this._isEmitting) return 0;
let emitted = 0;
if (this.config.burstCount > 0) {
// 爆发模式 | Burst mode
for (let i = 0; i < this.config.burstCount; i++) {
const p = pool.spawn();
if (p) {
this._initParticle(p, worldX, worldY, worldRotation, worldScaleX, worldScaleY);
emitted++;
}
}
this._isEmitting = false;
} else {
// 持续发射 | Continuous emission
this._emissionAccumulator += this.config.emissionRate * dt;
while (this._emissionAccumulator >= 1) {
const p = pool.spawn();
if (p) {
this._initParticle(p, worldX, worldY, worldRotation, worldScaleX, worldScaleY);
emitted++;
}
this._emissionAccumulator -= 1;
}
}
return emitted;
}
/**
* 立即爆发发射
* Burst emit immediately
*
* @param pool - Particle pool
* @param count - Number of particles to emit
* @param worldX - World position X
* @param worldY - World position Y
* @param worldRotation - World rotation in radians
* @param worldScaleX - World scale X
* @param worldScaleY - World scale Y
*/
burst(
pool: ParticlePool,
count: number,
worldX: number,
worldY: number,
worldRotation: number = 0,
worldScaleX: number = 1,
worldScaleY: number = 1
): number {
let emitted = 0;
for (let i = 0; i < count; i++) {
const p = pool.spawn();
if (p) {
this._initParticle(p, worldX, worldY, worldRotation, worldScaleX, worldScaleY);
emitted++;
}
}
return emitted;
}
/**
* 重置发射器
* Reset emitter
*/
reset(): void {
this._emissionAccumulator = 0;
this._isEmitting = true;
}
private _initParticle(
p: Particle,
worldX: number,
worldY: number,
worldRotation: number = 0,
worldScaleX: number = 1,
worldScaleY: number = 1
): void {
const config = this.config;
// 获取形状偏移 | Get shape offset
const [ox, oy] = this._getShapeOffset();
// 应用旋转和缩放到发射偏移 | Apply rotation and scale to emission offset
// 先缩放,再旋转 | Scale first, then rotate
const scaledOx = ox * worldScaleX;
const scaledOy = oy * worldScaleY;
const cos = Math.cos(worldRotation);
const sin = Math.sin(worldRotation);
const rotatedOx = scaledOx * cos - scaledOy * sin;
const rotatedOy = scaledOx * sin + scaledOy * cos;
// 位置 | Position
p.x = worldX + rotatedOx;
p.y = worldY + rotatedOy;
// 生命时间 | Lifetime
p.lifetime = randomRange(config.lifetime.min, config.lifetime.max);
p.age = 0;
// 速度方向(应用世界旋转)| Velocity direction (apply world rotation)
const baseDir = config.direction + randomRange(-config.directionSpread / 2, config.directionSpread / 2);
const dir = baseDir + worldRotation;
const speed = randomRange(config.speed.min, config.speed.max);
// 速度也应用缩放(使用平均缩放)| Speed also applies scale (use average scale)
const avgScale = (worldScaleX + worldScaleY) / 2;
p.vx = Math.cos(dir) * speed * avgScale;
p.vy = Math.sin(dir) * speed * avgScale;
// 加速度(重力)| Acceleration (gravity)
p.ax = config.gravityX;
p.ay = config.gravityY;
// 旋转 | Rotation
p.rotation = randomRange(config.startRotation.min, config.startRotation.max);
p.angularVelocity = randomRange(config.angularVelocity.min, config.angularVelocity.max);
// 缩放(应用世界缩放)| Scale (apply world scale)
const baseScale = randomRange(config.startScale.min, config.startScale.max);
p.scaleX = baseScale * worldScaleX;
p.scaleY = baseScale * worldScaleY;
p.startScaleX = p.scaleX;
p.startScaleY = p.scaleY;
// 颜色 | Color
p.r = clamp(config.startColor.r + randomRange(-config.startColorVariance.r, config.startColorVariance.r), 0, 1);
p.g = clamp(config.startColor.g + randomRange(-config.startColorVariance.g, config.startColorVariance.g), 0, 1);
p.b = clamp(config.startColor.b + randomRange(-config.startColorVariance.b, config.startColorVariance.b), 0, 1);
p.alpha = clamp(config.startColor.a + randomRange(-config.startColorVariance.a, config.startColorVariance.a), 0, 1);
p.startR = p.r;
p.startG = p.g;
p.startB = p.b;
p.startAlpha = p.alpha;
}
private _getShapeOffset(): [number, number] {
const config = this.config;
switch (config.shape) {
case EmissionShape.Point:
return [0, 0];
case EmissionShape.Circle: {
// 填充圆形 | Filled circle
const angle = Math.random() * Math.PI * 2;
const radius = Math.random() * config.shapeRadius;
return [Math.cos(angle) * radius, Math.sin(angle) * radius];
}
case EmissionShape.Ring: {
// 圆环边缘 | Ring edge only
const angle = Math.random() * Math.PI * 2;
return [Math.cos(angle) * config.shapeRadius, Math.sin(angle) * config.shapeRadius];
}
case EmissionShape.Rectangle: {
// 填充矩形 | Filled rectangle
const x = randomRange(-config.shapeWidth / 2, config.shapeWidth / 2);
const y = randomRange(-config.shapeHeight / 2, config.shapeHeight / 2);
return [x, y];
}
case EmissionShape.Edge: {
// 矩形边缘 | Rectangle edge only
const perimeter = 2 * (config.shapeWidth + config.shapeHeight);
const t = Math.random() * perimeter;
const w = config.shapeWidth;
const h = config.shapeHeight;
if (t < w) {
// 上边 | Top edge
return [t - w / 2, h / 2];
} else if (t < w + h) {
// 右边 | Right edge
return [w / 2, h / 2 - (t - w)];
} else if (t < 2 * w + h) {
// 下边 | Bottom edge
return [w / 2 - (t - w - h), -h / 2];
} else {
// 左边 | Left edge
return [-w / 2, -h / 2 + (t - 2 * w - h)];
}
}
case EmissionShape.Line: {
const t = Math.random() - 0.5;
const cos = Math.cos(config.direction + Math.PI / 2);
const sin = Math.sin(config.direction + Math.PI / 2);
return [cos * config.shapeWidth * t, sin * config.shapeWidth * t];
}
case EmissionShape.Cone: {
const angle = config.direction + randomRange(-config.coneAngle / 2, config.coneAngle / 2);
const radius = Math.random() * config.shapeRadius;
return [Math.cos(angle) * radius, Math.sin(angle) * radius];
}
default:
return [0, 0];
}
}
}
function randomRange(min: number, max: number): number {
return min + Math.random() * (max - min);
}
function clamp(value: number, min: number, max: number): number {
return Math.max(min, Math.min(max, value));
}