本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Unity作为一款强大的跨平台游戏开发引擎,广泛应用于2D、3D、VR及AR内容创作。其内置的粒子系统(ParticleSystem)是实现火焰、烟雾、爆炸、魔法等视觉特效的核心工具,能显著提升项目的沉浸感与表现力。本文围绕“Unity常用粒子特效”展开,系统介绍粒子系统的组件结构、形状控制、动画材质应用、生命周期管理、碰撞触发机制、分层遮罩技术,并涵盖脚本控制、性能优化、实例化处理以及Asset Store资源使用等关键内容。通过学习与实践,开发者可快速掌握高效创建与优化粒子特效的方法,提升项目视觉品质与运行效率。
Unity常用粒子特效

1. Unity粒子系统的核心架构与组件解析

Unity粒子系统采用模块化设计,其核心由多个功能组件协同工作。 ParticleSystem 组件作为容器,封装了发射、更新、渲染等生命周期管理逻辑。其中, Emitter 控制粒子生成频率与初始状态; Shape模块 定义发射几何区域,如球体、锥形或网格表面; Simulation Space 决定粒子运动是局部还是世界坐标系下进行。各模块通过数据流依次传递参数,形成完整的特效链条。在Inspector中可直观查看“Main”、“Emission”、“Shape”等子模块层级,每个模块对应特定职责,便于精细化调控。这种结构既保证灵活性,又为脚本化控制提供API支持,是实现复杂视觉效果的基础。

2. 粒子发射形态的设计与实现

在Unity中,粒子系统的视觉表现力很大程度上取决于其初始发射形态的构建。不同的发射形状不仅决定了粒子从何处生成、以何种方向飞出,更深刻影响着最终呈现的动态美感和物理合理性。无论是模拟火焰喷射、爆炸碎屑扩散,还是魔法光环环绕,精准控制发射几何结构是特效设计的第一步。Unity的 Shape Module (形状模块)提供了丰富的内置选项,允许开发者基于球体、锥形、网格表面等多种几何体定义粒子的出生位置与初速度方向。本章将系统性地解析这些发射类型背后的数学建模逻辑,并深入探讨如何通过参数调节、脚本驱动与多模块组合,实现高度可控且富有创意的发射行为。

2.1 发射形状的类型与几何特性

Unity中的粒子发射器通过 Shape 模块定义粒子的初始空间分布模式,这一模块本质上是一个几何采样器,负责在每一帧根据设定的概率密度函数从指定区域内随机选取发射点,并赋予相应的初始速度方向。理解每种形状的几何特性和内部参数的意义,是构建真实感或风格化特效的基础。

2.1.1 球体、半球与立方体发射:空间分布规律分析

最基础的三种体积型发射器为 球体(Sphere) 半球(Hemisphere) 立方体(Box) 。它们分别对应于三维空间中不同拓扑结构的粒子源区域。

  • 球体发射 适用于全方位均匀扩散的效果,如爆炸中心向外迸发的火花。其核心参数包括 Radius (半径),表示球体大小; Random Direction 决定粒子是否沿随机方向发射;若关闭,则所有粒子沿径向统一方向运动。
  • 半球发射 常用于地面喷发类效果(如火山灰、蒸汽上升)。它仅在Z轴正方向形成半个球面,避免粒子向下穿透地面。该模式特别适合配合重力使用,使粒子自然向上飘散后再下落。

  • 立方体发射 则提供了一个矩形空间内的均匀采样机制,适合模拟烟雾从房间弥漫、尘埃从箱体溢出等场景。其尺寸由 X/Y/Z 三个轴向长度决定,可拉伸成任意长方体。

下面通过一个代码示例展示如何通过C#脚本动态获取并修改当前粒子系统的形状类型及其参数:

using UnityEngine;
using System;

public class ShapeController : MonoBehaviour
{
    private ParticleSystem ps;
    private ParticleSystem.ShapeModule shapeModule;

    void Start()
    {
        ps = GetComponent<ParticleSystem>();
        shapeModule = ps.shape;

        // 设置为球体发射
        shapeModule.shapeType = ParticleSystemShapeType.Sphere;
        shapeModel.radius = 1.5f;               // 半径1.5单位
        shapeModule.randomDirectionAmount = 1.0f; // 完全随机方向
        shapeModule.sphericalDirectionAmount = 0.0f; // 非纯球向
    }

    void Update()
    {
        // 每隔3秒切换一次发射形状
        if (Time.time % 6 < 0.02f)
        {
            int rand = UnityEngine.Random.Range(0, 3);
            switch (rand)
            {
                case 0:
                    SetSphereEmission();
                    break;
                case 1:
                    SetHemisphereEmission();
                    break;
                case 2:
                    SetBoxEmission();
                    break;
            }
        }
    }

    void SetSphereEmission()
    {
        shapeModule.shapeType = ParticleSystemShapeType.Sphere;
        shapeModule.radius = 1.2f;
        Debug.Log("Switched to Sphere emission.");
    }

    void SetHemisphereEmission()
    {
        shapeModule.shapeType = ParticleSystemShapeType.Hemisphere;
        shapeModule.radius = 1.0f;
        shapeModule.alignToDirection = false;
        Debug.Log("Switched to Hemisphere emission.");
    }

    void SetBoxEmission()
    {
        shapeModule.shapeType = ParticleSystemShapeType.Box;
        shapeModule.scale = new Vector3(2.0f, 0.5f, 2.0f); // 扁平长方体
        Debug.Log("Switched to Box emission.");
    }
}
代码逻辑逐行解读与参数说明
  • 第4–5行引入必要命名空间: UnityEngine 为核心API, System 用于泛型与集合操作。
  • Start() 方法中获取组件引用,确保在运行初期完成初始化。
  • shapeModule.shapeType = ParticleSystemShapeType.Sphere; 明确设置当前发射器类型为球体。
  • radius 控制球体范围,值越大,粒子出生点越远离原点。
  • randomDirectionAmount = 1.0f 表示粒子初速度方向完全随机化;设为0则全部沿法线方向发射。
  • Update() 中利用时间取模实现周期性切换,增强可视化调试体验。
  • SetBoxEmission() 中使用 scale 向量调整立方体各轴尺寸,实现非对称发射区域。
形状类型 参数字段 默认值 作用描述
Sphere radius 1.0 定义球体半径
Hemisphere radius 1.0 半球半径,仅上半部分有效
Box scale.x/y/z 1.0/1.0/1.0 控制长宽高
flowchart TD
    A[启动游戏对象] --> B{获取ParticleSystem组件}
    B --> C[访问ShapeModule]
    C --> D[判断当前形状类型]
    D --> E[设置对应参数: radius/scale]
    E --> F[应用变化并刷新粒子流]
    F --> G[每3秒重新选择新形状]
    G --> D

上述流程图展示了脚本控制形状切换的整体执行路径。这种自动化测试方式有助于快速对比不同发射器在相同速率下的视觉差异。

