particle-modifier

Cocos Creator 2.4.x 粒子系统修饰器开发框架。用于创建、使用和调试自定义粒子修饰器,支持修改粒子位置、颜色、大小、旋转、速度等属性。当需要在 Cocos Creator 中实现复杂的粒子效果控制时使用此技能。

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "particle-modifier" with this command: npx skills add 958877748/skills/958877748-skills-particle-modifier

粒子修饰器系统开发指南

概述

本技能提供 Cocos Creator 2.4.x 粒子系统的自定义修饰器开发框架。通过编写修饰器类,可以精确控制每个粒子的行为,实现复杂的视觉效果。

核心文件结构

项目根目录/
├── ParticleModifier.ts           # 粒子修饰器胶水组件(拦截粒子系统更新)
├── ParticleModifierBase.ts       # 修饰器基类(提供钩子方法)
├── SKILL.md                      # 本技能文档
├── js/                           # 引擎核心源码(Cocos Creator 2.4.13)
│   ├── CCParticleAsset.js        # 粒子资源类
│   ├── CCParticleSystem.js       # 粒子系统组件(主组件)
│   ├── particle-simulator.js     # 粒子模拟器(核心逻辑)
│   └── particle-system-assembler.js  # 渲染汇编器
└── examples/
    ├── ColorFadeModifier.ts      # 颜色渐变修饰器示例
    ├── GravityModifier.ts        # 重力修饰器示例
    └── LeafRotationModifier.ts   # 叶子旋转修饰器示例

快速开始

步骤 1:复制核心文件

ParticleModifier.tsParticleModifierBase.ts 复制到项目的任意目录(建议放在 assets/scripts/ 下)

步骤 2:创建自定义修饰器

创建继承自 ParticleModifierBase 的新类,实现 onParticleUpdate 方法

步骤 3:在编辑器中配置

  1. 在粒子系统节点上添加 ParticleModifier 组件
  2. 添加自定义修饰器组件
  3. 配置参数并运行

修饰器开发模板

基础模板

import { IParticle } from "./ParticleModifier";
import ParticleModifierBase from "./ParticleModifierBase";

const { ccclass, property } = cc._decorator;

@ccclass
export class MyCustomModifier extends ParticleModifierBase {
    @property({ tooltip: '执行优先级(数值越小越先执行)' })
    priority: number = 0;

    @property({ tooltip: '属性说明' })
    myProperty: number = 1.0;

    onParticleEmit(particle: IParticle, system: cc.ParticleSystem): void {
        // 粒子发射时调用(可选)
        // 初始化粒子自定义数据
    }

    onParticleUpdate(particle: IParticle, dt: number, system: cc.ParticleSystem): void {
        // 粒子每帧更新时调用(必需)
        // 更新粒子状态
    }
}

IParticle 接口

interface IParticle {
    // 位置
    pos: cc.Vec2;                    // 当前位置(相对于发射器)
    startPos: cc.Vec2;               // 起始位置
    drawPos: cc.Vec2;                // 绘制位置

    // 颜色
    color: cc.Color;                 // 当前颜色
    deltaColor: { r, g, b, a };      // 颜色变化率
    preciseColor: { r, g, b, a };    // 精确颜色(浮点)

    // 大小
    size: number;                    // 当前大小
    deltaSize: number;               // 大小变化率
    aspectRatio: number;             // 宽高比

    // 旋转
    rotation: number;                // 当前旋转角度(度)
    deltaRotation: number;           // 旋转变化率(度/秒)

    // 生命周期
    timeToLive: number;              // 剩余生命周期(秒)

    // Mode A: 重力模式
    dir: cc.Vec2;                    // 速度向量
    radialAccel: number;             // 径向加速度
    tangentialAccel: number;         // 切向加速度

    // Mode B: 半径模式
    angle: number;                   // 当前角度(弧度)
    degreesPerSecond: number;        // 角速度
    radius: number;                  // 当前半径
    deltaRadius: number;             // 半径变化率
}

注意:IParticle 接口可以被扩展以存储自定义数据,但必须在 onParticleEmit 中初始化。

技术原理

粒子修饰器拦截机制

