Shuriken Particle System Scripting and Collision

Hello,

I have developed a script for a Shuriken particle system, where I am controlling all particles through script. So basically every update() I am setting each particles position independently. Everything runs very well, but I am trying to find when particles collide with something. I understand that I am to use the OnParticleCollision method in the script for the particle system, along with checking the ‘SendCollsionMessages’ box in the collision module for the particle system.

When I us it on a Shuriken system that I am not controlling through script I can get the method to fire. In my script, however, where I am controlling the position myself, I cannot get the method to fire. I am assume that the 2 are related, but have no proof. Can anyone confirm that controlling a Shuriken system’s particle positions through script renders you unable to detect collisions. If it has done before I would love to understand what I might be doing wrong.

Thanks in advance

Particle collision is calculated based on the particle’s velocity as well as it’s position. When you set a particle’s position you’re not actually affecting it’s velocity and so if your particles start out with zero velocity, they’ll never collide with anything.

Try something like this:

void Update () {
	if(parts.Length < pSys.particleCount)
		parts = new ParticleSystem.Particle[pSys.particleCount];

	int count = pSys.GetParticles(parts);

	for(int i=0; i<count; i++){
		ParticleSystem.Particle p = parts[i];
		Vector3 oldPosition = p.position;
		p.position = //NewPosition
		p.velocity = p.position - oldPosition;

		parts[i] = p;
	}

	pSys.SetParticles(parts, count);
}

You store the particle’s old position, set it’s new position and then get the velocity based on those two values.

Thank you so much for your response. I will try this when I get off of work tonight. I was having a hard time figuring out what to do with p.velocity since setting it does nothing on its own. This makes total sense and I would expect it to work. I will report back once I run some tests this evening.

Thanks again!

Unfortunately, this is still not working. I have tried setting the velocity as you said above, I have also tried setting it to the particles direction (since I keep track of it) and the particle’s direction.normalized, just to see if I can get anything and I am still not getting any collisions to be detected.

I am sure that this was still a piece I was missing and it has heightened my understanding of how this particle system works, but I still don’t get what I am missing. I am going to try to strip away anything that I don’t need and try to get it to work on a simple level. Please let me know if you have any other thoughts and, once again, thank you for the help!

If you want you can post the code and I’ll take a look tonight.

So here is a very dumbed down version of what I am doing… The real thing inherits from a long list of other behaviors and it would just be too much lol. But this is in essence what I am doing. This code will fire particles in a direction (z) according to how far away from the emitter the previous particle is. If the last active particle’s distance is greater than the ‘emmitAtDistance’ (oops spelling), the next particle will fire. I do not ‘GetParticles’ to start off like in your example, I am basically creating them from a parameter but in this case ‘500’ is what I used for simplicity.

using UnityEngine;
using System.Collections;

public class Collisiontest : MonoBehaviour {
	ParticleSystem.Particle [] parts;
	ParticleHelper [] partsH;

	float emmitAtDistance = 10;
	// Use this for initialization
	void Start () {
		parts = new ParticleSystem.Particle[500];
		partsH = new ParticleHelper[500];
		//create particles and particle helper for each
		for(int i = 0; i < parts.Length; i++)
		{
			ParticleSystem.Particle p = parts[i] ;
			p.size = 5;
			p.color = Color.clear;
			p.position = this.transform.position;
			parts[i] = p;
			partsH[i] = new ParticleHelper();
		}
		//make first particle animate
		AnimateParticle(0);
	}
	
	// Update is called once per frame
	void Update () {
		for(int i=0; i < parts.Length; i++){
			
			ParticleSystem.Particle p = parts[i];
			//if is alive, update the position
			if(partsH[i].isAlive)
			{
				Vector3 oldPosition = p.position;

				//increment z position by 2
				var z = oldPosition.z + 2;//NewPosition
				var x = p.position.x;
				var y = p.position.y;

				//calculate distance
				partsH[i].distance = Vector3.Distance(this.transform.position, p.position);

				//if distance of this particle is greater than distance required to fire next particle, and next particle is not alive (and we are not at the end of the array)
				if(partsH[i].distance > this.emmitAtDistance  i + 1 < parts.Length  !partsH[i + 1].isAlive)
				{
					AnimateParticle(i+1);
				}

				p.position = new Vector3(x,y,z);
			    p.velocity = p.position - oldPosition;
				//Debug.Log(p.velocity.ToString()+ "-" + p.position.ToString());
			

			}
			
			parts[i] = p;
			
		}	
		
		particleSystem.SetParticles(parts, parts.Length);
	}

	void AnimateParticle(int particleNumber)
	{
		partsH[particleNumber].isAlive = true;
		parts[particleNumber].color = Color.red;
	}

	void OnParticleCollision(GameObject other)
	{
		Debug.Log ("Collision");
	}

	internal class ParticleHelper
	{
		public bool isAlive = false;
		public float distance = 0;

		public ParticleHelper()
		{
			isAlive = false;
			distance = 0;
		}
	}
}

This is the particle systems setup in unity, real simple

Just drop this script on the particle system and play. You can un-comment line 53 if you want to see velocity and position in the debug window. I just drop a cube into the scene in front of the where the particles fire from.

Thanks for taking the time to look at this. I am stuck.

So here’s what I found.

Number one issue is that your particle system isn’t playing so you’ll never get a collision event even if the particles are moving.
Secondly I found that it wasn’t that I was setting both position and velocity that made it work in my test case. It was the fact that velocity was being applied to the particles, they were moving, and the particle system was able to call collision events.

Basically if all you do is move the particles yourself you’ll never get particle collisions because moving them yourself breaks the simulation.

You can, however guide the simulation by controlling the particles’ velocity, rather than their position:

public float particleSpeed = 5;

void Start () {
	particleSystem.Emit(500);
	parts = new ParticleSystem.Particle[500];
	
	partsH = new ParticleHelper[500];
	
	particleSystem.GetParticles(parts);
	
	//create particles and particle helper for each
	for(int i = 0; i < parts.Length; i++)
	{
		
		ParticleSystem.Particle p = parts[i] ;
		p.size = 5;
		p.lifetime = 20;
		p.color = Color.clear;
		
		p.position = this.transform.position;
		
		parts[i] = p;
		
		partsH[i] = new ParticleHelper();
	}
	
	//make first particle animate
	AnimateParticle(0);
	particleSystem.Play ();
}

void Update () {
	particleSystem.GetParticles(parts);
	for(int i=0; i < parts.Length; i++){
		ParticleSystem.Particle p = parts[i];
		
		//if is alive, update the position
		if(partsH[i].isAlive)
		{
			Vector3 oldPosition = p.position;
			
			//increment z position by 2
			Vector3 newPosition = p.position;
			newPosition.z += 2;
			
			//calculate distance
			partsH[i].distance = Vector3.Distance(this.transform.position, p.position);
			
			//if distance of this particle is greater than distance required to fire next particle, and next particle is not alive (and we are not at the end of the array)
			if(partsH[i].distance > this.emmitAtDistance  i + 1 < parts.Length  !partsH[i + 1].isAlive)
			{
				AnimateParticle(i+1);
			}
			
			p.velocity = (newPosition - oldPosition) * particleSpeed;
			p.lifetime = 20;
		}
		parts[i] = p;
	}   
	particleSystem.SetParticles(parts, parts.Length);

I didn’t change much but the major change is that instead of creating all the particles yourself I set the system to emit them on startup. Then get the emitted particles, set them as needed and Play() the particle system.
Each frame you’ll want to set the particle’s life back up to whatever it should be and you do need to both get and set the particles for the simulation to run correctly.

For guiding the particles, rather than setting their position, you get the desired position and set the velocity so they will end up there. This runs the particle system’s simulation and doesn’t break the collision detection.

Hope that helps.

Thank you once again! This works and actually makes my job easier, since I keep track of the direction of the particle, I can just have its velocity equal to its normalized direction * its speed. There is no need to increment its position, or even keep track of it in my helper class. I am now getting collisions. You are awesome, thanks for the help.

The many examples of scripting with Particle systems I have found online never use velocity or even particleSystem.Play(), they were doing it as I was, but not not wanting to use it the way I was. Thanks for taking the time to help me out.

Happy to help. Good luck with the rest and feel free to PM me if you need any more help.