2.1.2 锥形与环形喷射:角度控制与扩散行为建模

相较于均匀分布的体积发射器, 锥形(Cone) 环形(Donut) 更适用于定向喷射场景,例如火箭尾焰、探照灯光束或能量环波动。

锥形发射(Cone)

锥形发射器的核心参数为 Angle Radius

  • Angle 控制锥体张开的角度(单位:度),范围通常为0°~180°。当角度为0时,所有粒子沿轴向直线发射;随着角度增大,粒子逐渐向外扩散。
  • Radius 可选启用“Base Radius”,即在锥底添加一个圆形基座,使得粒子可以从整个圆面上发射而非仅顶点。
  • 支持两种模式:“In” 表示粒子从外向内汇聚,“Out” 表示从内向外发散。

典型应用场景:
- 小角度(5°~15°):激光束、高压水枪
- 大角度(60°以上):喷泉、火焰主体

环形发射(Donut)

环形发射器在XY平面上构造一个圆环,粒子从环周上均匀采样生成,初速度默认沿径向或Z轴方向。其关键优势在于能产生“空心”喷射效果,避免中心堆积。

主要参数:
- Radius :环的平均半径
- Arc :弧段占比(0~360°),可用于制作扇形喷口
- Speed Mode :可设置切向速度,实现旋转式喷射(如龙卷风边缘)

以下代码演示如何结合锥形与环形实现旋转能量束效果:

using UnityEngine;

public class RotatingBeamEmitter : MonoBehaviour
{
    public float rotationSpeed = 30f; // 度/秒
    private ParticleSystem ps;
    private ParticleSystem.ShapeModule shape;

    void Start()
    {
        ps = GetComponent<ParticleSystem>();
        shape = ps.shape;

        shape.shapeType = ParticleSystemShapeType.Cone;
        shape.angle = 10f;
        shape.radius = 0.2f;
        shape.rotation = new Vector3(0, 0, 0);
    }

    void Update()
    {
        // 绕Z轴旋转发射方向
        Quaternion rotZ = Quaternion.Euler(0, 0, Time.time * rotationSpeed);
        shape.rotation = rotZ.eulerAngles;

        // 动态调整弧度制造脉冲感
        float pulseArc = 360f * (0.5f + 0.5f * Mathf.Sin(Time.time * 4f));
        if (shape.shapeType == ParticleSystemShapeType.Donut)
        {
            shape.arc = pulseArc;
        }
    }
}
代码解释与扩展说明
  • 使用 Quaternion.Euler 构造绕Z轴的旋转四元数,再赋值给 shape.rotation 实现方向偏转。
  • pulseArc 利用正弦波调制弧度,在0°到360°之间振荡,模拟呼吸式闪烁环。
  • 若同时启用多个形状模块(见后文),可通过权重混合实现复合轨迹。
参数名 类型 范围 效果影响
Angle float 0~180° 决定锥体张角
Arc float 0~360° 控制环形覆盖角度
Rotation Vector3 欧拉角 改变发射基准方向
pie
    title 锥形 vs 环形适用场景比例
    “火焰喷射” : 35
    “光束追踪” : 20
    “能量环” : 25
    “特殊过渡” : 20

此饼图反映了在实际项目中各类定向发射器的应用频率统计,锥形仍占主导地位,但环形在科幻题材中有独特价值。

2.1.3 边缘发射与网格表面发射:复杂形状适配策略

当需要让粒子精确贴合某个不规则物体(如破损边缘、生物轮廓、建筑结构)时,标准几何体已无法满足需求。此时应采用 Edge(边缘) Mesh/MeshRenderer(网格) 发射模式。

Mesh 发射

支持从三角面、顶点或边沿采样粒子出生位置。常用配置如下:

  • Mesh Type : Vertex(顶点)、Triangle(面)、Edge(边)
  • Use Mesh Material Index : 是否按材质分区发射
  • Align to Surface : 粒子初速度是否垂直于表面

实战案例:让火焰沿着断裂的金属管道边缘燃烧。

using UnityEngine;

[RequireComponent(typeof(ParticleSystem))]
public class MeshEdgeEmitter : MonoBehaviour
{
    public MeshFilter sourceMeshFilter;
    public float emitFromEdgesOnly = 0.8f;

    private ParticleSystem ps;
    private ParticleSystem.ShapeModule shape;

    void Start()
    {
        if (!sourceMeshFilter) return;

        ps = GetComponent<ParticleSystem>();
        shape = ps.shape;

        shape.shapeType = ParticleSystemShapeType.Mesh;
        shape.mesh = sourceMeshFilter.sharedMesh;

        // 优先从边缘发射
        shape.meshShapeType = ParticleSystemMeshShapeType.Edge;
        shape.randomDirectionAmount = 0.3f;
        shape.sphericalDirectionAmount = 0.1f;

        // 对接外部控制接口
        StartCoroutine(AdjustOverTime());
    }

    System.Collections.IEnumerator AdjustOverTime()
    {
        while (true)
        {
            yield return new WaitForSeconds(2f);
            float noise = Mathf.PerlinNoise(Time.time * 0.5f, 0);
            shape.radius = Mathf.Lerp(0.1f, 0.4f, noise); // 微扰半径增加真实感
        }
    }
}
逻辑分析与参数详解
  • meshShapeType = Edge 强制只从模型边界线发射,适合裂痕特效。
  • PerlinNoise 引入程序化噪声调节 radius ,模拟火焰跳动感。
  • 若希望粒子沿法线方向弹出,需开启 alignToDirection = true 并禁用随机方向。
发射模式 数据来源 性能开销 典型用途
Vertex 模型顶点集 星点点缀
Triangle 面片中心 均匀覆盖表面
Edge 边缘线段 断口、闪电链
graph LR
    A[导入FBX模型] --> B[附加MeshFilter]
    B --> C[创建ParticleSystem]
    C --> D[设置Shape为Mesh]
    D --> E[选择Edge模式]
    E --> F[调节方向与噪声]
    F --> G[实时预览边缘火焰]

该流程图清晰表达了从资源准备到效果调试的完整工作链路,强调了数据依赖关系与模块协同的重要性。

3. 基于Sprite Sheet与材质系统的粒子动画构建

在现代游戏视觉特效开发中,静态粒子已难以满足复杂场景的表现需求。动态化的粒子动画不仅增强了画面的沉浸感,也显著提升了玩家对交互反馈的感知质量。Unity 提供了强大的 Sprite Sheet 支持机制,结合灵活的材质系统与着色器编程能力,开发者可以实现从帧动画到程序化外观变化的完整粒子表现体系。本章将深入剖析如何利用纹理图集驱动粒子帧动画、如何设计高性能且视觉丰富的自定义材质,并通过颜色渐变控制手段完成细腻的时间轴级视觉过渡。

