Creative way to attract particles

I've been playing with particle emitters in code, doing my best to produce a something like particle gravity.

I've written a very simple component script called "Attractor" that you attach to a gameobject. This then "attracts" all the particles of a specific emitter by iterating through their positions and lerping towards the transform.position. This works pretty quickly, and I'm happy with the overhead.

My question is, is there a quick and dirty way to only move the particles that are nearest the object (or possibly limit the effect of the attraction of the particles the further they are from the object). I've seen people use Physics.OverlapSphere to find objects within range, but couldn't seem to make this work with particles. Another promising angle is to change the Vector3.Lerp to some sort of exponential ease-in, so that the further away ones move slower. I tried checking distance, but that was clearly too expensive and ground my system to a halt. (this is for circa 2000 particles)

My code so far is roughly the following (this if from memory- will update with actual code this evening). Note that it needs an instantiated Particle system called "StrangeParticles", which is a one-shot system of particles. This script is then attached to gameobject, the attractor.

public ParticleEmitter p;
public Particle[] particles;

void Start()
{
    p = (ParticleEmitter)(GameObject.Find("StrangeParticles").GetComponent(typeof(ParticleEmitter)));
    particles = p.particles;
}

void Update()
{
    for (int i=0; i < particles.GetUpperBound(0))
    {
        particles_.position = Vector3.Lerp(particles*.position,transform.position,Time.deltaTime / 2.0f);*_
 _*}*_
 _*p.particles = particles;*_
_*}*_
_*```*_

You have the particle position already, and the script automatically comes with a link to it's own position (via transform). So it's a simply a matter of checking if the particle is in range of the affector object before applying your position change.

Sorry if there are any errors, i did not test it, but this should be the general idea...

public ParticleEmitter p;
public Particle[] particles;
public float affectDistance;
float sqrDist;
Transform thisTransform;

void Start()
{
    thisTransform = transform;
    p = (ParticleEmitter)(GameObject.Find("StrangeParticles").GetComponent(typeof(ParticleEmitter)));
    particles = p.particles;
    sqrDist = affectDistance*affectDistance;
}

void Update()
{
    float dist;
    for (int i=0; i < particles.GetUpperBound(0))
    {
        dist = Vector3.SqrMagnitude(thisTransform.position - particles*.position);*
 *if (dist < sqrDist) {*
 <em>particles_.position = Vector3.Lerp(particles*.position,transform.position,Time.deltaTime / 2.0f);*_</em>
 <em>_*}*_</em>
 <em>_*}*_</em>
 <em>_*p.particles = particles;*_</em>
<em>_*}*_</em>
<em>_*```*_</em>

based on the simple solution suggested in the question, I have found that fiddling around with the velocity of the particles instead of their actual position results in a much more natural motion. My code right now looks like this:

ParticleSystem.Particle[] ps = new ParticleSystem.Particle[particles.particleCount];
particles.GetParticles (ps);
for (int i =0; i<ps.Length; i++)
	ps <em>.velocity = Vector3.Lerp (ps _.velocity, (particlesAttractor.position - ps *.position).normalized, 0.1f);*_</em>

* particles.SetParticles (ps, ps.Length);*
So I am actually lerping the current particle velocity with the normalized vector that points to my attractor, with a factor of .1. Other nice things that you can do is take the lifetime/startlifetime of particles into consideration for the lerping factor, to particles which have shorter lifespans move faster. Or you can affect the power of the attractor based on the particle.size/particle.startsize.
Also I have found that if the simulation space of the particle system is set to local and not to world, positions of particles are relative to the particle system in code also. In this situation, you need to calculate the position of your attractor in the local space of the particle system, before using that to modify particle velocities or positions. This is important when you have a moving particle system with simulation in local space

Could someone please provide a working script? Just can’t seem to get it working… Thnk you :slight_smile:

I have found a simple non code way to make particles look as though they are being attracted.
Get your favorite modeling program or any modeling program that lets you modify normals.
Make your shape then set the normals inside and export to unity.
in unity set the particle emitter shape parameter to your custom shape then tweak to specifications.

