diff --git a/Packages/src/Editor/UIParticleEditor.cs b/Packages/src/Editor/UIParticleEditor.cs
index 857af90..df1d514 100644
--- a/Packages/src/Editor/UIParticleEditor.cs
+++ b/Packages/src/Editor/UIParticleEditor.cs
@@ -2,7 +2,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
-using UnityEditor.UI;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.UI;
@@ -18,6 +17,7 @@ using Object = UnityEngine.Object;
#if UNITY_2021_2_OR_NEWER
using UnityEditor.SceneManagement;
+
#elif UNITY_2018_3_OR_NEWER
using UnityEditor.Experimental.SceneManagement;
#endif
@@ -26,7 +26,7 @@ namespace Coffee.UIExtensions
{
[CustomEditor(typeof(UIParticle))]
[CanEditMultipleObjects]
- internal class UIParticleEditor : GraphicEditor
+ internal class UIParticleEditor : Editor
{
#if UNITY_2021_2_OR_NEWER
#if UNITY_2022_1_OR_NEWER
@@ -146,10 +146,8 @@ namespace Coffee.UIExtensions
///
/// This function is called when the object becomes enabled and active.
///
- protected override void OnEnable()
+ private void OnEnable()
{
- base.OnEnable();
-
_maskable = serializedObject.FindProperty("m_Maskable");
_scale3D = serializedObject.FindProperty("m_Scale3D");
_animatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
@@ -529,9 +527,7 @@ namespace Coffee.UIExtensions
{
if (!p || (ignoreCurrent && target == p)) return;
- var cr = p.canvasRenderer;
DestroyImmediate(p);
- DestroyImmediate(cr);
#if UNITY_2018_3_OR_NEWER
var stage = PrefabStageUtility.GetCurrentPrefabStage();
diff --git a/Packages/src/Runtime/UIParticle.cs b/Packages/src/Runtime/UIParticle.cs
index 1381c11..4c26549 100644
--- a/Packages/src/Runtime/UIParticle.cs
+++ b/Packages/src/Runtime/UIParticle.cs
@@ -6,7 +6,6 @@ using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Rendering;
using UnityEngine.Serialization;
-using UnityEngine.UI;
using Random = UnityEngine.Random;
[assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")]
@@ -19,7 +18,7 @@ namespace Coffee.UIExtensions
[ExecuteAlways]
[RequireComponent(typeof(RectTransform))]
[RequireComponent(typeof(CanvasRenderer))]
- public class UIParticle : MaskableGraphic, ISerializationCallbackReceiver
+ public class UIParticle : UIBehaviour, ISerializationCallbackReceiver
{
public enum AutoScalingMode
{
@@ -45,16 +44,19 @@ namespace Coffee.UIExtensions
[HideInInspector]
[SerializeField]
+ [Obsolete]
internal bool m_IsTrail;
[HideInInspector]
[FormerlySerializedAs("m_IgnoreParent")]
[SerializeField]
+ [Obsolete]
private bool m_IgnoreCanvasScaler;
[HideInInspector]
[SerializeField]
- private bool m_AbsoluteMode;
+ [Obsolete]
+ internal bool m_AbsoluteMode;
[Tooltip("Particle effect scale")]
[SerializeField]
@@ -93,25 +95,54 @@ namespace Coffee.UIExtensions
[SerializeField]
[Tooltip("Prevent the root-Canvas scale from affecting the hierarchy-scaled ParticleSystem.")]
- private bool m_AutoScaling = true;
+ [Obsolete]
+ internal bool m_AutoScaling;
[SerializeField]
[Tooltip("Transform: Transform.lossyScale (=world scale) will be set to (1, 1, 1)." +
"UIParticle: UIParticle.scale will be adjusted.")]
private AutoScalingMode m_AutoScalingMode = AutoScalingMode.Transform;
+ [SerializeField]
+ private bool m_Maskable = true;
+
private readonly List _renderers = new List();
+ private Canvas _canvas;
private int _groupId;
private Camera _orthoCamera;
private DrivenRectTransformTracker _tracker;
- ///
- /// Should this graphic be considered a target for ray-casting?
- ///
- public override bool raycastTarget
+ public RectTransform rectTransform => transform as RectTransform;
+
+ public Canvas canvas
{
- get => false;
- set { }
+ get
+ {
+ if (_canvas) return _canvas;
+
+ var tr = transform;
+ while (tr && !_canvas)
+ {
+ if (tr.TryGetComponent(out _canvas)) return _canvas;
+ tr = tr.parent;
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Does this graphic allow masking.
+ ///
+ public bool maskable
+ {
+ get => m_Maskable;
+ set
+ {
+ if (value == m_Maskable) return;
+ m_Maskable = value;
+ UpdateRendererMaterial();
+ }
}
///
@@ -266,8 +297,6 @@ namespace Coffee.UIExtensions
}
}
- public override Material materialForRendering => null;
-
///
/// Paused.
///
@@ -282,8 +311,8 @@ namespace Coffee.UIExtensions
ResetGroupId();
UpdateTracker();
UIParticleUpdater.Register(this);
- RegisterDirtyMaterialCallback(UpdateRendererMaterial);
+ //
if (0 < particles.Count)
{
RefreshParticles(particles);
@@ -293,7 +322,7 @@ namespace Coffee.UIExtensions
RefreshParticles();
}
- base.OnEnable();
+ UpdateRendererMaterial();
}
///
@@ -304,9 +333,15 @@ namespace Coffee.UIExtensions
UpdateTracker();
UIParticleUpdater.Unregister(this);
_renderers.ForEach(r => r.Reset());
- UnregisterDirtyMaterialCallback(UpdateRendererMaterial);
+ _canvas = null;
+ }
- base.OnDisable();
+ ///
+ /// Called when the state of the parent Canvas is changed.
+ ///
+ protected override void OnCanvasHierarchyChanged()
+ {
+ _canvas = null;
}
///
@@ -316,11 +351,20 @@ namespace Coffee.UIExtensions
{
}
+ ///
+ /// This function is called when a direct or indirect parent of the transform of the GameObject has changed.
+ ///
+ protected override void OnTransformParentChanged()
+ {
+ _canvas = null;
+ }
+
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
UpdateTracker();
+ UpdateRendererMaterial();
}
#endif
@@ -330,6 +374,7 @@ namespace Coffee.UIExtensions
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
+#pragma warning disable CS0612 // Type or member is obsolete
if (m_IgnoreCanvasScaler || m_AutoScaling)
{
m_IgnoreCanvasScaler = false;
@@ -342,6 +387,7 @@ namespace Coffee.UIExtensions
m_AbsoluteMode = false;
m_PositionMode = PositionMode.Absolute;
}
+#pragma warning restore CS0612 // Type or member is obsolete
}
public void Play()
@@ -400,9 +446,10 @@ namespace Coffee.UIExtensions
{
if (!instance) return;
- foreach (Transform child in transform)
+ var childCount = transform.childCount;
+ for (var i = 0; i < childCount; i++)
{
- var go = child.gameObject;
+ var go = transform.GetChild(i).gameObject;
go.SetActive(false);
if (destroyOldParticles)
{
@@ -433,7 +480,14 @@ namespace Coffee.UIExtensions
{
if (!root) return;
root.GetComponentsInChildren(true, particles);
- particles.RemoveAll(x => x.GetComponentInParent(true) != this);
+ for (var i = particles.Count - 1; 0 <= i; i--)
+ {
+ var ps = particles[i];
+ if (!ps || ps.GetComponentInParent(true) != this)
+ {
+ particles.RemoveAt(i);
+ }
+ }
for (var i = 0; i < particles.Count; i++)
{
@@ -448,31 +502,39 @@ namespace Coffee.UIExtensions
RefreshParticles(particles);
}
- public void RefreshParticles(List particles)
+ ///
+ /// Refresh UIParticle using a list of ParticleSystems.
+ ///
+ public void RefreshParticles(List particleSystems)
{
+ // Collect children UIParticleRenderer components.
// #246: Nullptr exceptions when using nested UIParticle components in hierarchy
_renderers.Clear();
- foreach (Transform child in transform)
+ var childCount = transform.childCount;
+ for (var i = 0; i < childCount; i++)
{
- var uiParticleRenderer = child.GetComponent();
-
- if (uiParticleRenderer != null)
+ var child = transform.GetChild(i);
+ if (child.TryGetComponent(out UIParticleRenderer uiParticleRenderer))
{
_renderers.Add(uiParticleRenderer);
}
}
+ // Reset the UIParticleRenderer components.
for (var i = 0; i < _renderers.Count; i++)
{
_renderers[i].Reset(i);
}
+ // Set the ParticleSystem to the UIParticleRenderer. If the trail is enabled, set it additionally.
var j = 0;
- for (var i = 0; i < particles.Count; i++)
+ for (var i = 0; i < particleSystems.Count; i++)
{
- var ps = particles[i];
+ var ps = particleSystems[i];
if (!ps) continue;
GetRenderer(j++).Set(this, ps, false);
+
+ // If the trail is enabled, set it additionally.
if (ps.trails.enabled)
{
GetRenderer(j++).Set(this, ps, true);
@@ -500,11 +562,10 @@ namespace Coffee.UIExtensions
for (var i = 0; i < _renderers.Count; i++)
{
var r = _renderers[i];
- if (!r)
- {
- RefreshParticles(particles);
- break;
- }
+ if (r) continue;
+
+ RefreshParticles(particles);
+ break;
}
var bakeCamera = GetBakeCamera();
@@ -512,6 +573,7 @@ namespace Coffee.UIExtensions
{
var r = _renderers[i];
if (!r) continue;
+
r.UpdateMesh(bakeCamera);
}
}
@@ -523,17 +585,6 @@ namespace Coffee.UIExtensions
: Random.Range(m_GroupId, m_GroupMaxId + 1);
}
- protected override void UpdateMaterial()
- {
- }
-
- ///
- /// Call to update the geometry of the Graphic onto the CanvasRenderer.
- ///
- protected override void UpdateGeometry()
- {
- }
-
private void UpdateRendererMaterial()
{
for (var i = 0; i < _renderers.Count; i++)
@@ -574,11 +625,12 @@ namespace Coffee.UIExtensions
// When render mode is ScreenSpaceOverlay, use ortho-camera.
if (!_orthoCamera)
{
- // Find existing ortho-camera.
- foreach (Transform child in transform)
+ // Find existing orthographic-camera.
+ var childCount = transform.childCount;
+ for (var i = 0; i < childCount; i++)
{
- var cam = child.GetComponent();
- if (cam && cam.name == "[generated] UIParticleOverlayCamera")
+ if (transform.GetChild(i).TryGetComponent(out var cam)
+ && cam.name == "[generated] UIParticleOverlayCamera")
{
_orthoCamera = cam;
break;
@@ -600,8 +652,7 @@ namespace Coffee.UIExtensions
}
//
- var size = ((RectTransform)root.transform).rect.size;
- _orthoCamera.orthographicSize = Mathf.Max(size.x, size.y) * root.scaleFactor;
+ _orthoCamera.orthographicSize = 10;
_orthoCamera.transform.SetPositionAndRotation(new Vector3(0, 0, -1000), Quaternion.identity);
_orthoCamera.orthographic = true;
_orthoCamera.farClipPlane = 2000f;
@@ -612,7 +663,7 @@ namespace Coffee.UIExtensions
private void UpdateTracker()
{
#pragma warning disable CS0618 // Type or member is obsolete
- if (!enabled || !autoScaling || autoScalingMode != AutoScalingMode.Transform)
+ if (!enabled || autoScalingMode != AutoScalingMode.Transform)
#pragma warning restore CS0618 // Type or member is obsolete
{
_tracker.Clear();
diff --git a/Packages/src/Runtime/UIParticleRenderer.cs b/Packages/src/Runtime/UIParticleRenderer.cs
index 727e85e..b3252cf 100644
--- a/Packages/src/Runtime/UIParticleRenderer.cs
+++ b/Packages/src/Runtime/UIParticleRenderer.cs
@@ -33,6 +33,7 @@ namespace Coffee.UIExtensions
private int _index;
private bool _isTrail;
private Bounds _lastBounds;
+ private Material _materialForRendering;
private Material _modifiedMaterial;
private UIParticle _parent;
private ParticleSystem _particleSystem;
@@ -91,6 +92,19 @@ namespace Coffee.UIExtensions
}
}
+ public override Material materialForRendering
+ {
+ get
+ {
+ if (!_materialForRendering)
+ {
+ _materialForRendering = base.materialForRendering;
+ }
+
+ return _materialForRendering;
+ }
+ }
+
public void Reset(int index = -1)
{
if (_renderer)
@@ -110,8 +124,7 @@ namespace Coffee.UIExtensions
if (this && isActiveAndEnabled)
{
material = null;
- workerMesh.Clear();
- canvasRenderer.SetMesh(workerMesh);
+ canvasRenderer.Clear();
_lastBounds = new Bounds();
enabled = false;
}
@@ -202,10 +215,11 @@ namespace Coffee.UIExtensions
);
if (!MaterialRepository.Valid(hash, _modifiedMaterial))
{
- MaterialRepository.Get(hash, ref _modifiedMaterial, () => new Material(modifiedMaterial)
+ MaterialRepository.Get(hash, ref _modifiedMaterial, x => new Material(x.mat)
{
- hideFlags = HideFlags.HideAndDontSave
- });
+ hideFlags = HideFlags.HideAndDontSave,
+ mainTexture = x.texture ? x.texture : x.mat.mainTexture
+ }, (mat: modifiedMaterial, texture));
}
return _modifiedMaterial;
@@ -232,7 +246,7 @@ namespace Coffee.UIExtensions
}
}
- _renderer = ps.GetComponent();
+ ps.TryGetComponent(out _renderer);
_renderer.enabled = false;
//_emitter = emitter;
@@ -424,66 +438,49 @@ namespace Coffee.UIExtensions
Profiler.EndSample();
+ // Update animatable material properties.
+ Profiler.BeginSample("[UIParticleRenderer] Update Animatable Material Properties");
+ UpdateMaterialProperties();
+ Profiler.EndSample();
// Get grouped renderers.
+ Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
s_Renderers.Clear();
if (_parent.useMeshSharing)
{
UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, s_Renderers);
}
- // Set mesh to the CanvasRenderer.
- Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
for (var i = 0; i < s_Renderers.Count; i++)
{
if (s_Renderers[i] == this) continue;
+
s_Renderers[i].canvasRenderer.SetMesh(workerMesh);
s_Renderers[i]._lastBounds = _lastBounds;
+ s_Renderers[i].canvasRenderer.materialCount = 1;
+ s_Renderers[i].canvasRenderer.SetMaterial(materialForRendering, 0);
}
- if (!_parent.canRender)
+ if (_parent.canRender)
+ {
+ canvasRenderer.SetMesh(workerMesh);
+ }
+ else
{
workerMesh.Clear();
}
- canvasRenderer.SetMesh(workerMesh);
- Profiler.EndSample();
-
- // Update animatable material properties.
- Profiler.BeginSample("[UIParticleRenderer] Update Animatable Material Properties");
-
-#if UNITY_EDITOR
- if (_modifiedMaterial != material)
- {
- _renderer.GetSharedMaterials(s_Materials);
- material = s_Materials[_isTrail ? 1 : 0];
- s_Materials.Clear();
- SetMaterialDirty();
- }
-#endif
-
- UpdateMaterialProperties();
- if (_parent.useMeshSharing)
- {
- if (!_currentMaterialForRendering)
- {
- _currentMaterialForRendering = materialForRendering;
- }
-
- for (var i = 0; i < s_Renderers.Count; i++)
- {
- if (s_Renderers[i] == this) continue;
-
- s_Renderers[i].canvasRenderer.materialCount = 1;
- s_Renderers[i].canvasRenderer.SetMaterial(_currentMaterialForRendering, 0);
- }
- }
-
Profiler.EndSample();
s_Renderers.Clear();
}
+ public override void SetMaterialDirty()
+ {
+ _materialForRendering = null;
+ base.SetMaterialDirty();
+ }
+
///
/// Call to update the geometry of the Graphic onto the CanvasRenderer.
///
@@ -687,12 +684,12 @@ namespace Coffee.UIExtensions
if (s_Mpb.isEmpty) return;
// #41: Copy the value from MaterialPropertyBlock to CanvasRenderer
- if (!_modifiedMaterial) return;
+ if (!materialForRendering) return;
for (var i = 0; i < _parent.m_AnimatableProperties.Length; i++)
{
var ap = _parent.m_AnimatableProperties[i];
- ap.UpdateMaterialProperties(_modifiedMaterial, s_Mpb);
+ ap.UpdateMaterialProperties(materialForRendering, s_Mpb);
}
s_Mpb.Clear();