feat(particle): 添加完整粒子系统和粒子编辑器 (#284)
* feat(editor-core): 添加用户系统自动注册功能 - IUserCodeService 新增 registerSystems/unregisterSystems/getRegisteredSystems 方法 - UserCodeService 实现系统检测、实例化和场景注册逻辑 - ServiceRegistry 在预览开始时注册用户系统,停止时移除 - 热更新时自动重新加载用户系统 - 更新 System 脚本模板添加 @ECSSystem 装饰器 * feat(editor-core): 添加编辑器脚本支持(Inspector/Gizmo) - registerEditorExtensions 实际注册用户 Inspector 和 Gizmo - 添加 unregisterEditorExtensions 方法 - ServiceRegistry 在项目加载时编译并加载编辑器脚本 - 项目关闭时自动清理编辑器扩展 - 添加 Inspector 和 Gizmo 脚本创建模板 * feat(particle): 添加粒子系统和粒子编辑器 新增两个包: - @esengine/particle: 粒子系统核心库 - @esengine/particle-editor: 粒子编辑器 UI 粒子系统功能: - ECS 组件架构,支持播放/暂停/重置控制 - 7种发射形状:点、圆、环、矩形、边缘、线、锥形 - 5个动画模块:颜色渐变、缩放曲线、速度控制、旋转、噪声 - 纹理动画模块支持精灵表动画 - 3种混合模式:Normal、Additive、Multiply - 11个内置预设:火焰、烟雾、爆炸、雨、雪等 - 对象池优化,支持粒子复用 编辑器功能: - 实时 Canvas 预览,支持全屏和鼠标跟随 - 点击触发爆发效果(用于测试爆炸类特效) - 渐变编辑器:可视化颜色关键帧编辑 - 曲线编辑器:支持缩放曲线和缓动函数 - 预设浏览器:快速应用内置预设 - 模块开关:独立启用/禁用各个模块 - Vector2 样式输入(重力 X/Y) * feat(particle): 完善粒子系统核心功能 1. Burst 定时爆发系统 - BurstConfig 接口支持时间、数量、循环次数、间隔 - 运行时自动处理定时爆发 - 支持无限循环爆发 2. 速度曲线模块 (VelocityOverLifetimeModule) - 6种曲线类型:Constant、Linear、EaseIn、EaseOut、EaseInOut、Custom - 自定义关键帧曲线支持 - 附加速度 X/Y - 轨道速度和径向速度 3. 碰撞边界模块 (CollisionModule) - 矩形和圆形边界类型 - 3种碰撞行为:Kill、Bounce、Wrap - 反弹系数和最小速度阈值 - 反弹时生命损失 * feat(particle): 添加力场模块、碰撞模块和世界/本地空间支持 - 新增 ForceFieldModule 支持风力、吸引点、漩涡、湍流四种力场类型 - 新增 SimulationSpace 枚举支持世界空间和本地空间切换 - ParticleSystemComponent 集成力场模块和空间模式 - 粒子编辑器添加 Collision 和 ForceField 模块的 UI 编辑支持 - 新增 Vortex、Leaves、Bouncing 三个预设展示新功能 - 编辑器预览实现完整的碰撞和力场效果 * fix(particle): 移除未使用的 transform 循环变量
This commit is contained in:
211
packages/particle/src/loaders/ParticleLoader.ts
Normal file
211
packages/particle/src/loaders/ParticleLoader.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* 粒子效果资源加载器
|
||||
* Particle effect asset loader
|
||||
*/
|
||||
|
||||
import type {
|
||||
IAssetLoader,
|
||||
IAssetContent,
|
||||
IAssetParseContext,
|
||||
AssetContentType
|
||||
} from '@esengine/asset-system';
|
||||
import { EmissionShape, type ColorValue } from '../ParticleEmitter';
|
||||
import { ParticleBlendMode } from '../ParticleSystemComponent';
|
||||
|
||||
/**
|
||||
* 粒子资产类型常量
|
||||
* Particle asset type constant
|
||||
*/
|
||||
export const ParticleAssetType = 'particle';
|
||||
|
||||
/**
|
||||
* 粒子模块配置
|
||||
* Particle module configuration
|
||||
*/
|
||||
export interface IParticleModuleConfig {
|
||||
/** 模块类型 | Module type */
|
||||
type: string;
|
||||
/** 是否启用 | Enabled */
|
||||
enabled: boolean;
|
||||
/** 模块参数 | Module parameters */
|
||||
params: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 粒子效果资源数据接口
|
||||
* Particle effect asset data interface
|
||||
*/
|
||||
export interface IParticleAsset {
|
||||
/** 资源版本 | Asset version */
|
||||
version: number;
|
||||
/** 效果名称 | Effect name */
|
||||
name: string;
|
||||
/** 效果描述 | Effect description */
|
||||
description?: string;
|
||||
|
||||
// 基础属性 | Basic properties
|
||||
/** 最大粒子数 | Maximum particles */
|
||||
maxParticles: number;
|
||||
/** 是否循环 | Looping */
|
||||
looping: boolean;
|
||||
/** 持续时间 | Duration in seconds */
|
||||
duration: number;
|
||||
/** 播放速度 | Playback speed */
|
||||
playbackSpeed: number;
|
||||
/** 启动时自动播放 | Auto play on start */
|
||||
playOnAwake: boolean;
|
||||
|
||||
// 发射属性 | Emission properties
|
||||
/** 发射速率 | Emission rate */
|
||||
emissionRate: number;
|
||||
/** 发射形状 | Emission shape */
|
||||
emissionShape: EmissionShape;
|
||||
/** 形状半径 | Shape radius */
|
||||
shapeRadius: number;
|
||||
/** 形状宽度 | Shape width */
|
||||
shapeWidth: number;
|
||||
/** 形状高度 | Shape height */
|
||||
shapeHeight: number;
|
||||
/** 圆锥角度 | Cone angle */
|
||||
shapeAngle: number;
|
||||
|
||||
// 粒子属性 | Particle properties
|
||||
/** 生命时间最小值 | Lifetime min */
|
||||
lifetimeMin: number;
|
||||
/** 生命时间最大值 | Lifetime max */
|
||||
lifetimeMax: number;
|
||||
/** 速度最小值 | Speed min */
|
||||
speedMin: number;
|
||||
/** 速度最大值 | Speed max */
|
||||
speedMax: number;
|
||||
/** 发射方向(度数)| Direction in degrees */
|
||||
direction: number;
|
||||
/** 方向扩散(度数)| Direction spread in degrees */
|
||||
directionSpread: number;
|
||||
/** 缩放最小值 | Scale min */
|
||||
scaleMin: number;
|
||||
/** 缩放最大值 | Scale max */
|
||||
scaleMax: number;
|
||||
/** 重力 X | Gravity X */
|
||||
gravityX: number;
|
||||
/** 重力 Y | Gravity Y */
|
||||
gravityY: number;
|
||||
|
||||
// 颜色属性 | Color properties
|
||||
/** 起始颜色 | Start color */
|
||||
startColor: ColorValue;
|
||||
/** 起始透明度 | Start alpha */
|
||||
startAlpha: number;
|
||||
/** 结束透明度 | End alpha */
|
||||
endAlpha: number;
|
||||
/** 结束缩放 | End scale */
|
||||
endScale: number;
|
||||
|
||||
// 渲染属性 | Rendering properties
|
||||
/** 粒子大小 | Particle size */
|
||||
particleSize: number;
|
||||
/** 混合模式 | Blend mode */
|
||||
blendMode: ParticleBlendMode;
|
||||
/** 排序顺序 | Sorting order */
|
||||
sortingOrder: number;
|
||||
/** 纹理路径 | Texture path */
|
||||
texture?: string;
|
||||
|
||||
// 模块配置 | Module configurations
|
||||
/** 模块列表 | Module list */
|
||||
modules?: IParticleModuleConfig[];
|
||||
|
||||
// 纹理动画(可选)| Texture animation (optional)
|
||||
/** 纹理图集列数 | Texture sheet columns */
|
||||
textureTilesX?: number;
|
||||
/** 纹理图集行数 | Texture sheet rows */
|
||||
textureTilesY?: number;
|
||||
/** 动画帧率 | Animation frame rate */
|
||||
textureAnimationFPS?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建默认粒子资源数据
|
||||
* Create default particle asset data
|
||||
*/
|
||||
export function createDefaultParticleAsset(name: string = 'New Particle'): IParticleAsset {
|
||||
return {
|
||||
version: 1,
|
||||
name,
|
||||
description: '',
|
||||
|
||||
maxParticles: 100,
|
||||
looping: true,
|
||||
duration: 5,
|
||||
playbackSpeed: 1,
|
||||
playOnAwake: true,
|
||||
|
||||
emissionRate: 10,
|
||||
emissionShape: EmissionShape.Point,
|
||||
shapeRadius: 0,
|
||||
shapeWidth: 0,
|
||||
shapeHeight: 0,
|
||||
shapeAngle: 30,
|
||||
|
||||
lifetimeMin: 1,
|
||||
lifetimeMax: 2,
|
||||
speedMin: 50,
|
||||
speedMax: 100,
|
||||
direction: 90,
|
||||
directionSpread: 30,
|
||||
scaleMin: 1,
|
||||
scaleMax: 1,
|
||||
gravityX: 0,
|
||||
gravityY: 0,
|
||||
|
||||
startColor: { r: 1, g: 1, b: 1, a: 1 },
|
||||
startAlpha: 1,
|
||||
endAlpha: 0,
|
||||
endScale: 1,
|
||||
|
||||
particleSize: 8,
|
||||
blendMode: ParticleBlendMode.Normal,
|
||||
sortingOrder: 0,
|
||||
|
||||
modules: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 粒子效果加载器实现
|
||||
* Particle effect loader implementation
|
||||
*/
|
||||
export class ParticleLoader implements IAssetLoader<IParticleAsset> {
|
||||
readonly supportedType = ParticleAssetType;
|
||||
readonly supportedExtensions = ['.particle', '.particle.json'];
|
||||
readonly contentType: AssetContentType = 'text';
|
||||
|
||||
/**
|
||||
* 从文本内容解析粒子资源
|
||||
* Parse particle asset from text content
|
||||
*/
|
||||
async parse(content: IAssetContent, _context: IAssetParseContext): Promise<IParticleAsset> {
|
||||
if (!content.text) {
|
||||
throw new Error('Particle content is empty');
|
||||
}
|
||||
|
||||
const jsonData = JSON.parse(content.text) as IParticleAsset;
|
||||
|
||||
// 验证必要字段 | Validate required fields
|
||||
if (jsonData.maxParticles === undefined) {
|
||||
throw new Error('Invalid particle format: missing maxParticles');
|
||||
}
|
||||
|
||||
// 填充默认值 | Fill default values
|
||||
const defaults = createDefaultParticleAsset();
|
||||
return { ...defaults, ...jsonData };
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放已加载的资源
|
||||
* Dispose loaded asset
|
||||
*/
|
||||
dispose(asset: IParticleAsset): void {
|
||||
(asset as any).modules = null;
|
||||
}
|
||||
}
|
||||
7
packages/particle/src/loaders/index.ts
Normal file
7
packages/particle/src/loaders/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export {
|
||||
ParticleLoader,
|
||||
ParticleAssetType,
|
||||
createDefaultParticleAsset,
|
||||
type IParticleAsset,
|
||||
type IParticleModuleConfig
|
||||
} from './ParticleLoader';
|
||||
Reference in New Issue
Block a user