Sprite 动画本质上是一种“时间-空间映射”技术:将多帧图像按规则排列于单张纹理上,再通过 UV 坐标偏移实现逐帧播放效果。这种技术广泛应用于爆炸、火焰、魔法技能等需要高帧率动态表现的粒子系统中。而材质系统则是连接渲染管线与视觉输出的核心桥梁,决定了粒子最终在屏幕上呈现的颜色混合方式、光照响应行为以及是否支持透明度混合或发光叠加等关键特性。因此,掌握 Sprite Sheet 与材质系统的协同工作原理,是构建高质量粒子动画的基础。

更重要的是,随着 Shader Graph 和 URP/HDRP 渲染管线的发展,传统固定功能着色器已被可编程图形逻辑所取代。这使得开发者不再局限于 Unity 内置的标准粒子着色器,而是能够根据项目风格定制独特的视觉语言——例如模拟水墨扩散、电弧闪烁、能量波动等非真实感渲染(NPR)效果。同时,结合 Color over Lifetime 等模块,还能实现随生命周期演进的颜色过渡与透明度调节,进一步增强动画的情感表达力。

以下章节将依次展开三个核心维度的技术细节:首先是 Sprite Sheet 的导入配置与帧序列控制逻辑;其次是自定义着色器的设计原则与实际编码实现;最后是颜色与透明度的曲线调控方法及其脚本扩展策略。整个流程贯穿资源准备、Shader 编写、材质绑定、粒子参数设置及运行时控制,形成一个闭环的动画构建链条。

3.1 Sprite Sheet动画的导入与帧序列配置

Sprite Sheet 技术的核心在于将一系列连续动作帧整合为一张二维纹理图集(Texture Atlas),并通过调整 UV 坐标来切换显示不同帧的内容。在 Unity 中,这一过程依赖于 Texture Import Settings 的精确配置与粒子系统的 Time over Lifetime 模块联动控制。正确设置图集切片参数不仅能确保动画流畅播放,还可避免因采样误差导致的画面撕裂或跳帧问题。

3.1.1 图集切片设置与UV偏移动画原理

当一张包含多个子图像的 PNG 文件被导入 Unity 后,默认会被识别为普通纹理。要启用 Sprite 切片功能,必须将其纹理类型(Texture Type)设为 Sprite (2D and UI) ,并选择 Sprite Mode Multiple 。随后点击 Sprite Editor 按钮进入切片界面,在此可通过自动分割(Automatic Slicing)或手动绘制(Custom Slicing)的方式划分各个帧区域。

假设我们有一张 512×512 像素的图集,包含 4 行 × 4 列共 16 帧火焰动画,每帧尺寸为 128×128 像素。此时需在 Sprite Editor 中设置 Grid Size 为 X=128, Y=128,Origin 设置为 Top Left(若源图以左上角为起点)。切片完成后保存,Unity 会为每一帧生成独立的 Sprite 资源。

参数 说明
Texture Type 必须设为 Sprite (2D and UI) 才能启用切片
Sprite Mode Multiple 允许同一纹理包含多个 Sprite
Pixels Per Unit 控制 Sprite 在世界单位中的缩放比例(如 100px = 1 unit)
Mesh Type Full Rect 或 Tight;Tight 可减少无效顶点数量
Border 若使用九宫格拉伸,可设置边缘像素保留不变形

切片完成后,这些 Sprite 将作为粒子系统的纹理输入源。Unity 粒子系统中的 Renderer 模块支持直接引用该图集作为 Texture Sheet Animation 使用。其底层机制是通过修改每个粒子的 UV 坐标来实现帧切换。具体而言,每个粒子拥有一个 [0,1] 区间内的归一化坐标 (u,v),代表当前应采样的纹理位置。当启用 Texture Sheet Animation 模块后,Unity 会依据设定的行列数自动计算每帧对应的 UV 偏移量。

例如,对于 4×4 的图集,第 n 帧(从 0 开始计数)的 UV 偏移公式如下:

int row = n / columns;        // 当前列
int col = n % columns;        // 当前行
Vector2 uvOffset = new Vector2(col / (float)columns, 1f - (row + 1) / (float)rows); // 注意Y轴翻转

注:由于 OpenGL 风格的纹理坐标系 Y 轴向上为正,而大多数图像编辑软件以左上角为原点向下递增,因此通常需要垂直翻转 Y 坐标。

该 UV 偏移值最终传递给 GPU,在着色器中用于定位正确的纹理区域进行采样。整个过程无需 CPU 实时干预,完全由 GPU 并行处理,极大提升了动画性能。

graph TD
    A[原始PNG图集] --> B{导入Unity}
    B --> C[设置Texture Type为Sprite]
    C --> D[进入Sprite Editor切片]
    D --> E[定义Grid Size和Origin]
    E --> F[保存生成多个Sprite]
    F --> G[应用至ParticleSystem]
    G --> H[启用Texture Sheet Animation模块]
    H --> I[GPU根据Index更新UV Offset]
    I --> J[Shader采样对应帧]

上述流程展示了从资源准备到渲染输出的完整数据流路径。值得注意的是,虽然 Sprite 切片发生在编辑期,但帧播放逻辑是在运行时由粒子系统动态调度的。这意味着即使所有帧都存在于同一张纹理上,也可以根据不同粒子的生命阶段独立播放任意帧序列,从而实现异步动画效果。

3.1.2 Time over Lifetime模块驱动帧播放节奏

一旦图集正确切片并关联至粒子系统,下一步便是控制动画的播放节奏。Unity 提供了 Texture Sheet Animation 模块专门用于管理帧序列播放,但它本身并不决定何时切换帧——真正的驱动力来自 Time over Lifetime 模块结合粒子自身的存活时间。

在 Particle System 的 Inspector 中启用 Texture Sheet Animation 后,主要配置项包括:

  • Tiles : 设置图集的列数(X)与行数(Y)
  • Animation : 可选 Whole Sheet(整张图循环)、Single Row(单行播放)或 Custom Row(指定某一行)
  • Frame Over Time : 控制帧索引随时间变化的曲线
  • Start Frame : 初始化时的起始帧(支持随机范围)

其中最关键的参数是 Frame Over Time ,它定义了一个从 0 到 1 的浮点曲线,表示粒子在其生命周期内应显示的帧位置。例如,若曲线为线性上升,则粒子会从第一帧匀速播放到最后帧;若为阶梯状曲线,则可实现定格动画效果。

// 示例:通过脚本动态设置 Frame Over Time 曲线
var textureSheetAnim = particleSystem.textureSheetAnimation;
textureSheetAnim.enabled = true;
textureSheetAnim.tilesX = 4;
textureSheetAnim.tilesY = 4;

AnimationCurve frameCurve = new AnimationCurve();
frameCurve.AddKey(0f, 0f);     // 0% 生命周期 → 第0帧
frameCurve.AddKey(1f, 15f);    // 100% 生命周期 → 第15帧

