Unity 粒子系统 Triggers 使用解析:监听粒子进入与离开区域并触发事件
【Unity笔记】Unity 粒子系统 Triggers 使用解析:监听粒子进入与离开区域并触发事件
引言
Unity 粒子系统(Particle System)的 Triggers 模块 是一个强大的交互功能,用于检测粒子进入/离开指定碰撞器区域并触发自定义回调。它支持 粒子生命周期事件(Enter/Exit/Inside)、碰撞类型过滤 和 自定义脚本响应,适用于特效交互(如火花触碰敌人)、物理模拟(如水滴落地)和游戏逻辑(如魔法粒子击中目标)。
核心原理:
- Triggers 模块通过物理引擎(Physics System)检测粒子与 Collider 的碰撞
- 支持多种触发模式:
Inside
(粒子在区域内)、Outside
(区域外)、Enter
(进入)、Exit
(离开) - 每帧检查粒子的位置/速度,与 Collider 边界进行空间查询
- 可触发
OnParticleTrigger()
回调,访问粒子数据(位置、速度、寿命等)
性能特点:
- 时间复杂度 O(n),n 为活动粒子数
- 推荐 Collider 为简单几何体(Box/Sphere)避免复杂 Mesh 计算
- 大量粒子场景需优化触发频率和区域大小
适用版本:Unity 2018.4+(推荐 2022.3 LTS),兼容 URP/HDRP。
Triggers 模块核心设置
1. 基本配置步骤
- 创建粒子系统:GameObject > Effects > Particle System
- 添加 Collider:在场景中创建触发区域(Box Collider、Sphere Collider 等),取消 “Is Trigger”(Triggers 模块需要物理碰撞检测)
- 启用 Triggers 模块:
- 选中 Particle System > Modules > Triggers > 勾选 “Enabled”
- Colliders:拖拽触发区域的 Collider 到列表
- Inside:粒子进入区域后的行为(Callback/Callback Separation/Kill)
- Outside:粒子离开区域的行为
- Enter:进入瞬间触发
- Exit:离开瞬间触发
2. 触发模式详解
模式 | 描述 | 典型应用 |
---|---|---|
Inside | 粒子持续在 Collider 内 | 持续伤害区域、风力场 |
Outside | 粒子持续在 Collider 外 | 安全区检测 |
Enter | 粒子首次进入(单次触发) | 粒子击中、爆炸触发 |
Exit | 粒子离开(单次触发) | 脱离效果、状态恢复 |
Callback Separation:每个 Collider 可独立配置触发模式(例如 Collider1=Enter,Collider2=Inside)
核心脚本实现
3. OnParticleTrigger 回调基础
using UnityEngine;
public class ParticleTriggerHandler : MonoBehaviour
{
private ParticleSystem particleSystem;
private ParticleSystem.TriggerModule triggerModule;
void Start()
{
particleSystem = GetComponent<ParticleSystem>();
triggerModule = particleSystem.trigger;
// 配置触发器
SetupTriggers();
// 注册回调
var main = particleSystem.main;
main.stopAction = ParticleSystemStopBehavior.Callback; // 粒子停止时回调
}
private void SetupTriggers()
{
// 添加 Collider 引用(运行时动态添加)
triggerModule.AddCollider(gameObject.AddComponent<SphereCollider>());
// 配置触发模式
triggerModule.inside = ParticleSystemTriggerAction.Callback;
triggerModule.outside = ParticleSystemTriggerAction.None;
triggerModule.enter = ParticleSystemTriggerAction.Callback;
triggerModule.exit = ParticleSystemTriggerAction.Callback;
// 启用碰撞形状(优化性能)
triggerModule.colliderInteractionType = ParticleSystemColliderInteractionType.ForceBoth;
}
// 核心回调:每帧检查触发粒子
void OnParticleTrigger()
{
int triggerCount = particleSystem.GetTriggerParticles(ParticleSystemTriggerEventType.Inside);
for (int i = 0; i < triggerCount; i++)
{
ParticleSystem.TriggerEvent triggerEvent = particleSystem.GetTriggerEvent(i, ParticleSystemTriggerEventType.Inside);
HandleInsideTrigger(triggerEvent);
}
// 处理其他触发类型
ProcessTriggerEvents(ParticleSystemTriggerEventType.Enter);
ProcessTriggerEvents(ParticleSystemTriggerEventType.Exit);
}
}
4. 详细触发事件处理
private void ProcessTriggerEvents(ParticleSystemTriggerEventType eventType)
{
int count = particleSystem.GetTriggerParticles(eventType);
for (int i = 0; i < count; i++)
{
ParticleSystem.TriggerEvent trigger = particleSystem.GetTriggerEvent(i, eventType);
switch (eventType)
{
case ParticleSystemTriggerEventType.Enter:
OnParticleEnter(trigger);
break;
case ParticleSystemTriggerEventType.Exit:
OnParticleExit(trigger);
break;
case ParticleSystemTriggerEventType.Inside:
OnParticleInside(trigger);
break;
case ParticleSystemTriggerEventType.Outside:
OnParticleOutside(trigger);
break;
}
}
}
private void OnParticleEnter(ParticleSystem.TriggerEvent trigger)
{
int particleIndex = trigger.index;
ParticleSystem.Particle particle = particleSystem.GetParticleByIndex(particleIndex);
// 示例:粒子击中目标,改变颜色并减少生命
particle.startColor = Color.red;
particle.remainingLifetime *= 0.5f; // 加速消亡
// 触发游戏逻辑
DealDamageAtPosition(particle.position);
particleSystem.SetParticles(new ParticleSystem.Particle[] { particle }, 1);
}
private void OnParticleInside(ParticleSystem.TriggerEvent trigger)
{
ParticleSystem.Particle particle = particleSystem.GetParticleByIndex(trigger.index);
// 持续效果:减速、旋转等
particle.velocity *= 0.95f; // 阻力
particle.angularVelocity = Vector3.up * 90f; // 旋转
particleSystem.SetParticles(new ParticleSystem.Particle[] { particle }, 1);
}
private void OnParticleExit(ParticleSystem.TriggerEvent trigger)
{
// 粒子离开:恢复属性或销毁
ParticleSystem.Particle particle = particleSystem.GetParticleByIndex(trigger.index);
particle.startSize *= 2f; // 放大离开效果
particleSystem.SetParticles(new ParticleSystem.Particle[] { particle }, 1);
}
private void DealDamageAtPosition(Vector3 position)
{
// 射线检测击中物体
RaycastHit hit;
if (Physics.Raycast(position, Vector3.down, out hit, 1f))
{
IDamageable damageable = hit.collider.GetComponent<IDamageable>();
damageable?.TakeDamage(10f);
}
}
5. 性能优化版本(批量处理)
public class OptimizedParticleTrigger : MonoBehaviour
{
private ParticleSystem particleSystem;
private NativeArray<ParticleSystem.Particle> particles;
void OnEnable()
{
particleSystem = GetComponent<ParticleSystem>();
var main = particleSystem.main;
main.maxParticles = 1000; // 设置上限
// 预分配 NativeArray(Burst 兼容)
particles = new NativeArray<ParticleSystem.Particle>(main.maxParticles, Allocator.Persistent);
}
void OnParticleTrigger()
{
// 批量获取触发粒子(避免逐个 GetParticle)
int enterCount = particleSystem.GetTriggerParticles(ParticleSystemTriggerEventType.Enter);
int insideCount = particleSystem.GetTriggerParticles(ParticleSystemTriggerEventType.Inside);
// 批量处理 Enter 事件
ProcessBatchEvents(ParticleSystemTriggerEventType.Enter, enterCount);
// 批量处理 Inside 事件
ProcessBatchEvents(ParticleSystemTriggerEventType.Inside, insideCount);
}
private void ProcessBatchEvents(ParticleSystemTriggerEventType eventType, int count)
{
if (count == 0) return;
// 获取所有触发粒子的索引
NativeArray<int> triggerIndices = new NativeArray<int>(count, Allocator.Temp);
particleSystem.GetTriggerIndices(triggerIndices, eventType);
// 批量获取粒子数据
particleSystem.GetParticles(particles);
for (int i = 0; i < count; i++)
{
int index = triggerIndices[i];
if (index < particles.Length)
{
ProcessSingleParticle(particles, index, eventType);
}
}
// 批量设置修改后的粒子
particleSystem.SetParticles(particles);
triggerIndices.Dispose();
}
private void ProcessSingleParticle(NativeArray<ParticleSystem.Particle> particles, int index, ParticleSystemTriggerEventType eventType)
{
ref var particle = ref particles[index];
switch (eventType)
{
case ParticleSystemTriggerEventType.Enter:
particle.startColor = Color.yellow;
// 记录击中位置用于后续逻辑
RecordHit(particle.position);
break;
case ParticleSystemTriggerEventType.Inside:
// 持续效果
particle.velocity *= 0.98f;
break;
}
}
void OnDisable()
{
particles.Dispose();
}
}
高级应用场景
6. 魔法粒子击中系统
public class MagicParticleSystem : MonoBehaviour
{
[Header("魔法设置")]
public float damagePerParticle = 5f;
public ParticleSystem magicParticles;
public LayerMask targetLayers;
private HashSet<Collider> affectedTargets = new HashSet<Collider>();
void OnParticleTrigger()
{
// Enter 事件:首次击中
int enterCount = magicParticles.GetTriggerParticles(ParticleSystemTriggerEventType.Enter);
for (int i = 0; i < enterCount; i++)
{
ParticleSystem.TriggerEvent trigger = magicParticles.GetTriggerEvent(i, ParticleSystemTriggerEventType.Enter);
Collider targetCollider = trigger.colliderComponent;
if (targetCollider != null && (targetLayers & (1 << targetCollider.gameObject.layer)) != 0)
{
affectedTargets.Add(targetCollider);
ApplyMagicEffect(targetCollider);
}
}
// Inside 事件:持续伤害
ProcessContinuousDamage();
}
private void ApplyMagicEffect(Collider target)
{
// 视觉效果:目标发光
var renderer = target.GetComponent<Renderer>();
if (renderer != null)
{
renderer.material.SetFloat("_Emission", 2f);
}
// 触发魔法逻辑
IMagicTarget magicTarget = target.GetComponent<IMagicTarget>();
magicTarget?.OnMagicHit();
}
private void ProcessContinuousDamage()
{
foreach (var target in affectedTargets)
{
Health health = target.GetComponent<Health>();
if (health != null)
{
health.TakeDamage(damagePerParticle * Time.deltaTime);
}
}
}
}
7. 环境交互粒子系统
public class EnvironmentParticleTrigger : MonoBehaviour
{
[Header("环境效果")]
public ParticleSystem fireParticles;
public ParticleSystem waterParticles;
public ParticleSystem windParticles;
private Dictionary<Collider, ParticleEffectType> activeEffects = new Dictionary<Collider, ParticleEffectType>();
public enum ParticleEffectType { Fire, Water, Wind, None }
void OnParticleTrigger()
{
ProcessEnvironmentalInteractions(ParticleSystemTriggerEventType.Enter);
ProcessEnvironmentalInteractions(ParticleSystemTriggerEventType.Inside);
}
private void ProcessEnvironmentalInteractions(ParticleSystemTriggerEventType eventType)
{
int count = fireParticles.GetTriggerParticles(eventType);
for (int i = 0; i < count; i++)
{
var trigger = fireParticles.GetTriggerEvent(i, eventType);
var targetCollider = trigger.colliderComponent;
if (targetCollider != null)
{
HandleEnvironmentalInteraction(targetCollider, ParticleEffectType.Fire, eventType);
}
}
}
private void HandleEnvironmentalInteraction(Collider target, ParticleEffectType effectType, ParticleSystemTriggerEventType eventType)
{
IEnvironmentalInteractable interactable = target.GetComponent<IEnvironmentalInteractable>();
if (eventType == ParticleSystemTriggerEventType.Enter)
{
interactable?.OnParticleEnter(effectType);
activeEffects[target] = effectType;
}
else if (eventType == ParticleSystemTriggerEventType.Inside)
{
interactable?.OnParticleStay(effectType);
}
else if (eventType == ParticleSystemTriggerEventType.Exit)
{
interactable?.OnParticleExit(effectType);
activeEffects.Remove(target);
}
}
}
性能优化与最佳实践
8. 性能优化技巧
- 减少检查频率:
[Header("性能设置")]
public float triggerCheckInterval = 0.1f; // 每 0.1s 检查一次
private float lastTriggerCheck;
void Update()
{
if (Time.time - lastTriggerCheck > triggerCheckInterval)
{
lastTriggerCheck = Time.time;
// 执行触发检查
}
}
- 空间分区优化:
- 使用多个小 Collider 替代一个大 Collider
- 启用 Layer-based 过滤:
LayerMask
只检测特定层
- 粒子池管理:
private void OptimizeParticleCount()
{
var main = particleSystem.main;
main.maxParticles = 500; // 限制最大粒子数
main.startLifetime = 2f; // 缩短寿命,减少持续计算
}
- LOD 系统:
public void SetTriggerLOD(float distance)
{
bool enableTriggers = distance < 50f; // 远距离禁用 Triggers
triggerModule.enabled = enableTriggers;
}
9. 调试与可视化
void OnDrawGizmos()
{
if (triggerModule.enabled)
{
// 可视化触发区域
foreach (var collider in triggerModule.colliders)
{
if (collider != null)
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireCube(collider.bounds.center, collider.bounds.size);
}
}
// 显示触发统计
Gizmos.color = Color.cyan;
Gizmos.DrawWireSphere(transform.position, 1f);
UnityEditor.Handles.Label(transform.position + Vector3.up * 2f,
$"Active Triggers: {particleSystem.GetTriggerParticles(ParticleSystemTriggerEventType.Inside)}");
}
}
常见问题与解决方案
问题 | 原因 | 解决方案 |
---|---|---|
触发不准确 | Collider 太小/粒子速度快 | 增大 Collider,启用 Continuous Collision |
性能瓶颈 | 粒子数量过多 | 限制 maxParticles,使用 LOD,批量处理 |
误触发 | Layer 冲突 | 设置正确的 LayerMask,排除不相关物体 |
回调不执行 | 模块未启用 | 检查 Triggers 模块和 Collider 引用 |
URP 兼容 | 渲染管线问题 | 确保粒子材质兼容 URP Shader |
完整使用示例
创建一个火球粒子系统,击中敌人造成伤害:
- 创建 Particle System(Fireball)
- 添加 Sphere Collider 到敌人
- 启用 Triggers 模块,设置 Enter=Callback
- 附加脚本处理 OnParticleTrigger
- 测试:火球击中敌人触发伤害和特效
Triggers 模块为粒子系统提供了强大的交互能力,通过合理配置和优化,可实现复杂的特效逻辑。如果需要特定场景的完整实现或性能分析,请提供更多细节!