From 386170cbf68ebf59d4510fe0a45cf83925ec9ba4 Mon Sep 17 00:00:00 2001 From: mob-sakai Date: Wed, 15 Jun 2022 10:24:31 +0900 Subject: [PATCH] feat: add particle attractor component --- Scripts/UIParticleAttractor.cs | 204 ++++++++++++++++++++++++++++ Scripts/UIParticleAttractor.cs.meta | 11 ++ Scripts/UIParticleUpdater.cs | 19 +++ 3 files changed, 234 insertions(+) create mode 100644 Scripts/UIParticleAttractor.cs create mode 100644 Scripts/UIParticleAttractor.cs.meta diff --git a/Scripts/UIParticleAttractor.cs b/Scripts/UIParticleAttractor.cs new file mode 100644 index 0000000..621a8d9 --- /dev/null +++ b/Scripts/UIParticleAttractor.cs @@ -0,0 +1,204 @@ +using UnityEngine; +using Coffee.UIParticleExtensions; +using UnityEngine.Events; +using System; + +namespace Coffee.UIExtensions +{ + [ExecuteAlways] + public class UIParticleAttractor : MonoBehaviour + { + public enum Movement + { + Linear, + Smooth, + Sphere, + } + + [SerializeField] + private ParticleSystem m_ParticleSystem; + + [Range(0.1f, 10f)] + [SerializeField] + private float m_DestinationRadius = 1; + + [Range(0f, 0.95f)] + [SerializeField] + private float m_DelayRate = 0; + + [Range(0.001f, 100f)] + [SerializeField] + private float m_MaxSpeed = 1; + + [SerializeField] + private Movement m_Movement; + + [SerializeField] + private UnityEvent m_OnAttracted; + + public float delay + { + get + { + return m_DelayRate; + } + set + { + m_DelayRate = value; + } + } + + public float maxSpeed + { + get + { + return m_MaxSpeed; + } + set + { + m_MaxSpeed = value; + } + } + + public Movement movement + { + get + { + return m_Movement; + } + set + { + m_Movement = value; + } + } + + private UIParticle _uiParticle; + + private void OnEnable() + { + if (m_ParticleSystem == null) + { + Debug.LogError("No particle system attached to particle attractor script", this); + enabled = false; + return; + } + + _uiParticle = m_ParticleSystem.GetComponentInParent(); + if (_uiParticle && !_uiParticle.particles.Contains(m_ParticleSystem)) + { + _uiParticle = null; + } + UIParticleUpdater.Register(this); + } + + private void OnDisable() + { + _uiParticle = null; + UIParticleUpdater.Unregister(this); + } + + internal void Attract() + { + if (m_ParticleSystem == null) return; + + var count = m_ParticleSystem.particleCount; + if (count == 0) return; + + var particles = ParticleSystemExtensions.GetParticleArray(count); + m_ParticleSystem.GetParticles(particles, count); + + var dstPos = GetDestinationPosition(); + 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; + } + + m_ParticleSystem.SetParticles(particles, count); + } + + private Vector3 GetDestinationPosition() + { + var isUI = _uiParticle && _uiParticle.enabled; + var psPos = m_ParticleSystem.transform.position; + var attractorPos = transform.position; + var dstPos = attractorPos; + if (m_ParticleSystem.main.simulationSpace == ParticleSystemSimulationSpace.Local) + { + dstPos = m_ParticleSystem.transform.InverseTransformPoint(dstPos); + if (isUI) + { + dstPos = dstPos.GetScaled(_uiParticle.transform.localScale, _uiParticle.scale3D.Inverse()); + } + } + else + { +#if UNITY_EDITOR + if (!Application.isPlaying && isUI) + { + var diff = dstPos - psPos; + diff = diff.GetScaled(_uiParticle.transform.localScale, _uiParticle.scale3D.Inverse()); + return psPos + diff; + } +#endif + if (isUI) + { + dstPos.Scale(_uiParticle.transform.localScale); + dstPos.Scale(_uiParticle.scale3D.Inverse()); + } + } + return dstPos; + } + + private Vector3 GetAttractedPosition(Vector3 current, Vector3 target, float duration, float time) + { + var speed = m_MaxSpeed; + 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); + } + + } +} \ No newline at end of file diff --git a/Scripts/UIParticleAttractor.cs.meta b/Scripts/UIParticleAttractor.cs.meta new file mode 100644 index 0000000..d5a3bab --- /dev/null +++ b/Scripts/UIParticleAttractor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00e55ae1441ff4583859c55384964d86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/UIParticleUpdater.cs b/Scripts/UIParticleUpdater.cs index 8472d69..0a67067 100644 --- a/Scripts/UIParticleUpdater.cs +++ b/Scripts/UIParticleUpdater.cs @@ -6,6 +6,7 @@ namespace Coffee.UIExtensions internal static class UIParticleUpdater { static readonly List s_ActiveParticles = new List(); + static readonly List s_ActiveAttractors = new List(); static readonly HashSet s_UpdatedGroupIds = new HashSet(); private static int frameCount = 0; @@ -29,6 +30,18 @@ namespace Coffee.UIExtensions s_ActiveParticles.Remove(particle); } + public static void Register(UIParticleAttractor attractor) + { + if (!attractor) return; + s_ActiveAttractors.Add(attractor); + } + + public static void Unregister(UIParticleAttractor attractor) + { + if (!attractor) return; + s_ActiveAttractors.Remove(attractor); + } + #if UNITY_EDITOR [UnityEditor.InitializeOnLoadMethod] #endif @@ -74,6 +87,12 @@ namespace Coffee.UIExtensions } s_UpdatedGroupIds.Clear(); + + // Attract + for(var i = 0;i< s_ActiveAttractors.Count;i++) + { + s_ActiveAttractors[i].Attract(); + } } public static void GetGroupedRenderers(int groupId, int index, List results)