textureSheetAnim.frameOverTime = frameCurve;

参数说明
- tilesX/tilesY :必须与图集实际行列一致,否则会导致错位。
- frameOverTime :传入的值是帧索引(非归一化),范围 [0, totalFrames-1]。
- 若启用了 Random Between Two Animations ,则需同时设置两个曲线并分配权重。

该代码段展示了如何通过 C# 脚本动态构造帧播放曲线。相比在编辑器中手动绘制,程序化生成更适合需要差异化动画的行为,比如让每个粒子以不同速度播放火焰动画,增强自然感。

此外,还可以结合 startFrame 实现“随机起始帧”效果,使一批粒子不会同步开始动画,避免机械重复感:

textureSheetAnim.startFrame = new ParticleSystem.MinMaxCurve(0f, 15f);

此设置意味着每个新发射的粒子将在 0~15 帧之间随机选择起始位置,非常适合模拟火花四溅、烟雾飘散等非同步现象。

需要注意的是, Frame Over Time 曲线的时间轴对应的是粒子的标准化生存时间(即 currentLifetime / totalLifetime ),而非绝对秒数。因此即便粒子寿命长短不一,动画仍能保持相对一致性。

3.1.3 动画循环模式与随机起始帧技巧

Unity 粒子系统提供了多种动画循环策略,直接影响用户体验的真实感与性能消耗。常见的模式包括:

模式 行为描述 适用场景
Loop Wire 循环播放整张图集 连续燃烧、水流
Ping Pong 正播一次后倒播一次 生物呼吸、脉动光效
Clamp 播放到最后一帧停止 爆炸一次性动画
Single Row + Random Row 在指定行内随机选取并播放 多状态怪物死亡特效

通过合理选择模式,可以在不增加额外纹理开销的前提下丰富视觉多样性。

更高级的应用是结合脚本实现条件触发式帧跳转。例如,在检测到碰撞事件后立即跳转至“破碎”帧序列:

public class ParticleFrameController : MonoBehaviour
{
    private ParticleSystem ps;
    private ParticleSystem.TextureSheetAnimationModule sheetAnim;

    void Start()
    {
        ps = GetComponent<ParticleSystem>();
        sheetAnim = ps.textureSheetAnimation;
    }

    public void TriggerBreakEffect()
    {
        var main = ps.main;
        main.duration = 0.5f; // 缩短生命周期

        AnimationCurve breakCurve = new AnimationCurve();
        breakCurve.AddKey(0.0f, 8f);   // 起始于中间帧
        breakCurve.AddKey(0.3f, 12f);  // 快速推进
        breakCurve.AddKey(1.0f, 15f);  // 结束于末尾

        sheetAnim.frameOverTime = breakCurve;
        ps.Play();
    }
}

逻辑分析
- TriggerBreakEffect() 方法可在外部调用(如 OnTriggerEnter 时)。
- 修改 duration 可强制粒子快速结束,配合新曲线实现瞬态特效。
- 新的 frameOverTime 曲线跳过了前几帧,直接进入“破裂”阶段,提升响应速度。

此类技术常用于 RPG 游戏中的技能命中反馈,或射击游戏中子弹击中墙体产生的碎屑动画。

综上所述,Sprite Sheet 动画不仅是简单的帧切换,更是时间控制、UV 映射与生命周期管理的综合体现。通过精细化调节切片参数、帧播放曲线与起始偏移,开发者能够在有限资源下创造出极具表现力的动态粒子效果。

3.2 自定义材质与着色器集成

材质系统是决定粒子视觉呈现质量的关键环节。默认的 Standard Particle Shader 虽然通用性强,但在特定艺术风格下往往显得单调。通过创建自定义着色器,不仅可以实现 Alpha 混合、加法混合等基础透明效果,还能引入噪声纹理、扭曲变形、边缘光等高级视觉特征,显著提升特效的艺术层次。

3.2.1 创建支持Alpha Blend与Additive混合的Shader

Unity 默认提供两种常用粒子着色器:
- Particles/Standard Surface :基于 PBR 的表面着色器,适合与光照交互
- Particles/Additive :简单加法混合,常用于火焰、光晕

但对于多数特效而言,推荐使用 Transparent Cutout Custom Unlit Shader 以获得更高自由度。

以下是 HLSL 编写的最小化透明混合着色器示例:

Shader "Custom/Particle_AlphaBlend"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        _Cutoff ("Alpha Cutoff", Range(0,1)) = 0.5
    }
    SubShader
    {
        Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" }
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.color = v.color * _Color;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv) * i.color;
                clip(col.a - _Cutoff); // 支持Alpha裁剪
                return col;
            }
            ENDCG
        }
    }
}

参数说明
- ZWrite Off :关闭深度写入,防止半透粒子遮挡后续物体
- Blend SrcAlpha OneMinusSrcAlpha :标准透明混合模式
- clip(col.a - _Cutoff) :实现 Alpha Test,剔除低于阈值的像素
- _Color :允许在材质面板中调整整体色调

此着色器兼容粒子系统的顶点颜色输出(由 Color over Lifetime 模块生成),并支持纹理采样与着色叠加,适用于大多数 2D 粒子动画。

3.2.2 利用Noise纹理增强粒子视觉细节层次

为了打破规则几何形态带来的机械感,可在着色器中引入噪声纹理(Noise Map)进行扰动。常见做法是使用 Perlin Noise 或 Worley Noise 作为采样偏移源:

sampler2D _NoiseTex;
float _NoiseScale;
float _NoiseSpeed;

v2f vert(appdata v)
{
    float2 noiseUV = v.uv * _NoiseScale + _Time.y * _NoiseSpeed;
    float noise = tex2Dlod(_NoiseTex, float4(noiseUV,0,0)).r;
    v.vertex.y += noise * 0.1; // Y方向轻微抖动
    // 继续原有变换...
}

该代码片段在顶点着色器中读取噪声纹理并对顶点进行微小扰动,模拟火焰跳动感或烟雾流动。

噪声类型 特性 应用场景
Value Noise 快速生成,块状明显 移动端轻量特效
Perlin Noise 平滑连续,方向性强 云层、水流
Simplex Noise 高维高效,无网格痕迹 高端PC/NPR风格

结合滚动 UV 技术,还可实现自传播式波纹动画:

graph LR
    A[Time] --> B[UV Offset += Speed * DeltaTime]
    B --> C[Sample Noise Texture]
    C --> D[Apply to Position/Color/Alpha]
    D --> E[Output Final Vertex/Fragment]

3.2.3 Shader Graph构建可编程粒子外观效果

对于不熟悉 HLSL 的美术人员,Shader Graph 是理想的可视化着色器创作工具。通过节点连接即可实现复杂的粒子外观逻辑。

典型结构如下:
- Master Stack : 使用 Particle Additive Unlit 主节点
- UV Animation : 添加 Time 节点驱动 Tiling & Offset
- Color Ramp : 连接 Gradient 节点实现色相渐变
- Distortion : 使用 Panosphere 或 Wave 节点制造扭曲

示例:创建一个脉冲发光粒子
- 输入:Time → Sine 波形 → Multiply 强度 → Emission 输入
- 输出:随时间周期性增强亮度,模拟心跳光效

Shader Graph 导出的着色器可直接赋给粒子材质,极大降低开发门槛。

3.3 粒子颜色与透明度的渐变控制

3.3.1 Color over Lifetime曲线编辑技巧

Color over Lifetime 模块允许基于粒子存活时间动态改变颜色。典型应用场景包括:

  • 火焰:黄 → 橙 → 红 → 黑(熄灭)
  • 烟雾:白 → 灰 → 透明
  • 魔法轨迹:蓝 → 紫 → 消失

在 Inspector 中可拖拽添加关键点,形成平滑或锐利过渡曲线。建议使用贝塞尔手柄精细调节斜率,避免突兀跳变。

3.3.2 Gradient资源复用与脚本化生成方法

可通过脚本预生成常用渐变:

public Gradient CreateFireGradient()
{
    Gradient grad = new Gradient();
    GradientColorKey[] colors = {
        new GradientColorKey(Color.yellow, 0f),
        new GradientColorKey(Color.red, 0.7f),
        new GradientColorKey(Color.black, 1f)
    };
    GradientAlphaKey[] alphas = {
        new GradientAlphaKey(1f, 0f),
        new GradientAlphaKey(0f, 1f)
    };
    grad.SetKeys(colors, alphas);
    return grad;
}

可存储为 ScriptableObject 资源便于团队共享。

3.3.3 实现淡入淡出、闪烁与变色过渡特效

结合 MinMaxGradient 与脚本定时器,可实现霓虹灯式闪烁:

void Update()
{
    var colorModule = ps.colorOverLifetime;
    float pulse = Mathf.PingPong(Time.time * 2, 1);
    colorModule.color = new ParticleSystem.MinMaxGradient(
        Color.Lerp(Color.clear, Color.cyan, pulse)
    );
}

利用 PingPong 函数实现呼吸式明暗交替。

此类技术广泛用于警报、充能、危险提示等 UI 相关特效中。

4. 粒子行为控制与交互响应机制

在现代游戏开发中,粒子系统已不仅仅是视觉装饰的附属品,而是成为实现动态反馈、环境互动和沉浸式体验的核心技术之一。Unity 的粒子系统通过高度可编程的行为模块与事件驱动机制,使得开发者能够精确控制每一个粒子从诞生到消亡的完整生命周期,并赋予其对物理世界或其他游戏对象的响应能力。本章将深入探讨粒子行为的精细化调控方式,涵盖运动动力学参数调优、碰撞检测系统的构建以及运行时脚本化干预策略,揭示如何通过组合多个行为模块来实现复杂且真实的特效逻辑。

粒子行为的塑造不仅依赖于初始发射形态(如第三章所述),更关键的是在其生命周期内的持续演化过程。这一过程由多个“Over Lifetime”模块共同作用,包括速度变化、加速度影响、重力修正、外部力场介入等。同时,当粒子进入场景中的特定区域或与其他物体发生接触时,需要触发相应的反应逻辑——例如爆炸碎片飞溅、魔法命中目标后产生伤害判定、火焰遇到墙壁后的反弹与扩散等。这些交互行为的实现离不开 Unity 提供的 Collision 与 Trigger 模块,它们为粒子赋予了感知环境的能力。

此外,随着项目复杂度提升,静态配置已无法满足需求,越来越多的特效需要根据游戏状态进行动态调整。比如技能充能阶段逐渐增强喷射强度,或者敌人死亡瞬间触发一次性的大规模粒子爆发。这就要求开发者掌握通过 C# 脚本访问并修改粒子系统内部模块的方法,实现在运行时对发射速率、颜色渐变、生命周期等核心属性的实时操控。这种脚本化控制能力极大提升了特效的表现灵活性和上下文适应性。

整个粒子行为控制系统呈现出典型的“数据流+事件驱动”架构特征:主模块定义基础行为模式,子模块按时间轴或空间条件施加影响,而脚本则作为高层控制器介入调节。理解这一架构有助于开发者设计出既稳定又富有表现力的特效体系。接下来的内容将从最基础的生命期参数协同开始,逐步展开至高级交互与程序化控制,形成一条由内而外、由静到动的技术演进路径。

4.1 生命周期核心参数调优

粒子系统的动态表现本质上是由其在时间维度上的演化规律所决定的。要实现自然流畅的运动轨迹,必须合理配置一系列与生命周期密切相关的参数。其中, Start Lifetime (起始寿命)和 Start Speed (起始速度)构成了所有后续行为的基础;而 Velocity over Lifetime External Forces Gravity Modifier 则进一步丰富了运动的多样性与真实性。这些模块之间的协同作用决定了粒子是轻盈飘散、猛烈喷发还是受控飞行。

4.1.1 Start Lifetime与Start Speed的协同作用

Start Lifetime 定义了每个粒子存在的时间长度,单位为秒。它可以设置为恒定值、随机区间或曲线函数输出。较长的生命周期适合表现缓慢漂浮的效果(如烟雾、尘埃),而较短的生命周期则适用于瞬时爆发类效果(如火花、闪光)。与此对应的 Start Speed 控制粒子出生时的初速度大小,同样支持常量、范围或曲线形式。

二者之间的关系直接影响粒子的整体覆盖范围与密度分布:

  • Start Lifetime 长但 Start Speed 小,则粒子移动缓慢,在局部聚集,形成浓密云团;
  • 若两者都大,则粒子快速飞离源头并在空中停留较久,易造成稀疏感;
  • Lifetime 短但 Speed 大,则粒子虽快但很快消失,适合短促冲击波效果。

下面是一个典型的配置示例代码片段,用于通过脚本动态设置主模块中的这两个参数:

using UnityEngine;
using System.Collections;

public class ParticleLifetimeController : MonoBehaviour
{
    public ParticleSystem particleSystem;
    private ParticleSystem.MainModule mainModule;

