| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- using System;
- using System.Collections.Generic;
- using Coffee.UIParticleInternal;
- using UnityEngine;
- using UnityEngine.Events;
- namespace Coffee.UIExtensions
- {
- [ExecuteAlways]
- public class UIParticleAttractor : MonoBehaviour, ISerializationCallbackReceiver
- {
- public enum Movement
- {
- Linear,
- Smooth,
- Sphere
- }
- public enum UpdateMode
- {
- Normal,
- UnscaledTime
- }
- [SerializeField]
- [HideInInspector]
- private ParticleSystem m_ParticleSystem;
- [SerializeField]
- private List<ParticleSystem> m_ParticleSystems = new List<ParticleSystem>();
- [Range(0.1f, 10f)]
- [SerializeField]
- private float m_DestinationRadius = 1;
- [Range(0f, 0.95f)]
- [SerializeField]
- private float m_DelayRate;
- [Range(0.001f, 100f)]
- [SerializeField]
- private float m_MaxSpeed = 1;
- [SerializeField]
- private Movement m_Movement;
- [SerializeField]
- private UpdateMode m_UpdateMode;
- [SerializeField]
- private UnityEvent m_OnAttracted;
- private List<UIParticle> _uiParticles = new List<UIParticle>();
- public float destinationRadius
- {
- get => m_DestinationRadius;
- set => m_DestinationRadius = Mathf.Clamp(value, 0.1f, 10f);
- }
- public float delay
- {
- get => m_DelayRate;
- set => m_DelayRate = value;
- }
- public float maxSpeed
- {
- get => m_MaxSpeed;
- set => m_MaxSpeed = value;
- }
- public Movement movement
- {
- get => m_Movement;
- set => m_Movement = value;
- }
- public UpdateMode updateMode
- {
- get => m_UpdateMode;
- set => m_UpdateMode = value;
- }
- public UnityEvent onAttracted
- {
- get => m_OnAttracted;
- set => m_OnAttracted = value;
- }
- /// <summary>
- /// The target ParticleSystems to attract. Use <see cref="AddParticleSystem"/> and
- /// <see cref="RemoveParticleSystem"/> to modify the list.
- /// </summary>
- public IReadOnlyList<ParticleSystem> particleSystems => m_ParticleSystems;
- public void AddParticleSystem(ParticleSystem ps)
- {
- if (m_ParticleSystems == null)
- {
- m_ParticleSystems = new List<ParticleSystem>();
- }
- var i = m_ParticleSystems.IndexOf(ps);
- if (0 <= i) return; // Already added: skip
- m_ParticleSystems.Add(ps);
- _uiParticles.Clear();
- }
- public void RemoveParticleSystem(ParticleSystem ps)
- {
- if (m_ParticleSystems == null)
- {
- return;
- }
- var i = m_ParticleSystems.IndexOf(ps);
- if (i < 0) return; // Not found. skip
- m_ParticleSystems.RemoveAt(i);
- _uiParticles.Clear();
- }
- private void Awake()
- {
- UpgradeIfNeeded();
- }
- private void OnEnable()
- {
- UIParticleUpdater.Register(this);
- }
- private void OnDisable()
- {
- UIParticleUpdater.Unregister(this);
- }
- private void OnDestroy()
- {
- _uiParticles = null;
- m_ParticleSystems = null;
- }
- internal void Attract()
- {
- // Collect UIParticle if needed (same size as m_ParticleSystems)
- CollectUIParticlesIfNeeded();
- for (var particleIndex = 0; particleIndex < m_ParticleSystems.Count; particleIndex++)
- {
- var particleSystem = m_ParticleSystems[particleIndex];
- // Skip: The ParticleSystem is not active
- if (particleSystem == null || !particleSystem.gameObject.activeInHierarchy) continue;
- // Skip: No active particles
- var count = particleSystem.particleCount;
- if (count == 0) continue;
- var particles = ParticleSystemExtensions.GetParticleArray(count);
- particleSystem.GetParticles(particles, count);
- var uiParticle = _uiParticles[particleIndex];
- var dstPos = GetDestinationPosition(uiParticle, particleSystem);
- for (var i = 0; i < count; i++)
- {
- // Attracted
- var p = particles[i];
- if (0f < p.remainingLifetime && Vector3.Distance(p.position, dstPos) < m_DestinationRadius)
- {
- p.remainingLifetime = 0f;
- particles[i] = p;
- if (m_OnAttracted != null)
- {
- try
- {
- m_OnAttracted.Invoke();
- }
- catch (Exception e)
- {
- Debug.LogException(e);
- }
- }
- continue;
- }
- // Calc attracting time
- var delayTime = p.startLifetime * m_DelayRate;
- var duration = p.startLifetime - delayTime;
- var time = Mathf.Max(0, p.startLifetime - p.remainingLifetime - delayTime);
- // Delay
- if (time <= 0) continue;
- // Attract
- p.position = GetAttractedPosition(p.position, dstPos, duration, time);
- p.velocity *= 0.5f;
- particles[i] = p;
- }
- particleSystem.SetParticles(particles, count);
- }
- }
- private Vector3 GetDestinationPosition(UIParticle uiParticle, ParticleSystem particleSystem)
- {
- var isUI = uiParticle && uiParticle.enabled;
- var psPos = particleSystem.transform.position;
- var attractorPos = transform.position;
- var dstPos = attractorPos;
- var isLocalSpace = particleSystem.IsLocalSpace();
- if (isLocalSpace)
- {
- dstPos = particleSystem.transform.InverseTransformPoint(dstPos);
- }
- if (isUI)
- {
- var inverseScale = uiParticle.parentScale.Inverse();
- var scale3d = uiParticle.scale3DForCalc;
- dstPos = dstPos.GetScaled(inverseScale, scale3d.Inverse());
- // Relative mode
- if (uiParticle.positionMode == UIParticle.PositionMode.Relative)
- {
- var diff = uiParticle.transform.position - psPos;
- diff.Scale(scale3d - inverseScale);
- diff.Scale(scale3d.Inverse());
- dstPos += diff;
- }
- #if UNITY_EDITOR
- if (!Application.isPlaying && !isLocalSpace)
- {
- dstPos += psPos - psPos.GetScaled(inverseScale, scale3d.Inverse());
- }
- #endif
- }
- return dstPos;
- }
- private Vector3 GetAttractedPosition(Vector3 current, Vector3 target, float duration, float time)
- {
- var speed = m_MaxSpeed;
- switch (m_UpdateMode)
- {
- case UpdateMode.Normal:
- speed *= 60 * Time.deltaTime;
- break;
- case UpdateMode.UnscaledTime:
- speed *= 60 * Time.unscaledDeltaTime;
- break;
- }
- switch (m_Movement)
- {
- case Movement.Linear:
- speed /= duration;
- break;
- case Movement.Smooth:
- target = Vector3.Lerp(current, target, time / duration);
- break;
- case Movement.Sphere:
- target = Vector3.Slerp(current, target, time / duration);
- break;
- }
- return Vector3.MoveTowards(current, target, speed);
- }
- private void CollectUIParticlesIfNeeded()
- {
- if (m_ParticleSystems.Count == 0 || _uiParticles.Count != 0) return;
- // Expand capacity
- if (_uiParticles.Capacity < m_ParticleSystems.Capacity)
- {
- _uiParticles.Capacity = m_ParticleSystems.Capacity;
- }
- // Find UIParticle that controls the ParticleSystem
- for (var i = 0; i < m_ParticleSystems.Count; i++)
- {
- var ps = m_ParticleSystems[i];
- if (ps == null)
- {
- _uiParticles.Add(null);
- continue;
- }
- var uiParticle = ps.GetComponentInParent<UIParticle>(true);
- _uiParticles.Add(uiParticle.particles.Contains(ps) ? uiParticle : null);
- }
- }
- #if UNITY_EDITOR
- private void OnValidate()
- {
- _uiParticles.Clear();
- }
- #endif
- void ISerializationCallbackReceiver.OnBeforeSerialize()
- {
- UpgradeIfNeeded();
- }
- void ISerializationCallbackReceiver.OnAfterDeserialize()
- {
- }
- private void UpgradeIfNeeded()
- {
- // Multiple ParticleSystems support: from 'm_ParticleSystem' to 'm_ParticleSystems'
- if (m_ParticleSystem != null)
- {
- if (!m_ParticleSystems.Contains(m_ParticleSystem))
- {
- m_ParticleSystems.Add(m_ParticleSystem);
- }
- m_ParticleSystem = null;
- Debug.Log($"Upgraded!");
- }
- }
- }
- }
|