Hello, I’m new to C# and Unity and have visited this page many, many times looking for a way to make a particle attractor script. I took Pyro’s advice re the smoothing and implemented a feature to try and remove some hanging particles (which needs improvement) and have been at this for a week! But I learned a lot! Excuse the notorious amount of comments, they were mostly for me as I slowly gained an understanding of what was going on.

Here is the script, instructions at the bottom.
UPDATE removed all comments, neater script now, also implemented timer function.

using UnityEngine;
using System.Collections;

public class ParticleAttractTimed : MonoBehaviour 
{
	public float PullDistance = 200;
	public Transform MagnetPoint;
	public int TimeUntilPull = 0;

	private bool waitCalled = false;
	
	IEnumerator ParticleWait ()
	{
		waitCalled = true;
		yield return new WaitForSeconds(TimeUntilPull);
		TimeUntilPull = 0;
		Debug.Log("TimeUntilPull set to 0");
	}

	void ParticlePull ()
	{
		float sqrPullDistance = PullDistance * PullDistance;

		ParticleSystem.Particle[] x = new ParticleSystem.Particle[gameObject.particleSystem.particleCount+1]; 
		int y = particleSystem.GetParticles(x);

		for (int i = 0; i < y; i++)
		{
			Vector3 offset = MagnetPoint.localPosition - x*.position;*
  •  	//creates Vector3 variable based on the position of the target MagnetPoint (set by user) and the current particle position*
    
  •  	float sqrLen = offset.sqrMagnitude;*
    
  •  	//creats float type integer based on the square magnitude of the offset variable set above (faster than .Magnitude)*
    
  •  	if (sqrLen <= sqrPullDistance)*
    
  •  	{*
    

x_.position = Vector3.Lerp(x*.position, MagnetPoint.localPosition, Mathf.SmoothStep(0, 2, (Time.deltaTime / 0.1F)));
/Lerping moves an object between two vectors (syntax is FromVector, ToVector, Fraction) by a given fraction. In our example_
_ we take the position of particle i, of particle system x, and the local position of the MagnetPoint transform, and move the

particles in from x towards MagnetPoint over time. Lower the Time.deltaTime / # value to increase how fast the particle attracts*/
if ((x*.position-MagnetPoint.localPosition).magnitude <= 30)
{
x.lifetime = 0;
}
}
}*_

* gameObject.particleSystem.SetParticles(x, y);*
* return;*
* }*

* //Use this for initialization*
* void Start ()*
* {*
* Debug.Log("Time until pull equals " + TimeUntilPull);*
* }*

_ /Update is called once per frame and is good for simple timers, basing changes off frame rate and recieving input.
Note that if one frame takes longer to process than the next, Update will not be called consistently/

* void Update ()
{
if ((TimeUntilPull > 0) & (waitCalled == false))
{
Debug.Log(“Coroutine called”);
StartCoroutine(ParticleWait());
}
else if (TimeUntilPull == 0)
ParticlePull();
}
}*

1. You will need to create a particle emitter (or other object that can be passed as a transform) to act as a magnet point for your particles if you don’t already have one. I tested this script with mine set to a size of 1, no shape, no emission - only Renderer remained checked inside the Unity inspector. This script uses a localPosition call for the MagnetPoint so you may need to make it a child of another emitter, which contains both this transform and the particle system you want to manipulate.
3. Select the particle emitter you want to manipulate (the one whose particles you want to go somewhere) and attach this script to it (add component, script, this script).
4. Drag the particle emitter you created in step 1 to the public variable space titled ‘Magnet Point’.
5. Set the pull distance as desired. Keep in mind it will need to be relative to the scale of your particle effect and what you want to achieve - for example - in my effect, the magnet point was at the centre of a bunch of emitted particles and only those particles that were close enough to the magnet point were drawn in (so some float away, and others draw into the centre point).
Enjoy my first unity script!_