    void Start()
    {
        mainModule = particleSystem.main;

        // 设置生命周期:2.0 ~ 3.5 秒之间随机
        mainModule.startLifetime = new ParticleSystem.MinMaxCurve(2.0f, 3.5f);

        // 设置初始速度:3.0 ~ 6.0 单位/秒
        mainModule.startSpeed = new ParticleSystem.MinMaxCurve(3.0f, 6.0f);

        // 启用随机方向(否则默认沿Z轴)
        mainModule.startRotationX = new ParticleSystem.MinMaxCurve(-180f, 180f);
        mainModule.startRotationY = new ParticleSystem.MinMaxCurve(-180f, 180f);
        mainModule.startRotationZ = new ParticleSystem.MinMaxCurve(-180f, 180f);
        mainModule.randomizeRotationDirection = 1.0f;
    }
}
代码逻辑逐行解读:
  1. mainModule = particleSystem.main;
    获取粒子系统的主模块引用,这是大多数全局参数的控制入口。
  2. mainModule.startLifetime = new ParticleSystem.MinMaxCurve(2.0f, 3.5f);
    使用 MinMaxCurve 类型设置一个随机范围的生命周期,表示每个粒子存活时间为 2 到 3.5 秒之间的某个值。该结构支持动画曲线映射,也可用于随时间变化的动态插值。

  3. mainModule.startSpeed = new ParticleSystem.MinMaxCurve(3.0f, 6.0f);
    同样使用随机范围设定初始速度,使部分粒子更快飞出,增加视觉层次感。

  4. startRotationX/Y/Z 结合 randomizeRotationDirection
    允许粒子以任意方向旋转出生,避免所有粒子朝同一方向运动导致的机械感。

参数说明表:

参数 类型 说明
startLifetime MinMaxCurve 粒子存在时间,可设为固定、随机或基于曲线
startSpeed MinMaxCurve 出生时的速度大小,影响扩散距离
startSize MinMaxCurve 初始尺寸,配合缩放动画使用
startColor MinMaxGradient 初始颜色,可用于区分不同批次粒子

该配置可用于模拟篝火上升的火星:低速、长寿命、微小尺寸,配合轻微向上偏移的速度即可实现逼真效果。

4.1.2 Velocity over Lifetime与External Forces模块联动

除了初始速度外,粒子在整个生命周期中仍可能受到持续性的速度影响。 Velocity over Lifetime 模块允许我们指定一个恒定或随时间变化的速度矢量,直接叠加到当前粒子速度上。

例如,若希望所有粒子始终受到向右的风力推动,可如下设置:

void SetupWindEffect()
{
    var velocityModule = particleSystem.velocityOverLifetime;
    velocityModule.enabled = true;
    // 持续施加 X=2.0 的恒定风速
    velocityModule.x = 2.0f;
    velocityModule.y = 0.0f;
    velocityModule.z = 0.0f;

    // 局部坐标系下生效(相对于粒子自身方向)
    velocityModule.space = ParticleSystemSimulationSpace.Local;
}

与此同时, External Forces 模块允许将场景中的全局力场(如 Wind Zone)应用到粒子上。启用此功能后,只需在场景中添加 Wind Zone 对象并将其关联至粒子系统即可。

// 启用外部力场影响
var externalForceModule = particleSystem.externalForces;
externalForceModule.enabled = true;
externalForceModule.multiplier = 1.5f; // 放大力场效果
Mermaid 流程图展示行为优先级顺序:
graph TD
    A[粒子出生] --> B{是否有 External Forces?}
    B -- 是 --> C[应用 Wind Zone 力]
    B -- 否 --> D[跳过]
    C --> E[计算 Velocity over Lifetime]
    D --> E
    E --> F[叠加 Gravity Modifier]
    F --> G[最终速度更新]
    G --> H[位置积分推进]

该流程表明:外部力场优先于 Velocity over Lifetime 被处理,而重力修饰符通常最后应用。理解这一执行顺序对于调试非预期运动轨迹至关重要。

4.1.3 Gravity Modifier与Turbulence噪声场模拟自然运动

尽管名为“重力”, Gravity Modifier 实际上是一个通用的线性加速度调节器。它不局限于向下拉拽,也可以反向使用以模拟上升气流(如热浪、魔法悬浮)。

void EnableBuoyancyEffect()
{
    var gravityModule = particleSystem.gravityModifier;
    gravityModule.enabled = true;
    gravityModule.multiplyDragByGravity = false; // 不联动阻尼
    gravityModule.value = -0.8f; // 负值表示向上加速
}

为了实现更加自然无序的运动(如烟雾缭绕、树叶飘舞),可结合噪声(Noise)模块引入湍流扰动。噪声模块会周期性地对粒子施加随机方向的小幅震动,形成类似布朗运动的效果。

void AddTurbulenceNoise()
{
    var noiseModule = particleSystem.noise;
    noiseModule.enabled = true;
    noiseModule.strength = 1.5f;           // 扰动强度
    noiseModule.frequency = 0.5f;          // 噪声频率
    noiseModule.damping = true;            // 启用衰减平滑
    noiseModule.scrollSpeed = 2.0f;        // 噪声场移动速度
    noiseModule.separateAxes = true;       // 可分别控制 XYZ 强度
    noiseModule.quality = ParticleSystemNoiseQuality.High; // 高质量采样
}
参数对比表格(不同噪声配置对视觉效果的影响):
Strength Frequency ScrollSpeed 视觉表现
0.5 0.3 0.0 微弱抖动,适合细小灰尘
1.5 0.7 2.0 明显扭曲,适合浓烟
3.0 1.0 5.0 剧烈翻滚,适合爆炸残骸
0.0 - - 无扰动,直线运动

综上,通过对 Start Lifetime Start Speed 的基础设定,再辅以 Velocity over Lifetime External Forces Gravity Modifier 的多层叠加,配合 Noise 模块引入不确定性,可以构建出极具真实感的粒子运动体系。这种分层控制思想体现了 Unity 粒子系统“组合优于继承”的设计理念。

4.2 碰撞检测与事件触发系统

粒子并非孤立存在的视觉元素,它们常常需要与游戏世界的其他实体发生交互。无论是子弹击中墙壁产生的火花,还是雨滴落在地面激起的水花,都需要依赖粒子系统的碰撞检测机制来识别接触时机并作出响应。Unity 提供了两套互补的机制: Collision Module 实现物理层面的反弹与消亡, Trigger Module 则专注于事件监听与脚本回调,二者结合可构建完整的交互闭环。

4.2.1 Collision模块配置粒子与物理世界的交互

要启用粒子碰撞,首先需激活 Collision 模块:

void ConfigureCollision()
{
    var collisionModule = particleSystem.collision;
    collisionModule.enabled = true;
    collisionModule.type = ParticleSystemCollisionType.Planes; // 或 World
    collisionModule.plane0 = GameObject.Find("Floor").transform; // 绑定平面
    collisionModule.dampen = 0.3f;      // 碰撞后速度衰减比例
    collisionModule.bounce = 0.7f;      // 回弹系数(0=完全吸收,1=完全弹性)
    collisionModule.lifetimeLoss = 0.2f;// 每次碰撞减少20%剩余寿命
    collisionModule.maxKillDistance = 10.0f; // 超出该距离自动销毁
}

若选择 World 类型,则粒子将与场景中所有带有 Collider 的对象发生碰撞,无需手动绑定平面。但性能开销更高,建议配合 Layer Mask 过滤无关物体:

collisionModule.type = ParticleSystemCollisionType.World;
collisionModule.collidesWith = LayerMask.GetMask("Environment"); // 仅与环境层碰撞

碰撞发生时,粒子可根据设置反弹、减速或立即销毁,非常适合制作弹片、喷泉飞沫等物理反馈效果。

4.2.2 Trigger模块响应碰撞并执行C#回调函数

相比 Collision 模块关注物理行为, Trigger Module 更侧重于“感知”事件的发生。它可以监听粒子进入、停留或离开某个 Collider 区域,并调用 C# 方法进行逻辑处理。

void SetupTriggerResponse()
{
    var triggerModule = particleSystem.trigger;
    triggerModule.enabled = true;

    var playerCollider = GameObject.Find("Player").GetComponent<Collider>();
    triggerModule.AddCollider(playerCollider); // 添加监听对象

    triggerModule.inside = true;   // 进入时触发
    triggerModule.outside = false; // 离开时不触发
    triggerModule.enter = true;    // 刚进入时触发
    triggerModule.exit = false;    // 离开时不触发
}

void OnParticleTrigger()
{
    var particles = new List<ParticleSystem.Particle>();
    int numParticles = particleSystem.GetTriggerParticles(particles);

    foreach (var p in particles)
    {
        if (p.remainingLifetime > 0)
        {
            Debug.Log($"粒子触碰玩家,位置: {p.position}");
            // 可在此处播放音效、造成伤害、生成新特效等
        }
    }
}
Mermaid 序列图展示触发流程:
sequenceDiagram
    participant ParticleSystem
    participant TriggerModule
    participant Script as C# Script
    participant GameLogic

    ParticleSystem->>TriggerModule: 粒子进入Collider区域
    TriggerModule->>Script: 调用 OnParticleTrigger()
    Script->>Script: 获取触发粒子列表
    Script->>GameLogic: 执行伤害/反馈逻辑
    GameLogic-->>Script: 返回结果
    Script-->>ParticleSystem: 完成处理

该机制广泛应用于技能命中检测、区域AOE判定、环境感应特效等场景。

4.2.3 实现击中反馈、爆炸溅射与弹跳效果

综合上述模块,可实现完整的战斗反馈链。例如,当一枚火球击中敌人时:

  1. 使用 Collision 模块让部分碎片在地面弹跳;
  2. Trigger 检测是否命中敌方角色;
  3. 触发后播放击中特效与音效,并施加伤害。
public GameObject impactEffectPrefab;

void OnParticleTrigger()
{
    List<ParticleSystem.Particle> triggeredParticles = new();
    int count = particleSystem.GetTriggerParticles(triggeredParticles);

    for (int i = 0; i < count; ++i)
    {
        var p = triggeredParticles[i];
        Instantiate(impactEffectPrefab, p.position, Quaternion.identity);
        // 发送消息给附近敌人组件
        Collider[] hits = Physics.OverlapSphere(p.position, 2.0f);
        foreach (var hit in hits)
        {
            if (hit.TryGetComponent<IDamageable>(out var damageable))
                damageable.TakeDamage(10);
        }
    }
}

通过这种方式,粒子系统不再是单纯的“画面装饰”,而是真正融入 gameplay 的功能性组件。

4.3 脚本化动态控制粒子系统

静态编辑器配置难以应对复杂的运行时变化需求。真正的生产级特效往往需要根据游戏状态动态调整参数。Unity 提供了完整的 API 接口,允许通过脚本访问几乎所有模块并实施精细控制。

4.3.1 通过ParticleSystem.MainModule访问主参数

主模块包含最核心的全局设置,如粒子总数、循环模式、模拟空间等:

var main = particleSystem.main;
main.loop = false;                    // 关闭循环播放
main.playOnAwake = true;              // 启动时自动播放
main.simulationSpace = ParticleSystemSimulationSpace.World; // 在世界坐标中模拟
main.maxParticles = 500;              // 最大粒子数限制

4.3.2 使用EmissionModule实现按需发射控制

发射模块控制粒子生成节奏。可通过 Burst 实现脉冲式喷发:

var emission = particleSystem.emission;
emission.enabled = true;

// 每隔1秒爆发一次,每次发射50个粒子
var burst = new ParticleSystem.EmissionModule.Burst(1.0f, 50);
emission.burstCount = 1;
emission.SetBurst(0, burst);

也可实时调节发射率:

emission.rateOverTime = new ParticleSystem.MinMaxCurve(30.0f, 50.0f);

4.3.3 运行时调整Rate over Time创建脉冲式爆发

结合协程实现呼吸式脉动效果:

IEnumerator PulseEmission(float minRate, float maxRate, float duration)
{
    var emission = particleSystem.emission;
    float elapsed = 0f;

    while (elapsed < duration)
    {
        float t = Mathf.PingPong(elapsed * 2, 1.0f);
        emission.rateOverTime = Mathf.Lerp(minRate, maxRate, t);
        elapsed += Time.deltaTime;
        yield return null;
    }
}

此方法可用于表现能量充能、心跳光效等有节奏的变化。

表格:常用脚本控制接口汇总

模块 可控参数 示例用途
main lifetime, speed, loop 全局行为控制
emission rateOverTime, bursts 动态启停、爆发
colorOverLifetime color gradient 闪烁、变色
sizeOverLifetime size curve 缩放动画
rotationOverLifetime angular velocity 自转控制

通过脚本化手段,开发者得以将粒子系统纳入整体 game logic 架构之中,实现真正意义上的智能特效系统。

5. 高性能粒子特效的工程化落地

5.1 LOD(Level of Detail)分级渲染策略与实现

在复杂场景中,大量高密度粒子系统同时运行会显著增加GPU绘制调用(Draw Calls)和顶点处理负担。为缓解这一问题,Unity提供了基于距离的LOD机制,可在不同摄像机距离下动态切换粒子系统的细节层级。

Unity本身未内置粒子系统的LOD Group支持,但可通过 LODGroup 组件结合多个 ParticleSystem 实例手动构建。常见做法是准备3个不同复杂度的粒子预制体:

LOD层级 最大距离 发射率 (Rate over Time) 粒子数量上限 特效质量
0 ∞ ~ 20m 100 500 高清、多层混合、带Trail
1 20 ~ 50m 60 300 中等分辨率、简化模块
2 50m+ 20 100 极简发射、仅基础形态
// 动态绑定LOD行为示例
public class ParticleLODController : MonoBehaviour
{
    public Transform playerCamera;
    public float[] lodDistances = { 20f, 50f };
    public GameObject[] particleLODs;

    void Update()
    {
        float distance = Vector3.Distance(playerCamera.position, transform.position);
        for (int i = 0; i < particleLODs.Length; i++)
        {
            bool shouldBeActive = distance <= (i == 0 ? float.PositiveInfinity : lodDistances[i - 1]);
            particleLODs[i].SetActive(shouldBeActive);
        }
    }
}

