- Home /
How to detect when and where a particle hits a surface?
I'm trying to create an obstacle where meteors are falling onto the map and if they hit, it causes an explosions, at the point of contact, and throws back anything with a rigidbody.
The main issue is I cannot find a way to detect the particle collision (meteors) when the reach the plane (map).
I've tried OnParticleCollision, but to no avail. I've been at it for a couple hours and I feel I've exhausted all possible errors.
If there is something I'm overlooking or a better way of going about this task, I would greatly appreciate any help.
Thank you and have a good one.
Answer by Eudaimonium · Jun 03, 2016 at 09:47 AM
The OnParticleCollision method should be what you're looking for. I'm implementing a magic spell system that uses particle collisions for damage, and it works pretty reliably.
Here's my solution, In the Master Particle System (the one where each particle represents your meteor, not it's trails or sparks or whatever)
Enable the "Collision" tab in the particle Inspector
Enable the "Send Collision Messages" checkbox in the tab
Set Collision Mode to "World" and "3D"
Enable "Enable Dynamic Colliders"
In a script that is attached to the same game object this particle system is (this is important, they have to be on the same object), implement the function like so:
private ParticleSystem PSystem;
private ParticleCollisionEvent[] CollisionEvents;
// Grab your PSystem in Start or Awake method via GetComponent, and also:
//In Start or such: CollisionEvents = new ParticleCollisionEvent[8];
//Then, in this callback function:
public void OnParticleCollision(GameObject other)
{
int collCount = PSystem.GetSafeCollisionEventSize();
if (collCount > CollisionEvents.Length)
CollisionEvents = new ParticleCollisionEvent[collCount];
int eventCount = PSystem.GetCollisionEvents(other, CollisionEvents);
for (int i = 0; i < eventCount; i++)
{
//TODO: Do your collision stuff here.
// You can access the CollisionEvent[i] to obtaion point of intersection, normals that kind of thing
// You can simply use "other" GameObject to access it's rigidbody to apply force, or check if it implements a class that takes damage or whatever
}
}
It is a bit weird, considering how normally these kind of events are easily accessed in Unity's scripting but there you go, this is the way to do this particular example.
This is definitely looking good. However I'm one bug away from getting it to work.
using UnityEngine;
using System.Collections;
public class $$anonymous$$eteor_Explode : $$anonymous$$onoBehaviour {
public float radius = 5f;
public float power = 100f;
public float liftPower = 50f;
private ParticleSystem PSystem;
private ParticleCollisionEvent[] CollisionEvents;
void Start () {
PSystem = GetComponent<ParticleSystem> ();
}
public void OnParticleCollision(GameObject other)
{
int collCount = PSystem.GetSafeCollisionEventSize();
if (collCount > CollisionEvents.Length) {
CollisionEvents = new ParticleCollisionEvent[collCount];
}
int eventCount = PSystem.GetCollisionEvents(other, CollisionEvents);
for (int i = 0; i < eventCount; i++)
{
Explosions (CollisionEvents[i].intersection);
//TODO: Do your collision stuff here.
// You can access the CollisionEvent[i] to obtaion point of intersection, normals that kind of thing
// You can simply use "other" GameObject to access it's rigidbody to apply force, or check if it implements a class that takes damage or whatever
}
}
void Explosions(Vector3 intersection){
Vector3 explosionPos = intersection;
Collider[] colliders = Physics.OverlapSphere (explosionPos, radius);
foreach (Collider hit in colliders) {
if (hit && hit.GetComponent<Rigidbody> ()) {
hit.GetComponent<Rigidbody> ().AddExplosionForce (power, explosionPos, radius, liftPower);
}
}
GetComponent<AudioSource> ().Play ();
}
}
On line 19 I am receiving a NullReferenceException: Object reference not set to an instance of an object.
I'm going to try to keep at it, but if you could point anything out to me, I'd greatly appreciate it.
Ah of course, I'm sorry, I forgot to copy the initialization of the array.
In your Start function, simply initialize the array to maybe 8 events:
CollisionEvents = new ParticleCollisionEvent[8];
I think this should be it.
Works wonderfully now. Thank you for all your help.
Thanks, that worked great for an RPG using the free Effect Examples in the asset store.
Answer by denholmspurr · Apr 24, 2020 at 04:28 PM
@Eudaimonium Damn, the above solution is now obsolete.
How do I update
PSystem.GetCollisionEvents(other, CollisionEvents);?
I didn't even know this went obsolete. Apparently the extension method used in above example is marked as Obsolete... https://docs.unity3d.com/ScriptReference/ParticlePhysicsExtensions.GetCollisionEvents.html
But no alternative appears to be given. Even this documentation page links and suggests to use the Obsolete method: https://docs.unity3d.com/ScriptReference/$$anonymous$$onoBehaviour.OnParticleCollision.html
I've tried googling it a bit, but I cannot find a replacement for this method. $$anonymous$$y advice is just to keep using it. It would be great if we could get new API before obsoleting or deprecating old one, Unity.
So Visual Studio suggests List as an alternative...
When you say "keep using it",can you still use obsolete commands? I think my Visual Studio comes up with an error and won't attach.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class particleCollision2 : $$anonymous$$onoBehaviour { public ParticleSystem pSystem; public List collisionEvents; public int score; public ParticleSystem pSystem2; public List collisionEvents2; public int score2; private void Start() { collisionEvents = new List(); collisionEvents2 = new List(); } public void OnParticleCollision(GameObject hurricane) { int eventCount = pSystem.GetCollisionEvents(hurricane, collisionEvents); int i = 0; while (i < eventCount) { score++; } int eventCount2 = pSystem2.GetCollisionEvents(hurricane, collisionEvents); int i2 = 0; while (i2 < eventCount) { score2++; } } }$$anonymous$$yAttempt using List (sorry for formatting)
You probably meant to use List with a generic parameter ins$$anonymous$$d of just "List" which does not exist.
Find my username on Unity Forums and send me a direct message there. I'll help you out.
I've tried using the obsolete code and it allows me to attach but it's not registering any collisions.
Answer by blakelowe · May 25, 2020 at 10:07 PM
For anyone wondering how to do this today, this tutorial goes through the current correct api usage. https://www.youtube.com/watch?v=C3pf9XUsEtM
I've been using this tutorial. It uses the obsolete GetCollisionEvents. Still can't find an alternative.
Answer by dubuu_ · Oct 14, 2021 at 05:59 PM
GetCollisionEvents function using ParticleCollisionEvent[]
is deprecated. Use List<ParticleCollisionEvent>
instead. You can use this as example:
private List<ParticleCollisionEvent> _particleCollisionEvents = new List<ParticleCollisionEvent>();
var eventCount = playerLaser.GetCollisionEvents(gameObject, _particleCollisionEvents);