粒子修饰器系统通过以下方式拦截粒子更新:

  1. 获取粒子系统引用

    const simulator = (system as any)._simulator;
    
  2. 保存原始函数

    this._originalEmitParticle = simulator.emitParticle;
    this._originalStep = simulator.step;
    
  3. 覆盖关键函数

    simulator.emitParticle = (pos: cc.Vec2) => {
        this._originalEmitParticle.call(simulator, pos);
        this._onParticleEmit(particle);
    };
    
    simulator.step = (dt: number) => {
        this._originalStep.call(simulator, dt);
        this._onParticleUpdate(particle, dt);
    };
    

aspectRatio 处理逻辑

引擎底层的 updateParticleBuffer 函数(js/particle-simulator.js:257):

let aspectRatio = particle.aspectRatio;
aspectRatio > 1 ? (height = width / aspectRatio) : (width = height * aspectRatio);

这就是长方形图片旋转跳变的根本原因!当 aspectRatio 从 >1 变到 <1 时,引擎会切换锚点。

常见修饰器模式

1. 位置修改

onParticleUpdate(particle: IParticle, dt: number, system: cc.ParticleSystem): void {
    // X 轴摆动
    particle.pos.x += Math.sin(Date.now() / 1000) * 10 * dt;
}

2. 颜色渐变

// 需要先定义扩展接口
interface IColorFadeOptions extends IParticle {
    maxTimeToLive: number;  // 记录初始生命周期
}

onParticleEmit(particle: IColorFadeOptions, system: cc.ParticleSystem): void {
    particle.maxTimeToLive = particle.timeToLive;
}

onParticleUpdate(particle: IColorFadeOptions, dt: number, system: cc.ParticleSystem): void {
    const lifeRatio = 1 - (particle.timeToLive / particle.maxTimeToLive);
    particle.color.r = Math.floor(255 * lifeRatio);
    particle.color.g = Math.floor(255 * (1 - lifeRatio));
}

3. 大小呼吸

// 基于粒子生命周期计算呼吸效果
interface IBreathingSizeOptions extends IParticle {
    maxTimeToLive: number;  // 记录初始生命周期
    baseSize: number;       // 记录初始大小
}

onParticleEmit(particle: IBreathingSizeOptions, system: cc.ParticleSystem): void {
    particle.maxTimeToLive = particle.timeToLive;
    particle.baseSize = particle.size;
}

onParticleUpdate(particle: IBreathingSizeOptions, dt: number, system: cc.ParticleSystem): void {
    const lifeRatio = 1 - (particle.timeToLive / particle.maxTimeToLive);
    // 基于生命周期的呼吸效果,每个粒子有独立相位
    particle.size = particle.baseSize + Math.sin(lifeRatio * Math.PI * 4) * 20;
}

4. 3D 旋转(关键)

⚠️ 重要:aspectRatio 跳变问题

长方形图片旋转时会出现视觉跳变,因为引擎底层硬切换锚点:

  • aspectRatio > 1:宽度固定为 particle.size
  • aspectRatio < 1:高度固定为 particle.size

解决方案:长方形图片(AR > 1)只使用 X 轴旋转

interface ILeafRotationOptions extends IParticle {
    maxTimeToLive: number;           // 自定义:记录初始生命周期
    originalAspectRatio: number;     // 自定义:记录初始宽高比
    rotationAngle: number;           // 自定义:当前旋转角度
    rotationSpeed: number;           // 自定义:旋转速度
    randomOffset: number;            // 自定义:随机偏移
}

@ccclass
export class LeafRotationModifier extends ParticleModifierBase {
    @property({ tooltip: '最小旋转速度(圈/秒)' })
    minRotationSpeed: number = 0.5;

    @property({ tooltip: '最大旋转速度(圈/秒)' })
    maxRotationSpeed: number = 2.0;

    @property({ tooltip: '旋转轴(X=上下翻转,Y=左右翻转)' })
    rotationAxis: 'X' | 'Y' = 'X';

