Skip to content

Instantly share code, notes, and snippets.

@b3x206
Created February 16, 2024 16:38
Show Gist options
  • Save b3x206/6abf5a1b3f182479987a36842fffe2c1 to your computer and use it in GitHub Desktop.
Save b3x206/6abf5a1b3f182479987a36842fffe2c1 to your computer and use it in GitHub Desktop.
A Unity Particle System particles attractor, useful for VFX/Other purposes.
// ParticleAttractor.cs
// Attracts ParticleSystem particles towards given transform
// The code can be used for anything without attribution and it's from a part of 'Flag Race'
using Unity.Collections;
using UnityEngine;
using UnityEngine.Serialization;
/// <summary>
/// Attracts unity particle system (<see cref="ParticleSystem"/>) particles to given object <see cref="targetTransform"/>.
/// <br>Can be used on effects such as a powering up effect.</br>
/// <br/>
/// <br>Unlike most particle system attractors, this version doesn't allocate much C# heap garbage. This is because of the usage of 'NativeArray'.</br>
/// </summary>
[RequireComponent(typeof(ParticleSystem)), ExecuteAlways]
public class ParticleAttractor : MonoBehaviour
{
public Transform targetTransform;
[SerializeField, FormerlySerializedAs("particleSystemAllocSize")]
private int m_ParticleSystemAllocSize = 256;
public int ParticleSystemAllocSize
{
get => m_ParticleSystemAllocSize;
set
{
m_ParticleSystemAllocSize = value;
// Resize array :
// There exists an extension to resize a NativeArray but i won't use that.
// This is because the particle system does not care about prior values (grug not care about value)
// grug want no GC Alloc in profiler
if (m_Particles.IsCreated)
{
// We can discard the previous data as it's just going to be overwritten with something else.
m_Particles.Dispose();
}
m_Particles = new NativeArray<ParticleSystem.Particle>(m_ParticleSystemAllocSize, Allocator.Persistent);
}
}
private ParticleSystem m_TargetParticleSystem;
private ParticleSystem TargetParticleSystem
{
get
{
if (m_TargetParticleSystem == null)
{
m_TargetParticleSystem = GetComponent<ParticleSystem>();
}
// Assert the 'simulationSpace'
ParticleSystem.MainModule main = m_TargetParticleSystem.main;
main.simulationSpace = ParticleSystemSimulationSpace.World;
return m_TargetParticleSystem;
}
}
/// <summary>
/// List of current manually managed particles.
/// </summary>
private NativeArray<ParticleSystem.Particle> m_Particles;
private void Start()
{
if (!Application.isPlaying)
{
return;
}
if (targetTransform == null)
{
// Not a very important thing (to log), ignore as the transform parent could be the target object.
// Debug.Log($"[ParticleAttractor::Awake] Field 'targetTransform' was left blank on object '{this.GetPath()}'. Assigning self as the object.");
targetTransform = transform;
}
}
private void LateUpdate()
{
if (!Application.isPlaying && targetTransform == null)
{
return; // Don't do anything if it isn't running.
}
if (!m_Particles.IsCreated)
{
m_Particles = new NativeArray<ParticleSystem.Particle>(m_ParticleSystemAllocSize, Allocator.Persistent);
}
if (TargetParticleSystem.isPlaying)
{
int size = TargetParticleSystem.GetParticles(m_Particles);
Vector3 targetPos = targetTransform.position;
// ---
// Attraction behaviour can be customized here, you can use a 'Func<out ParticleSystem.Particle, in Vector3>'
// variable with a default interpolation (like in here) to make it changeable..
// -
// This current behaviour makes the particle closer to the target transform's
// position depending on the lifetime remaining on the given particle.
// ---
for (int i = 0; i < size; i++)
{
ParticleSystem.Particle setParticle = m_Particles[i];
setParticle.position += (targetPos - setParticle.position) / setParticle.remainingLifetime * Time.deltaTime;
m_Particles[i] = setParticle;
}
// aabb / unrelated error(s) because we are putting invalid, default data to the SetParticles function.
// Because of this, always define the size on the 'SetParticles'
TargetParticleSystem.SetParticles(m_Particles, size);
}
}
private void OnDestroy()
{
// Dispose the AllocationType.Persistent NativeArray
if (m_Particles.IsCreated)
{
m_Particles.Dispose();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment