#if UNITY_2019_3_11 || UNITY_2019_3_12 || UNITY_2019_3_13 || UNITY_2019_3_14 || UNITY_2019_3_15 || UNITY_2019_4_OR_NEWER #define SERIALIZE_FIELD_MASKABLE #endif using System.Collections.Generic; using System.Runtime.CompilerServices; using Coffee.UIParticleExtensions; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")] namespace Coffee.UIExtensions { /// /// Render maskable and sortable particle effect ,without Camera, RenderTexture or Canvas. /// [ExecuteAlways] [RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(CanvasRenderer))] public class UIParticle : MaskableGraphic { [HideInInspector][SerializeField] internal bool m_IsTrail = false; [Tooltip("Particle effect scale")] [SerializeField] private Vector3 m_Scale3D = new Vector3(10, 10, 10); [Tooltip("Animatable material properties. If you want to change the material properties of the ParticleSystem in Animation, enable it.")] [SerializeField] internal AnimatableProperty[] m_AnimatableProperties = new AnimatableProperty[0]; [Tooltip("Particles")] [SerializeField] private List m_Particles = new List(); private List m_Renderers = new List(); #if !SERIALIZE_FIELD_MASKABLE [SerializeField] private bool m_Maskable = true; #endif private DrivenRectTransformTracker _tracker; private Camera _orthoCamera; /// /// Should this graphic be considered a target for raycasting? /// public override bool raycastTarget { get { return false; } set { } } /// /// Particle effect scale. /// public float scale { get { return m_Scale3D.x; } set { m_Scale3D = new Vector3(value, value, value); } } /// /// Particle effect scale. /// public Vector3 scale3D { get { return m_Scale3D; } set { m_Scale3D = value; } } public List particles { get { return m_Particles; } } /// /// Get all base materials to render. /// public IEnumerable materials { get { for (var i = 0; i < m_Renderers.Count; i++) { if (!m_Renderers[i] || !m_Renderers[i].material) continue; yield return m_Renderers[i].material; } yield break; } } public override Material materialForRendering { get { return null; } } /// /// Paused. /// public bool isPaused { get; internal set; } public void Play() { particles.Exec(p => p.Simulate(0, false, true)); isPaused = false; } public void Pause() { particles.Exec(p => p.Pause()); isPaused = true; } public void Resume() { isPaused = false; } public void Stop() { particles.Exec(p => p.Stop()); isPaused = true; } public void Clear() { particles.Exec(p => p.Clear()); isPaused = true; } public void SetParticleSystemInstance(GameObject instance) { SetParticleSystemInstance(instance, true); } public void SetParticleSystemInstance(GameObject instance, bool destroyOldParticles) { if (!instance) return; foreach (Transform child in transform) { var go = child.gameObject; go.SetActive(false); if (!destroyOldParticles) continue; #if UNITY_EDITOR if (!Application.isPlaying) DestroyImmediate(go); else #endif Destroy(go); } var tr = instance.transform; tr.SetParent(transform, false); tr.localPosition = Vector3.zero; RefreshParticles(instance); } public void SetParticleSystemPrefab(GameObject prefab) { if (!prefab) return; SetParticleSystemInstance(Instantiate(prefab.gameObject), true); } public void RefreshParticles() { RefreshParticles(gameObject); } public void RefreshParticles(GameObject root) { if (!root) return; root.GetComponentsInChildren(particles); particles.RemoveAll(x => x.GetComponentInParent() != this); foreach (var ps in particles) { var tsa = ps.textureSheetAnimation; if (tsa.mode == ParticleSystemAnimationMode.Sprites && tsa.uvChannelMask == 0) tsa.uvChannelMask = UVChannelFlags.UV0; } RefreshParticles(particles); } public void RefreshParticles(List particles) { GetComponentsInChildren(m_Renderers); for (var i = 0; i < m_Renderers.Count; i++) { GetRenderer(i).Clear(i); } var j = 0; for (var i = 0; i < particles.Count; i++) { GetRenderer(j++).Set(this, particles[i], false); if (particles[i].trails.enabled) { GetRenderer(j++).Set(this, particles[i], true); } } } internal void UpdateTransformScale() { //var newScale = Vector3.one; //if (uiScaling) //{ // newScale = transform.parent.lossyScale.Inverse(); //} var newScale = transform.parent.lossyScale.Inverse(); if (transform.localScale != newScale) { transform.localScale = newScale; } } internal void UpdateRenderers() { var bakeCamera = GetBakeCamera(); for (var i = 0; i < m_Renderers.Count; i++) { m_Renderers[i].UpdateMesh(bakeCamera); } } protected override void OnEnable() { #if !SERIALIZE_FIELD_MASKABLE maskable = m_Maskable; #endif _tracker.Add(this, rectTransform, DrivenTransformProperties.Scale); UIParticleUpdater.Register(this); RegisterDirtyMaterialCallback(UpdateRendererMaterial); RefreshParticles(particles); base.OnEnable(); } /// /// This function is called when the behaviour becomes disabled. /// protected override void OnDisable() { _tracker.Clear(); UIParticleUpdater.Unregister(this); m_Renderers.ForEach(r => r.Clear()); UnregisterDirtyMaterialCallback(UpdateRendererMaterial); base.OnDisable(); } protected override void UpdateMaterial() { } /// /// Call to update the geometry of the Graphic onto the CanvasRenderer. /// protected override void UpdateGeometry() { } /// /// Callback for when properties have been changed by animation. /// protected override void OnDidApplyAnimationProperties() { } private void UpdateRendererMaterial() { for (var i = 0; i < m_Renderers.Count; i++) { if (!m_Renderers[i]) continue; m_Renderers[i].maskable = maskable; m_Renderers[i].SetMaterialDirty(); } } internal UIParticleRenderer GetRenderer(int index) { if (m_Renderers.Count <= index) { m_Renderers.Add(UIParticleRenderer.AddRenderer(this, index)); } return m_Renderers[index]; } private Camera GetBakeCamera() { if (!canvas) return Camera.main; // World camera. var root = canvas.rootCanvas; if (root.renderMode != RenderMode.ScreenSpaceOverlay) return root.worldCamera ? root.worldCamera : Camera.main; // Create ortho-camera. if (!_orthoCamera) { _orthoCamera = GetComponentInChildren(); if (!_orthoCamera) { var go = new GameObject("UIParticleOverlayCamera") { hideFlags = HideFlags.DontSave, }; go.SetActive(false); go.transform.SetParent(transform, false); _orthoCamera = go.AddComponent(); _orthoCamera.enabled = false; } } // var size = ((RectTransform)root.transform).rect.size; _orthoCamera.orthographicSize = Mathf.Max(size.x, size.y) * root.scaleFactor; _orthoCamera.transform.SetPositionAndRotation(new Vector3(0, 0, -1000), Quaternion.identity); _orthoCamera.orthographic = true; _orthoCamera.farClipPlane = 2000f; return _orthoCamera; } } }