    onParticleEmit(particle: ILeafRotationOptions, system: cc.ParticleSystem): void {
        // ⚠️ 重要:必须在 onParticleEmit 中初始化自定义数据
        particle.maxTimeToLive = particle.timeToLive;
        particle.originalAspectRatio = particle.aspectRatio || 1.0;
        particle.rotationAngle = 0;

        const randomSpeed = this.minRotationSpeed + Math.random() * (this.maxRotationSpeed - this.minRotationSpeed);
        particle.rotationSpeed = Math.PI * 2 * randomSpeed;
        particle.randomOffset = Math.random() * Math.PI * 2;
    }

    onParticleUpdate(particle: ILeafRotationOptions, dt: number, system: cc.ParticleSystem): void {
        particle.rotationAngle += particle.rotationSpeed * dt;
        const currentAngle = particle.rotationAngle + particle.randomOffset;
        const cosValue = Math.abs(Math.cos(currentAngle));
        const safeCos = Math.max(0.01, cosValue);

        if (this.rotationAxis === 'X') {
            // X 轴旋转:宽度不变,高度缩放
            particle.aspectRatio = particle.originalAspectRatio / safeCos;
        } else {
            // Y 轴旋转:⚠️ 长方形图片会跳变
            particle.aspectRatio = particle.originalAspectRatio * safeCos;
        }
    }
}

使用建议:

  • 长方形图片(AR > 1):使用 rotationAxis = 'X'
  • 正方形图片(AR ≈ 1):可以使用任意轴
  • 配合粒子 Z 轴旋转:只做 X 轴 3D 翻转 + 粒子 rotation 属性

高级功能

访问顶点缓冲区

⚠️ 警告:此功能需要深入了解引擎顶点格式,仅用于高级场景

onParticleUpdate(particle: IParticle, dt: number, system: cc.ParticleSystem): void {
    const simulator = (system as any)._simulator;
    const buffer = system._assembler.getBuffer();
    const vbuf = buffer._vData;

    const particleIndex = simulator.particles.indexOf(particle);
    // vfmtPosUvColor 格式:每顶点 8 floats (pos:2 + uv:2 + color:4),每粒子 4 顶点 = 32 floats
    const FLOAT_PER_PARTICLE = 32;
    const offset = particleIndex * FLOAT_PER_PARTICLE;

    // 修改第一个顶点的 UV 坐标(offset + 8, offset + 9)
    // 第二个顶点:offset + 16, offset + 17
    // 第三个顶点:offset + 24, offset + 25
    // 第四个顶点:offset + 32, offset + 33
    vbuf[offset + 8] = 0.0;
    vbuf[offset + 9] = 0.0;

    // step 函数会自动上传数据,无需手动调用 uploadData
}

多修饰器协同

在编辑器中添加多个修饰器,按 priority 顺序执行(数值越小越先执行):

ParticleSystem
├── ParticleModifier (胶水组件,自动收集并排序子修饰器)
├── GravityModifier (priority: 0)       # 先应用重力
├── LeafRotationModifier (priority: 1) # 再应用 3D 旋转
└── ColorFadeModifier (priority: 2)    # 最后应用颜色渐变

执行顺序影响

  • 如果先应用颜色渐变,再应用 3D 旋转,旋转不会影响颜色
  • 优先级决定了修饰器的应用顺序,可能影响最终效果

调试技巧

查看粒子系统状态

⚠️ 性能警告:以下调试方法会产生大量日志,仅在开发环境使用,生产环境务必移除

onParticleUpdate(particle: IParticle, dt: number, system: cc.ParticleSystem): void {
    // 仅在每 100 帧输出一次,避免日志爆炸
    if (Date.now() % 100 < 16) {  // 约 6fps
        const simulator = (system as any)._simulator;
        console.log('粒子数量:', simulator.particles.length);
        console.log('激活状态:', simulator.active);
    }
}

监控 aspectRatio

if (particle.aspectRatio > 1.1 || particle.aspectRatio < 0.9) {
    console.log('aspectRatio 异常:', particle.aspectRatio);
}

性能分析

const startTime = Date.now();
// ... 修饰器逻辑
const endTime = Date.now();
if (endTime - startTime > 1) {
    console.warn('执行时间过长:', endTime - startTime, 'ms');
}

质量校验清单

在创建修饰器时,请确保满足以下标准:

  • 性能检查:避免在 onParticleUpdate 中创建新对象
  • 初始化检查:粒子自定义数据在 onParticleEmit 中初始化
  • 生命周期检查:不修改粒子数组,只修改单个粒子属性
  • 兼容性检查:确认粒子系统模式(Gravity 或 Radius)
  • aspectRatio 检查:长方形图片使用 X 轴旋转避免跳变
  • priority 设置:合理设置优先级控制执行顺序

常见问题与解决方案

问题 1:修饰器未生效

检查清单:

  • 是否添加了 ParticleModifier 组件?
  • 修饰器是否已添加到粒子系统节点?
  • priority 是否正确(按顺序执行)?

问题 2:性能问题

优化建议:

  • 减少粒子数量(建议 < 1000)
  • 简化修饰器逻辑
  • 避免复杂的数学运算

问题 3:长方形图片旋转跳变

解决方案:

  • 使用 rotationAxis = 'X'
  • 公式:aspectRatio = originalAspectRatio / cos

问题 4:粒子间交互需求

实现方法:

  • 在修饰器中维护粒子列表
  • onParticleUpdate 中遍历计算

问题 5:自定义数据丢失

解决方案:

  • 必须在 onParticleEmit 中初始化自定义数据
  • 使用 TypeScript 接口扩展 IParticle

参考资源

示例文件(位于本技能包的 examples/ 目录)

  • examples/ColorFadeModifier.ts - 颜色渐变
  • examples/GravityModifier.ts - 重力修改
  • examples/LeafRotationModifier.ts - 3D 旋转

注意:这些示例文件位于粒子修饰器技能包中,可参考实现方式

引擎源码(位于本技能包的 js/ 目录,供理解原理使用)

js/particle-simulator.js - 粒子模拟器

  • Particle 类定义:粒子对象结构,包含位置、颜色、大小、旋转、生命周期等属性
  • emitParticle 函数:发射新粒子,初始化粒子属性
  • step 函数:每帧更新所有粒子,包括发射、更新、回收
  • updateParticleBuffer 函数:更新顶点缓冲区,包含 aspectRatio 处理的关键逻辑(根据 aspectRatio 切换宽高锚点)
  • 对象池:粒子对象池管理,避免频繁创建销毁

js/CCParticleSystem.js - 粒子系统组件

  • 属性定义:粒子系统所有可配置属性(totalParticles、duration、emissionRate、life、startColor、endColor、gravity、speed 等)
  • _simulator 初始化:创建粒子模拟器实例
  • lateUpdate 函数:每帧调用模拟器的 step 方法
  • emitterMode:GRAVITY(重力模式)和 RADIUS(半径模式)
  • positionType:FREE(自由模式)、RELATIVE(相对模式)、GROUPED(整组模式)

js/particle-system-assembler.js - 渲染汇编器

  • getBuffer 函数:获取顶点缓冲区(QuadBuffer)
  • fillBuffers 函数:提交渲染数据到 GPU
  • 顶点格式:vfmtPosUvColor(位置 + UV + 颜色)
  • 每粒子顶点数:4 个顶点,6 个索引(2 个三角形)

js/CCParticleAsset.js - 粒子资源

  • spriteFrame 属性:粒子贴图资源
  • 支持格式:plist 格式的粒子配置文件

注意:这些是 Cocos Creator 引擎源码,位于技能包中供理解原理使用

最佳实践

  1. 优先使用 X 轴旋转:长方形图片避免跳变
  2. 避免对象创建:在 onParticleUpdate 中不创建新对象
  3. 合理控制粒子数:建议 < 1000 个粒子
  4. 使用 priority 控制顺序:修饰器按 priority 从小到大执行
  5. 在 onParticleEmit 初始化:粒子自定义数据在发射时初始化
  6. 扩展 IParticle 接口:使用 TypeScript 接口扩展自定义数据类型

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

bilibili-downloader

No summary provided by upstream source.

Repository SourceNeeds Review
General

groq-stt

No summary provided by upstream source.

Repository SourceNeeds Review
General

gitreset

No summary provided by upstream source.

Repository SourceNeeds Review
General

cocos-build

No summary provided by upstream source.

Repository SourceNeeds Review
particle-modifier | V50.AI