简介:这份 Cocos Creator 特效包面向 2D 游戏开发者与特效学习者,集中解决粒子效果素材零散、调试成本高的问题。包内共 149 个文件,以 81 个 meta 元数据、55 张 png 粒子贴图、11 个 prefab 预制体为主,另含 json 配置与 fire 场景文件,压缩包约 1.22MB,体积轻巧便于直接导入项目。特效覆盖爆炸、气泡、天气、发射光、火焰、闪光、光环、光线、烟雾、图形与拖尾等常见类型,可分别用于场景装饰、角色技能、击中反馈与 UI 动画。技术上依托 plist 粒子配置与 png 贴图配合,prefab 可复用挂载,meta 负责资源版本管理,方便在多个场景中统一调用与二次调参。目前已有 3941 人学习下载,适合希望快速提升画面表现力、减少从零制作粒子成本的开发者参考使用。
1. 特效包不是素材堆:从「Cocos Creator 特效包.zip」看游戏特效的工程化落地
拿到一个名为「Cocos Creator 特效包.zip」的压缩包,很多人的第一反应是解压、拖进 assets、挂到节点上跑起来看效果。但真正在项目里用过特效的人都知道,特效包从来不是一堆粒子文件的简单集合,它背后牵扯的是渲染批次、DrawCall 控制、资源加载策略和跨版本兼容性。一个特效包能不能直接用,取决于它的粒子系统是基于什么版本导出的、贴图有没有打成图集、材质有没有共享、脚本有没有外部依赖。我见过太多团队把特效包直接丢进项目,结果真机上帧率从 60 掉到 25,查了半天发现是每个粒子节点都挂了一张独立贴图,DrawCall 直接爆炸。这篇文章面向的是需要在 Cocos Creator 项目里集成第三方特效包的开发者,不管你是刚接触特效系统的新手,还是已经用过几套特效包的老手,我都会从包结构拆解、导入流程、参数调优到性能排查,把这条链路讲透。特效包的价值不在于「好看」,而在于「可控」——你能不能在 30 分钟内判断它能不能用、怎么用、用完会不会翻车。
2. 拆开特效包:目录结构、文件类型与版本信号
2.1 一个典型特效包里到底有什么
把「Cocos Creator 特效包.zip」解压后,你通常会看到几类文件混在一起。第一类是.plist和.png配对,这是老版本 Cocos 的粒子贴图配置格式,.plist里记录了每张小图的坐标和尺寸,.png是合并后的大图。第二类是.plist加.json或直接.json,这是粒子属性文件,记录了发射器参数、生命周期、速度曲线等。第三类是.effect或.mtl材质文件,定义了粒子使用的 Shader 和混合模式。第四类是.prefab预制体,把粒子节点、材质、贴图引用打包成一个可实例化的对象。第五类可能还有.ts或.js脚本,用来做特效播放控制或自定义曲线。
判断一个特效包能不能直接用在你的项目里,第一步不是看效果,而是看版本信号。打开.plist文件,如果里面出现<key>textureFileName</key>这种字段,说明它是 Cocos2d-x 时代的产物,Cocos Creator 3.x 不能直接识别。如果.json里出现"_$erializedType"或"__type__": "cc.ParticleSystem",那它是 Creator 2.x 的序列化格式。3.x 的粒子系统改成了cc.ParticleSystem2D和cc.ParticleSystem3D两套,序列化字段变成了"_components"数组结构。版本不对,导入后要么报错,要么粒子完全不显示。
我一般会先跑一个快速检查脚本,把包里的文件类型和版本特征扫一遍:
# 统计特效包内文件类型分布 find ./effect_pack -type f | sed 's/.*\.//' | sort | uniq -c | sort -rn # 检查 plist 文件是否包含旧版纹理字段 grep -l "textureFileName" ./effect_pack/**/*.plist 2>/dev/null # 检查 json 文件中的序列化类型标记 grep -o '"__type__"[[:space:]]*:[[:space:]]*"[^"]*"' ./effect_pack/**/*.json 2>/dev/null | sort | uniq -c这段脚本的作用是快速定位版本分界线。find加sed统计扩展名分布,能让你一眼看出这个包是以 plist 为主还是以 json 为主。grep -l找出所有包含旧版纹理字段的 plist,这些文件在 Creator 3.x 里需要转换。最后一条命令提取 json 里的__type__字段,如果大量出现cc.ParticleSystem而没有cc.ParticleSystem2D,说明是 2.x 格式。参数上,2>/dev/null是为了屏蔽权限报错,sort | uniq -c用来统计频次,频次最高的类型就是包的主体格式。
2.2 贴图、材质与预制体的依赖关系
特效包能不能直接用,第二个判断点是依赖关系是否完整。一个粒子特效通常由三部分组成:粒子属性文件(描述运动)、贴图(描述外观)、材质(描述混合方式)。这三者通过 UUID 或路径互相引用。如果特效包是从某个项目里导出的,引用路径很可能是绝对路径或者原项目的相对路径,直接导入你的项目就会丢引用。
检查依赖关系的方法是看.prefab或.json里的_texture、_material字段。在 Creator 3.x 里,这些字段存的是 UUID,格式类似"__uuid__": "a1b2c3d4-..."。你需要确认这些 UUID 对应的资源是否在包内。如果包内只有.plist和.png,没有对应的.meta文件,那 UUID 就是空的,导入后需要手动重新绑定贴图。
我通常会用一段 Python 脚本把预制体里的资源引用提取出来,和包内实际文件做比对:
import json import os import re def extract_uuids(prefab_path): """从 prefab 或 json 中提取所有 __uuid__ 引用""" with open(prefab_path, 'r', encoding='utf-8') as f: content = f.read() # 匹配 "__uuid__": "xxxx" 格式 uuids = re.findall(r'"__uuid__"\s*:\s*"([^"]+)"', content) return set(uuids) def scan_pack(pack_dir): """扫描特效包内所有 meta 文件,收集可用 UUID""" available = set() for root, dirs, files in os.walk(pack_dir): for file in files: if file.endswith('.meta'): meta_path = os.path.join(root, file) with open(meta_path, 'r', encoding='utf-8') as f: meta = json.load(f) if 'uuid' in meta: available.add(meta['uuid']) return available # 使用示例 pack_dir = './effect_pack' prefab_files = [f for f in os.listdir(pack_dir) if f.endswith('.prefab')] all_available = scan_pack(pack_dir) for prefab in prefab_files: needed = extract_uuids(os.path.join(pack_dir, prefab)) missing = needed - all_available print(f"{prefab}: 需要 {len(needed)} 个引用, 缺失 {len(missing)} 个") if missing: print(f" 缺失 UUID: {missing}")这段脚本的逻辑是:先从预制体里提取所有 UUID 引用,再扫描包内所有.meta文件收集实际存在的 UUID,两者做差集就是缺失的依赖。参数上,re.findall的正则"__uuid__"\s*:\s*"([^"]+)"能兼容不同缩进格式。os.walk递归扫描确保不遗漏子目录。输出结果里如果缺失数量大于 0,说明这个特效包不能直接拖进项目用,必须先补齐贴图或材质资源。常见做法是去原项目里找对应 UUID 的资源,或者用 Creator 的资源重新导入功能让编辑器自动重建引用。
2.3 版本适配:2.x 特效包迁移到 3.x 的实操路径
如果你拿到的特效包是 Creator 2.x 格式,而项目用的是 3.x,直接导入会失败。迁移的核心工作是三件事:粒子属性字段映射、材质系统替换、预制体结构重建。2.x 的粒子属性里,_life对应 3.x 的_life,_startColor对应_startColor,但_speed在 3.x 里拆成了_speed和_speedVar两个字段。材质方面,2.x 用cc.Material加_effectAsset,3.x 改成了cc.Material加_techniqueIndex和_defines。
手动迁移一个特效包的工作量很大,我一般会写一个转换脚本处理批量迁移:
import json import os # 2.x 到 3.x 的粒子字段映射表 FIELD_MAP = { '_life': '_life', '_lifeVar': '_lifeVar', '_startColor': '_startColor', '_startColorVar': '_startColorVar', '_speed': '_speed', '_speedVar': '_speedVar', # 2.x 可能没有这个字段,需要补默认值 '_angle': '_angle', '_angleVar': '_angleVar', '_gravity': '_gravity', '_emissionRate': '_emissionRate', '_duration': '_duration', '_texture': '_texture', } def convert_particle_json(src_path, dst_path): """将 2.x 粒子 json 转换为 3.x 格式""" with open(src_path, 'r', encoding='utf-8') as f: data = json.load(f) converted = {} for old_key, new_key in FIELD_MAP.items(): if old_key in data: converted[new_key] = data[old_key] else: # 补默认值,避免 3.x 解析时报 undefined if new_key == '_speedVar': converted[new_key] = 0 elif new_key == '_lifeVar': converted[new_key] = 0 else: converted[new_key] = data.get(old_key, 0) # 3.x 必须的字段 converted['__type__'] = 'cc.ParticleSystem2D' converted['_enabled'] = True with open(dst_path, 'w', encoding='utf-8') as f: json.dump(converted, f, indent=2, ensure_ascii=False) print(f"转换完成: {src_path} -> {dst_path}") # 批量转换 src_dir = './effect_pack_2x' dst_dir = './effect_pack_3x' os.makedirs(dst_dir, exist_ok=True) for file in os.listdir(src_dir): if file.endswith('.json'): convert_particle_json( os.path.join(src_dir, file), os.path.join(dst_dir, file) )这段代码的关键在于FIELD_MAP字典,它定义了新旧字段的对应关系。_speedVar和_lifeVar在 2.x 里可能不存在,转换时需要补 0,否则 3.x 解析会报错。__type__字段必须改成cc.ParticleSystem2D,这是 3.x 识别粒子系统的依据。参数上,indent=2保证输出可读,ensure_ascii=False避免中文注释被转义。转换完成后,还需要在 Creator 编辑器里手动重新绑定贴图和材质,因为 UUID 引用无法通过脚本自动重建。常见做法是先把转换后的 json 导入项目,然后在编辑器里逐个替换_texture和_material字段。
3. 导入与集成:从解压到跑通第一个特效
3.1 资源目录规划与 meta 文件生成
特效包导入项目时,目录结构不能随便放。Creator 3.x 的资源数据库依赖.meta文件来管理 UUID,如果直接把文件拖进 assets 目录,编辑器会自动生成 meta,但自动生成的 UUID 和特效包内预制体引用的 UUID 不一致,导致引用丢失。正确的做法是先把特效包解压到一个临时目录,保留原有的.meta文件,然后整体复制到 assets 下的某个子目录。
我一般会按功能分类建目录,比如assets/effects/particle/放粒子预制体,assets/effects/texture/放贴图,assets/effects/material/放材质。复制时用cp -r保留目录结构:
# 创建目标目录 mkdir -p assets/effects/particle mkdir -p assets/effects/texture mkdir -p assets/effects/material # 按类型复制,保留 meta 文件 cp -r ./effect_pack/prefabs/* assets/effects/particle/ cp -r ./effect_pack/textures/* assets/effects/texture/ cp -r ./effect_pack/materials/* assets/effects/material/ # 检查 meta 文件是否完整 find assets/effects -name "*.meta" | wc -l find assets/effects -type f ! -name "*.meta" | wc -l这段命令的核心是cp -r保留目录结构,确保.meta文件和资源文件一起复制。最后两条find命令用来核对 meta 文件数量是否和资源文件数量一致。如果 meta 数量少于资源数量,说明有文件缺失 meta,需要在编辑器里右键重新导入。参数上,-p确保父目录不存在时自动创建,-r递归复制子目录。
注意:如果特效包内的
.meta文件版本和当前 Creator 版本不匹配,编辑器会提示升级。升级时选择「保留原 UUID」而不是「重新生成」,否则预制体引用会全部断开。
3.2 预制体实例化与播放控制脚本
资源导入后,下一步是在场景里实例化特效预制体。Creator 3.x 里用instantiate加addChild的方式挂载,但特效通常需要播放完成后自动销毁,否则节点会一直留在场景里消耗性能。我一般会写一个通用的特效播放组件,挂在特效预制体上,控制播放、循环和自动回收。
import { _decorator, Component, Node, ParticleSystem2D, ParticleSystem3D, instantiate, Prefab } from 'cc'; const { ccclass, property } = _decorator; @ccclass('EffectPlayer') export class EffectPlayer extends Component { @property(Prefab) effectPrefab: Prefab = null!; @property autoDestroy: boolean = true; @property destroyDelay: number = 0.5; // 粒子播放完后的缓冲时间 private _particle2D: ParticleSystem2D | null = null; private _particle3D: ParticleSystem3D | null = null; onLoad() { // 兼容 2D 和 3D 粒子系统 this._particle2D = this.node.getComponent(ParticleSystem2D); this._particle3D = this.node.getComponent(ParticleSystem3D); } play() { if (this._particle2D) { this._particle2D.resetSystem(); } if (this._particle3D) { this._particle3D.resetSystem(); } if (this.autoDestroy) { // 根据粒子生命周期计算销毁时间 const duration = this._particle2D?.life ?? this._particle3D?.life ?? 1; this.scheduleOnce(() => { this.node.destroy(); }, duration + this.destroyDelay); } } // 在指定位置播放特效 static playAt(parent: Node, prefab: Prefab, position: Vec3) { const node = instantiate(prefab); node.setPosition(position); parent.addChild(node); const player = node.getComponent(EffectPlayer); if (player) { player.play(); } return node; } }这段组件的逻辑是:onLoad里同时获取 2D 和 3D 粒子组件,兼容两种格式。play方法调用resetSystem让粒子从头播放,然后根据life字段计算销毁时间,destroyDelay是缓冲值,防止粒子还没完全消失节点就被销毁。playAt是静态方法,方便在任意位置生成特效。参数上,autoDestroy控制是否自动销毁,如果特效需要循环播放就设为false。destroyDelay默认 0.5 秒,如果粒子有拖尾效果可以适当加大。
3.3 材质与混合模式:Additive 和 Alpha 的选择
特效的视觉效果很大程度上取决于材质的混合模式。Cocos Creator 里粒子常用的混合模式有两种:Additive和Alpha。Additive是把粒子颜色叠加到背景上,适合火焰、光效、能量波这类发光效果。Alpha是标准透明混合,适合烟雾、灰尘、水花这类不发光的效果。选错了混合模式,特效要么过曝,要么灰蒙蒙一片。
在 Creator 3.x 里,混合模式在材质文件的_techniqueIndex和_defines里定义。Additive对应的_defines里会有USE_ADDITIVE或SRC_ALPHA加ONE的混合因子。Alpha对应SRC_ALPHA加ONE_MINUS_SRC_ALPHA。如果你拿到的特效包材质文件里混合模式不对,可以在编辑器里手动改:选中材质,在 Inspector 面板里找到Blend State,把Source Blend Factor和Destination Blend Factor改成对应值。
我一般会用一个对照表来快速判断:
| 效果类型 | 推荐混合模式 | Source Factor | Destination Factor |
|---|---|---|---|
| 火焰/光效 | Additive | SRC_ALPHA | ONE |
| 烟雾/灰尘 | Alpha | SRC_ALPHA | ONE_MINUS_SRC_ALPHA |
| 能量护盾 | Additive | ONE | ONE |
| 水花/碎片 | Alpha | SRC_ALPHA | ONE_MINUS_SRC_ALPHA |
改完混合模式后,记得在场景里实时预览。如果粒子颜色偏白,说明 Additive 叠加过度,需要降低_startColor的 alpha 值。如果粒子边缘有黑边,说明贴图没有预乘 alpha,需要在材质里勾选Premultiply Alpha。
4. 性能调优:DrawCall、粒子数与真机帧率
4.1 DrawCall 合并:图集与材质共享
特效包对性能最大的冲击是 DrawCall。每个粒子节点如果使用独立贴图和独立材质,就会产生一次 DrawCall。一个复杂的特效可能有几十个粒子节点,DrawCall 直接上百,中低端手机扛不住。优化的核心思路是合并:把多张小贴图打成一张图集,多个粒子共享同一个材质。
检查特效包是否已经做了图集合并,看.plist文件里是否包含多个frames条目。如果每个 plist 只对应一张小图,说明没有合并。手动合并的方法是:把所有粒子贴图导入 Creator 的自动图集工具,生成一张大图,然后修改粒子属性里的_texture引用指向图集,并在_spriteFrame里指定对应的子图。
// 批量替换粒子贴图为图集引用 import { Asset, SpriteFrame, Texture2D, resources, assetManager } from 'cc'; async function replaceParticleTextures(prefabNode: Node, atlasPath: string) { // 加载图集 const atlas = await new Promise<SpriteFrame[]>((resolve) => { resources.load(atlasPath, SpriteFrame, (err, frames) => { if (err) { console.error('图集加载失败', err); resolve([]); return; } resolve(Array.isArray(frames) ? frames : [frames]); }); }); // 遍历所有粒子节点,替换贴图 const particles = prefabNode.getComponentsInChildren('cc.ParticleSystem2D'); for (const particle of particles) { const currentTexture = particle.texture; if (!currentTexture) continue; // 根据原贴图名称匹配图集中的子图 const matchedFrame = atlas.find(frame => frame.name.includes(currentTexture.name) ); if (matchedFrame) { particle.texture = matchedFrame.texture; console.log(`替换成功: ${currentTexture.name} -> ${matchedFrame.name}`); } } }这段代码的逻辑是:先加载图集资源,然后遍历预制体下所有 2D 粒子组件,根据原贴图名称匹配图集中的子图,替换texture引用。参数上,resources.load的路径是相对于resources目录的,图集需要放在resources下才能动态加载。getComponentsInChildren会递归查找所有子节点,确保不遗漏。替换完成后,所有粒子共享同一张图集纹理,DrawCall 可以从几十降到个位数。
4.2 粒子数上限与发射速率调参
粒子数是另一个性能杀手。每个粒子在 CPU 端要计算位置、速度、颜色,在 GPU 端要渲染一个四边形。粒子数超过 500 时,中低端手机就开始掉帧。特效包里默认的_emissionRate和_maxParticles往往偏高,需要根据目标机型调整。
调参的原则是:先降_maxParticles,再降_emissionRate,最后调_life。_maxParticles是硬上限,超过这个数的粒子不会被创建。_emissionRate是每秒发射数,降低它可以让粒子更稀疏。_life是粒子存活时间,缩短它可以让粒子更快消失,减少同时存在的粒子数。
我一般会按这个顺序调:
# 在 Creator 编辑器里批量修改粒子参数 # 选中所有粒子预制体,在 Inspector 里修改以下字段 # 1. 设置 maxParticles 上限 _maxParticles: 200 # 中低端机型建议 100-200 # 2. 降低 emissionRate _emissionRate: 30 # 默认可能是 100,降到 30 左右 # 3. 缩短 life _life: 0.8 # 默认可能是 2.0,降到 0.8-1.2 # 4. 关闭不必要的模块 _enableRotation: false # 如果不需要旋转就关掉 _enableGravity: false # 如果不需要重力就关掉调完后在真机上跑一遍,用 Creator 的 Profiler 看帧率和 DrawCall。如果帧率还是低,继续降_maxParticles到 100 以下。如果特效看起来太稀疏,可以适当提高_emissionRate但保持_maxParticles不变,这样粒子会更密集但总数不变。
4.3 真机排查:从 Profiler 到 GPU 耗时
编辑器里的预览和真机表现往往差距很大。编辑器用的是桌面 GPU,真机用的是移动 GPU,渲染管线不同,性能特征也不同。排查真机性能问题,Creator 自带的 Profiler 是第一个工具。打开 Profiler,看Renderer面板里的DrawCall和Triangles,如果 DrawCall 超过 50 或者 Triangles 超过 10 万,说明特效包需要优化。
第二个工具是 GPU 耗时分析。Creator 3.x 支持在真机上开启 GPU 计时,在settings里打开debugMode,然后在 Profiler 里看GPU Time。如果 GPU Time 超过 16ms,说明渲染压力大,需要降低粒子数或简化 Shader。
我遇到过一个典型案例:一个特效包在编辑器里跑 60 帧,真机上只有 25 帧。用 Profiler 一看,DrawCall 是 80,Triangles 是 15 万。原因是每个粒子节点都用了独立的材质,而且材质里开了USE_NORMAL_MAP。解决方法是合并材质、关闭法线贴图,DrawCall 降到 12,帧率回到 55。这个血泪经验告诉我,特效包的优化不能只看效果,一定要在真机上跑数据。
5. 避坑与常见问题:特效包集成中的五个翻车现场
5.1 导入后粒子不显示,控制台无报错
现象:把特效包拖进场景,粒子节点在层级管理器里能看到,但场景里什么都不显示,控制台也没有报错。
原因:最常见的是贴图丢失或材质引用为空。Creator 3.x 里粒子系统如果_texture字段为空,会静默不渲染,不会报错。另一个可能是粒子的_startColor的 alpha 值为 0,导致完全透明。
解决:选中粒子节点,在 Inspector 里检查Texture字段是否为空。如果为空,手动拖入贴图。然后检查Start Color的 alpha 值,确保不是 0。如果还是不行,把粒子系统的Play On Load勾上,或者在脚本里手动调用resetSystem()。
5.2 真机上特效闪烁或撕裂
现象:编辑器里正常,真机上粒子闪烁、撕裂,或者出现奇怪的条纹。
原因:通常是混合模式在移动 GPU 上不兼容。Additive混合在某些移动 GPU 上需要预乘 alpha,如果贴图没有预乘,就会出现闪烁。另一个可能是粒子的_life太短,导致粒子在单帧内创建和销毁,产生撕裂。
解决:在材质里勾选Premultiply Alpha,或者把混合模式改成Alpha试试。如果是_life太短,把_life调到 0.3 以上。还可以在粒子系统里开启_spriteFrame的packable选项,让引擎自动处理纹理对齐。
5.3 特效播放完不消失,节点越积越多
现象:每次播放特效都会生成一个新节点,播放完后节点不销毁,场景里的节点数量持续增长,内存和 DrawCall 越来越高。
原因:特效预制体上没有挂自动销毁脚本,或者脚本里的autoDestroy被设成了false。另一个可能是粒子系统设了loop模式,播放完不自动停止。
解决:给特效预制体挂上EffectPlayer组件,确保autoDestroy为true。如果特效需要循环播放,在不需要的时候手动调用node.destroy()。还要检查粒子系统的Loop选项,非循环特效要关掉Loop。
5.4 图集合并后粒子显示错位
现象:把粒子贴图打成图集后,粒子显示的是图集里的错误区域,或者显示成整张大图。
原因:粒子系统引用的是Texture2D而不是SpriteFrame。图集合并后,每个子图是一个SpriteFrame,如果粒子直接引用整张图集的Texture2D,就会显示整张大图。
解决:在粒子属性里把_texture改成引用SpriteFrame的texture,并设置_spriteFrame字段指向正确的子图。Creator 3.x 的粒子系统支持_spriteFrame字段,设置后会自动计算 UV 偏移。
5.5 跨版本迁移后材质变紫
现象:从 2.x 迁移到 3.x 后,粒子材质变成紫色,或者显示为默认材质。
原因:3.x 的材质系统改成了cc.Material加_techniqueIndex,2.x 的_effectAsset字段在 3.x 里不存在。迁移脚本如果没有正确处理材质引用,就会导致材质丢失,引擎用默认的紫色材质替代。
解决:在编辑器里手动重新指定材质。选中粒子节点,在Materials字段里拖入正确的材质文件。如果材质文件本身也是 2.x 格式,需要在编辑器里重新创建材质,选择builtin-particle或builtin-particle-add作为 Effect。
6. 进阶技巧:用自定义 Shader 和曲线控制让特效包脱胎换骨
特效包自带的粒子效果往往只能满足基础需求,真正让特效出彩的是自定义 Shader 和曲线控制。Creator 3.x 支持在材质里挂自定义 Effect,你可以写一个简单的 Shader 来改变粒子的颜色渐变、扭曲或溶解效果。比如,给火焰特效加一个基于时间的颜色渐变,让火焰从橙色过渡到蓝色,视觉冲击力会强很多。
// 自定义粒子 Effect 片段着色器 CCProgram fs %{ precision highp float; in vec2 v_uv0; in vec4 v_color; uniform sampler2D texture; uniform float time; void main() { vec4 texColor = texture2D(texture, v_uv0); // 基于时间的颜色渐变:从橙色到蓝色 float t = fract(time * 0.5); vec3 warmColor = vec3(1.0, 0.6, 0.2); vec3 coolColor = vec3(0.2, 0.4, 1.0); vec3 gradient = mix(warmColor, coolColor, t); // 叠加到粒子颜色上 vec4 finalColor = vec4(texColor.rgb * gradient, texColor.a * v_color.a); gl_FragColor = finalColor; } }%这段 Shader 的核心是mix函数,它根据时间t在暖色和冷色之间插值。fract(time * 0.5)让渐变每 2 秒循环一次。texColor.rgb * gradient把渐变叠加到贴图颜色上,texColor.a * v_color.a保留粒子的透明度。参数上,time是引擎自动传入的全局时间,v_uv0是粒子 UV,v_color是粒子顶点颜色。把这个 Effect 挂到材质上,粒子就会呈现动态颜色变化。
曲线控制是另一个进阶点。Creator 的粒子系统支持_startColor、_speed、_size等字段的曲线编辑。在 Inspector 里点击字段旁边的曲线图标,可以拖拽控制点,定义粒子在不同生命阶段的属性变化。比如,让粒子的大小从 0 快速增大到 1,再缓慢缩小到 0,这样粒子会有「爆发」的感觉。我一般会把_size曲线设成先陡后缓,_speed曲线设成线性衰减,_startColor的 alpha 曲线设成先增后减。
最后说一个我自己的习惯:每次拿到新的特效包,先不急着往项目里塞,而是建一个空的测试场景,把特效包里的每个预制体单独跑一遍,用 Profiler 记录 DrawCall、Triangles 和 GPU Time。只有数据达标了,才考虑集成到正式项目里。这个习惯帮我省了很多后悔药,也让我对每个特效包的性能边界心里有数。希望帮到你。
本文还有配套的精品资源,点击获取