该脚本通过监测玩家视角与特效的距离,动态激活对应LOD层级。配合Scene视图中的 LOD Preview 工具可实时调试切换阈值。

此外,建议关闭远距离LOD的 Simulation Speed 或使用 Scaling Mode: Local 控制整体缩放一致性。

5.2 Baking预计算动画与CPU开销优化

当粒子运动轨迹具有确定性(如爆炸碎片飞散、固定路径喷泉),可利用 Baked Simulation 技术将粒子动画预先计算并存储为关键帧序列,从而在运行时完全卸载CPU模拟负担。

启用方式:

var main = particleSystem.main;
main.customSimulationSpace = transform; // 设置参考坐标系
main.simulationSpace = ParticleSystemSimulationSpace.Custom;
main.scalingMode = ParticleSystemScalingMode.Hierarchy;

// 启用预烘焙
main.useUnscaledTime = false;
main.duration = 3.0f;
main.loop = false;

// 在Editor模式下执行烘焙
#if UNITY_EDITOR
UnityEditor.ParticleSystemEditorUtils.BakeSimulation(particleSystem, 3.0f);
#endif

烘焙完成后,粒子系统不再依赖 Update() 循环进行物理积分运算,适用于移动端低功耗设备。需注意:一旦启用Baking,则无法再通过脚本修改速度、重力等动态参数。

推荐流程图表示其工作逻辑:

graph TD
    A[设计粒子轨迹] --> B{是否需要运行时变化?}
    B -- 否 --> C[启用Baking预计算]
    C --> D[导出为静态动画]
    D --> E[减少每帧CPU更新开销]
    B -- 是 --> F[保留实时模拟]
    F --> G[结合对象池管理生命周期]

5.3 对象池技术避免GC压力

频繁Instantiate/Destroy粒子特效会导致内存抖动,引发GC(Garbage Collection)停顿,尤其在移动平台表现明显。

构建通用对象池模板如下:

public class ParticleObjectPool : MonoBehaviour
{
    [System.Serializable]
    public class PoolItem
    {
        public string name;
        public GameObject prefab;
        public int poolSize;
        public bool autoExpand = true;
    }

    public List<PoolItem> pools;
    private Dictionary<string, Queue<GameObject>> poolDictionary = new();

    void Start()
    {
        foreach (var item in pools)
        {
            var queue = new Queue<GameObject>();
            for (int i = 0; i < item.poolSize; i++)
            {
                var obj = Instantiate(item.prefab, transform);
                obj.SetActive(false);
                queue.Enqueue(obj);
            }
            poolDictionary[item.name] = queue;
        }
    }

    public GameObject Spawn(string name, Vector3 position, Quaternion rotation)
    {
        if (!poolDictionary.ContainsKey(name)) return null;

        var queue = poolDictionary[name];
        GameObject obj = null;

        if (queue.Count > 0)
        {
            obj = queue.Dequeue();
        }
        else
        {
            var template = pools.Find(x => x.name == name)?.prefab;
            obj = Instantiate(template, position, rotation);
        }

        obj.transform.position = position;
        obj.transform.rotation = rotation;
        obj.SetActive(true);

        // 自动回收:监听粒子结束
        var ps = obj.GetComponent<ParticleSystem>();
        Invoke(nameof(ReturnToPool), ps.main.duration + ps.main.startLifetime.constantMax);
        return obj;
    }

    void ReturnToPool(GameObject go, string poolName)
    {
        go.SetActive(false);
        poolDictionary[poolName].Enqueue(go);
    }
}

通过维护一个持久化的对象队列,确保粒子系统复用已有实例,极大降低内存分配频率。配合 OnParticleSystemStopped 事件可实现更精准的回收时机控制。

5.4 批量实例化与GPU Instancing集成

对于成千上万相同类型的粒子效果(如雨滴、落叶、星尘背景),应采用 Graphics.DrawMeshInstanced 或URP/HDRP下的 GPU Instancing 方案进行合批渲染。

Unity粒子系统原生支持GPU Instancing,需满足以下条件:
- 使用支持Instancing的Shader(如Universal Render Pipeline/Lit)
- 材质启用 Enable GPU Instancing
- 所有实例共享同一材质变体

// 批量发射优化示例:使用EmissionModule控制突发频率
var emission = particleSystem.emission;
emission.rateOverTime = 0; // 关闭持续发射

// 脉冲式爆发,减少长期负载
InvokeRepeating("BurstParticles", 0.5f, 1.0f);

void BurstParticles()
{
    emission.Burst(0, new ParticleSystem.Burst(0.0f, 50)); // 每秒释放50个
}

同时,在Project Settings > Quality中开启 Vertex Streaming Instance Animation Culling 可进一步提升效率。

5.5 第三方资源导入与定制化规范

从Asset Store导入粒子包时常存在命名混乱、图集冗余、Shader不兼容等问题。建议建立标准化处理流程:

  1. 重命名规则 VFX_Fire_Explosion_Large.prefab
  2. 材质分离 :按功能拆分至 Materials/VFX/ 目录
  3. 纹理压缩设置
    - 移动端使用ASTC 4x4或ETC2
    - 开启Mip Maps仅用于远距离特效
  4. 脚本自动化清洗
#if UNITY_EDITOR
[MenuItem("Tools/Clean Imported Particles")]
static void CleanUpParticles()
{
    var guids = AssetDatabase.FindAssets("t:prefab", new[] { "Assets/VFX" });
    foreach (var guid in guids)
    {
        string path = AssetDatabase.GUIDToAssetPath(guid);
        var go = AssetDatabase.LoadAssetAtPath<GameObject>(path);
        var ps = go.GetComponent<ParticleSystem>();

        if (ps != null)
        {
            var renderer = ps.GetComponent<ParticleSystemRenderer>();
            renderer.mesh = null; // 清除非必要网格引用
            renderer.trailMaterial = null; // 删除冗余材质
        }
    }
    AssetDatabase.SaveAssets();
}
#endif

此流程确保所有外部资源符合项目性能与架构标准,便于后期维护与跨团队协作。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Unity作为一款强大的跨平台游戏开发引擎,广泛应用于2D、3D、VR及AR内容创作。其内置的粒子系统(ParticleSystem)是实现火焰、烟雾、爆炸、魔法等视觉特效的核心工具,能显著提升项目的沉浸感与表现力。本文围绕“Unity常用粒子特效”展开,系统介绍粒子系统的组件结构、形状控制、动画材质应用、生命周期管理、碰撞触发机制、分层遮罩技术,并涵盖脚本控制、性能优化、实例化处理以及Asset Store资源使用等关键内容。通过学习与实践,开发者可快速掌握高效创建与优化粒子特效的方法,提升项目视觉品质与运行效率。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

助力合肥开发者学习交流的技术社区,不定期举办线上线下活动,欢迎大家的加入

更多推荐