You've already forked ParticleEffectForUGUI
mirror of
https://github.com/mob-sakai/ParticleEffectForUGUI.git
synced 2026-05-15 04:30:09 +00:00
3.0.0-preview.19
# [3.0.0-preview.19](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v3.0.0-preview.18...v3.0.0-preview.19) (2020-08-28) ### Bug Fixes * baking camera settings for camera space ([436c5e4](436c5e47f7)) * fix local simulation ([7add9de](7add9defb7)) ### Features * add menu to create UIParticle ([2fa1843](2fa18431f0)) * add play/pause/stop api ([f09a386](f09a386bc5)) * support for changing rendering orders ([745d4a5](745d4a5988)) * Support for child ParticleSystem rendering ([4ee90be](4ee90be17c)) * UIParticle for trail is no longer needed ([466e43c](466e43cf93)) ### BREAKING CHANGES * The child UIParticle is no longer needed.
This commit is contained in:
@@ -1,76 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
|
||||
[assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")]
|
||||
|
||||
namespace Coffee.UIExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Render maskable and sortable particle effect ,without Camera, RenderTexture or Canvas.
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[RequireComponent(typeof(CanvasRenderer))]
|
||||
public class UIParticle : MaskableGraphic
|
||||
{
|
||||
//################################
|
||||
// Serialize Members.
|
||||
//################################
|
||||
[Tooltip("The ParticleSystem rendered by CanvasRenderer")] [SerializeField]
|
||||
ParticleSystem m_ParticleSystem;
|
||||
|
||||
[Tooltip("The UIParticle to render trail effect")] [SerializeField]
|
||||
internal UIParticle m_TrailParticle;
|
||||
|
||||
[HideInInspector] [SerializeField] bool m_IsTrail = false;
|
||||
|
||||
[Tooltip("Ignore canvas scaler")] [SerializeField]
|
||||
bool m_IgnoreCanvasScaler = false;
|
||||
|
||||
[Tooltip("Ignore parent scale")] [SerializeField]
|
||||
bool m_IgnoreParent = false;
|
||||
[Tooltip("Ignore canvas scaler")] [SerializeField] [FormerlySerializedAs("m_IgnoreParent")]
|
||||
bool m_IgnoreCanvasScaler = true;
|
||||
|
||||
[Tooltip("Particle effect scale")] [SerializeField]
|
||||
float m_Scale = 0;
|
||||
float m_Scale = 100;
|
||||
|
||||
[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("Particle effect scale")] [SerializeField]
|
||||
internal Vector3 m_Scale3D = Vector3.one;
|
||||
[Tooltip("Particles")] [SerializeField]
|
||||
private List<ParticleSystem> m_Particles = new List<ParticleSystem>();
|
||||
|
||||
private readonly Material[] _maskMaterials = new Material[2];
|
||||
private DrivenRectTransformTracker _tracker;
|
||||
private Mesh _bakedMesh;
|
||||
private ParticleSystemRenderer _renderer;
|
||||
private int _cachedSharedMaterialId;
|
||||
private int _cachedTrailMaterialId;
|
||||
private bool _cachedSpritesModeAndHasTrail;
|
||||
private readonly List<Material> _modifiedMaterials = new List<Material>();
|
||||
private uint _activeMeshIndices;
|
||||
private Vector3 _cachedPosition;
|
||||
private static readonly List<Material> s_TempMaterials = new List<Material>(2);
|
||||
|
||||
|
||||
//################################
|
||||
// Public/Protected Members.
|
||||
//################################
|
||||
/// <summary>
|
||||
/// Should this graphic be considered a target for raycasting?
|
||||
/// </summary>
|
||||
public override bool raycastTarget
|
||||
{
|
||||
get { return false; }
|
||||
set { base.raycastTarget = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cached ParticleSystem.
|
||||
/// </summary>
|
||||
public ParticleSystem cachedParticleSystem
|
||||
{
|
||||
get { return m_ParticleSystem ? m_ParticleSystem : (m_ParticleSystem = GetComponent<ParticleSystem>()); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cached ParticleSystem.
|
||||
/// </summary>
|
||||
internal ParticleSystemRenderer cachedRenderer
|
||||
{
|
||||
get { return _renderer; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public bool ignoreCanvasScaler
|
||||
@@ -84,116 +59,143 @@ namespace Coffee.UIExtensions
|
||||
/// </summary>
|
||||
public float scale
|
||||
{
|
||||
get { return m_Scale3D.x; }
|
||||
set { m_Scale3D.Set(value, value, value); }
|
||||
get { return m_Scale; }
|
||||
set { m_Scale = Mathf.Max(0.001f, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Particle effect scale.
|
||||
/// </summary>
|
||||
public Vector3 scale3D
|
||||
{
|
||||
get { return m_Scale3D; }
|
||||
set { m_Scale3D = value; }
|
||||
}
|
||||
|
||||
internal bool isTrailParticle
|
||||
{
|
||||
get { return m_IsTrail; }
|
||||
}
|
||||
|
||||
internal bool isSpritesMode
|
||||
{
|
||||
get { return textureSheetAnimationModule.enabled && textureSheetAnimationModule.mode == ParticleSystemAnimationMode.Sprites; }
|
||||
}
|
||||
|
||||
private bool isSpritesModeAndHasTrail
|
||||
{
|
||||
get { return isSpritesMode && trailModule.enabled; }
|
||||
}
|
||||
|
||||
private ParticleSystem.TextureSheetAnimationModule textureSheetAnimationModule
|
||||
{
|
||||
get { return cachedParticleSystem.textureSheetAnimation; }
|
||||
}
|
||||
|
||||
internal ParticleSystem.TrailModule trailModule
|
||||
{
|
||||
get { return cachedParticleSystem.trails; }
|
||||
}
|
||||
|
||||
internal ParticleSystem.MainModule mainModule
|
||||
{
|
||||
get { return cachedParticleSystem.main; }
|
||||
}
|
||||
|
||||
public bool isValid
|
||||
{
|
||||
get { return m_ParticleSystem && _renderer && canvas; }
|
||||
}
|
||||
|
||||
public Mesh bakedMesh
|
||||
internal Mesh bakedMesh
|
||||
{
|
||||
get { return _bakedMesh; }
|
||||
}
|
||||
|
||||
protected override void UpdateMaterial()
|
||||
public List<ParticleSystem> particles
|
||||
{
|
||||
if (!_renderer) return;
|
||||
get { return m_Particles; }
|
||||
}
|
||||
|
||||
if (!isSpritesMode) // Non sprite mode: canvas renderer has main and trail materials.
|
||||
public IEnumerable<Material> materials
|
||||
{
|
||||
get { return _modifiedMaterials; }
|
||||
}
|
||||
|
||||
internal uint activeMeshIndices
|
||||
{
|
||||
get { return _activeMeshIndices; }
|
||||
set
|
||||
{
|
||||
canvasRenderer.materialCount = trailModule.enabled ? 2 : 1;
|
||||
canvasRenderer.SetMaterial(GetModifiedMaterial(_renderer.sharedMaterial, 0), 0);
|
||||
if (trailModule.enabled)
|
||||
canvasRenderer.SetMaterial(GetModifiedMaterial(_renderer.trailMaterial, 1), 1);
|
||||
}
|
||||
else if (isTrailParticle) // Sprite mode (Trail): canvas renderer has trail material.
|
||||
{
|
||||
canvasRenderer.materialCount = 1;
|
||||
canvasRenderer.SetMaterial(GetModifiedMaterial(_renderer.trailMaterial, 0), 0);
|
||||
}
|
||||
else // Sprite mode (Main): canvas renderer has main material.
|
||||
{
|
||||
canvasRenderer.materialCount = 1;
|
||||
canvasRenderer.SetMaterial(GetModifiedMaterial(_renderer.sharedMaterial, 0), 0);
|
||||
if (_activeMeshIndices == value) return;
|
||||
_activeMeshIndices = value;
|
||||
UpdateMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
private Material GetModifiedMaterial(Material baseMaterial, int index)
|
||||
internal Vector3 cachedPosition
|
||||
{
|
||||
if (!baseMaterial || 1 < index || !isActiveAndEnabled) return null;
|
||||
get { return _cachedPosition; }
|
||||
set { _cachedPosition = value; }
|
||||
}
|
||||
|
||||
var hasAnimatableProperties = 0 < m_AnimatableProperties.Length && index == 0;
|
||||
if (hasAnimatableProperties || isTrailParticle)
|
||||
baseMaterial = new Material(baseMaterial);
|
||||
public void Play()
|
||||
{
|
||||
particles.Exec(p => p.Play());
|
||||
}
|
||||
|
||||
var baseMat = baseMaterial;
|
||||
if (m_ShouldRecalculateStencil)
|
||||
public void Pause()
|
||||
{
|
||||
particles.Exec(p => p.Pause());
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
particles.Exec(p => p.Stop());
|
||||
}
|
||||
|
||||
public void RefreshParticles()
|
||||
{
|
||||
GetComponentsInChildren(particles);
|
||||
|
||||
particles.Exec(p => p.GetComponent<ParticleSystemRenderer>().enabled = !enabled);
|
||||
particles.SortForRendering(transform);
|
||||
|
||||
SetMaterialDirty();
|
||||
}
|
||||
|
||||
protected override void UpdateMaterial()
|
||||
{
|
||||
// Clear modified materials.
|
||||
for (var i = 0; i < _modifiedMaterials.Count; i++)
|
||||
{
|
||||
m_ShouldRecalculateStencil = false;
|
||||
|
||||
if (maskable)
|
||||
{
|
||||
var sortOverrideCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
|
||||
m_StencilValue = MaskUtilities.GetStencilDepth(transform, sortOverrideCanvas) + index;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_StencilValue = 0;
|
||||
}
|
||||
StencilMaterial.Remove(_modifiedMaterials[i]);
|
||||
DestroyImmediate(_modifiedMaterials[i]);
|
||||
_modifiedMaterials[i] = null;
|
||||
}
|
||||
|
||||
var component = GetComponent<Mask>();
|
||||
if (m_StencilValue <= 0 || (component != null && component.IsActive())) return baseMat;
|
||||
_modifiedMaterials.Clear();
|
||||
|
||||
var stencilId = (1 << m_StencilValue) - 1;
|
||||
var maskMaterial = StencilMaterial.Add(baseMat, stencilId, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, stencilId, 0);
|
||||
StencilMaterial.Remove(_maskMaterials[index]);
|
||||
_maskMaterials[index] = maskMaterial;
|
||||
baseMat = _maskMaterials[index];
|
||||
// Recalculate stencil value.
|
||||
if (m_ShouldRecalculateStencil)
|
||||
{
|
||||
var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
|
||||
m_StencilValue = maskable ? MaskUtilities.GetStencilDepth(transform, rootCanvas) : 0;
|
||||
m_ShouldRecalculateStencil = false;
|
||||
}
|
||||
|
||||
return baseMat;
|
||||
// No mesh to render.
|
||||
if (activeMeshIndices == 0 || !isActiveAndEnabled || particles.Count == 0)
|
||||
{
|
||||
_activeMeshIndices = 0;
|
||||
canvasRenderer.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
var materialCount = Mathf.Max(8, activeMeshIndices.BitCount());
|
||||
canvasRenderer.materialCount = materialCount;
|
||||
var j = 0;
|
||||
for (var i = 0; i < particles.Count; i++)
|
||||
{
|
||||
if (materialCount <= j) break;
|
||||
var ps = particles[i];
|
||||
if (!ps) continue;
|
||||
|
||||
var r = ps.GetComponent<ParticleSystemRenderer>();
|
||||
r.GetSharedMaterials(s_TempMaterials);
|
||||
|
||||
// Main
|
||||
var bit = 1 << (i * 2);
|
||||
if (0 < (activeMeshIndices & bit) && 0 < s_TempMaterials.Count)
|
||||
{
|
||||
var mat = GetModifiedMaterial(s_TempMaterials[0], ps.GetTextureForSprite());
|
||||
canvasRenderer.SetMaterial(mat, j++);
|
||||
}
|
||||
|
||||
// Trails
|
||||
if (materialCount <= j) break;
|
||||
bit <<= 1;
|
||||
if (0 < (activeMeshIndices & bit) && 1 < s_TempMaterials.Count)
|
||||
{
|
||||
var mat = GetModifiedMaterial(s_TempMaterials[1], null);
|
||||
canvasRenderer.SetMaterial(mat, j++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Material GetModifiedMaterial(Material baseMaterial, Texture2D texture)
|
||||
{
|
||||
if (0 < m_StencilValue)
|
||||
{
|
||||
baseMaterial = StencilMaterial.Add(baseMaterial, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0);
|
||||
_modifiedMaterials.Add(baseMaterial);
|
||||
}
|
||||
|
||||
if (texture == null && m_AnimatableProperties.Length == 0) return baseMaterial;
|
||||
|
||||
baseMaterial = new Material(baseMaterial);
|
||||
_modifiedMaterials.Add(baseMaterial);
|
||||
if (texture)
|
||||
baseMaterial.mainTexture = texture;
|
||||
|
||||
return baseMaterial;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -201,25 +203,19 @@ namespace Coffee.UIExtensions
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
UpdateVersionIfNeeded();
|
||||
InitializeIfNeeded();
|
||||
|
||||
_cachedPosition = transform.localPosition;
|
||||
_activeMeshIndices = 0;
|
||||
|
||||
UIParticleUpdater.Register(this);
|
||||
particles.Exec(p => p.GetComponent<ParticleSystemRenderer>().enabled = false);
|
||||
_tracker.Add(this, rectTransform, DrivenTransformProperties.Scale);
|
||||
|
||||
// Initialize.
|
||||
_renderer = cachedParticleSystem ? cachedParticleSystem.GetComponent<ParticleSystemRenderer>() : null;
|
||||
if (_renderer != null)
|
||||
_renderer.enabled = false;
|
||||
|
||||
CheckMaterials();
|
||||
|
||||
// Create objects.
|
||||
_bakedMesh = new Mesh();
|
||||
_bakedMesh.MarkDynamic();
|
||||
|
||||
MeshHelper.Register();
|
||||
BakingCamera.Register();
|
||||
UIParticleUpdater.Register(this);
|
||||
|
||||
base.OnEnable();
|
||||
}
|
||||
|
||||
@@ -228,25 +224,14 @@ namespace Coffee.UIExtensions
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
UIParticleUpdater.Unregister(this);
|
||||
particles.Exec(p => p.GetComponent<ParticleSystemRenderer>().enabled = true);
|
||||
_tracker.Clear();
|
||||
|
||||
// Destroy object.
|
||||
DestroyImmediate(_bakedMesh);
|
||||
_bakedMesh = null;
|
||||
|
||||
MeshHelper.Unregister();
|
||||
BakingCamera.Unregister();
|
||||
UIParticleUpdater.Unregister(this);
|
||||
|
||||
CheckMaterials();
|
||||
|
||||
// Remove mask materials.
|
||||
for (var i = 0; i < _maskMaterials.Length; i++)
|
||||
{
|
||||
StencilMaterial.Remove(_maskMaterials[i]);
|
||||
_maskMaterials[i] = null;
|
||||
}
|
||||
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
@@ -271,75 +256,28 @@ namespace Coffee.UIExtensions
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//################################
|
||||
// Private Members.
|
||||
//################################
|
||||
private static bool HasMaterialChanged(Material material, ref int current)
|
||||
private void InitializeIfNeeded()
|
||||
{
|
||||
var old = current;
|
||||
current = material ? material.GetInstanceID() : 0;
|
||||
return current != old;
|
||||
}
|
||||
if (0 < particles.Count) return;
|
||||
|
||||
internal void UpdateTrailParticle()
|
||||
{
|
||||
// Should have a UIParticle for trail particle?
|
||||
if (isActiveAndEnabled && isValid && !isTrailParticle && isSpritesModeAndHasTrail)
|
||||
if (m_IsTrail
|
||||
|| transform.parent && transform.parent.GetComponentInParent<UIParticle>())
|
||||
{
|
||||
if (!m_TrailParticle)
|
||||
{
|
||||
// Create new UIParticle for trail particle
|
||||
m_TrailParticle = new GameObject("[UIParticle] Trail").AddComponent<UIParticle>();
|
||||
var trans = m_TrailParticle.transform;
|
||||
trans.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
trans.localScale = Vector3.one;
|
||||
trans.SetParent(transform, false);
|
||||
|
||||
m_TrailParticle._renderer = _renderer;
|
||||
m_TrailParticle.m_ParticleSystem = m_ParticleSystem;
|
||||
m_TrailParticle.m_IsTrail = true;
|
||||
}
|
||||
|
||||
m_TrailParticle.gameObject.hideFlags = HideFlags.DontSave;
|
||||
}
|
||||
else if (m_TrailParticle)
|
||||
{
|
||||
// Destroy a UIParticle for trail particle.
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
DestroyImmediate(m_TrailParticle.gameObject);
|
||||
gameObject.SetActive(false);
|
||||
if (Application.isPlaying)
|
||||
Destroy(gameObject);
|
||||
else
|
||||
#endif
|
||||
Destroy(m_TrailParticle.gameObject);
|
||||
|
||||
m_TrailParticle = null;
|
||||
DestroyImmediate(gameObject);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
internal void CheckMaterials()
|
||||
{
|
||||
if (!_renderer) return;
|
||||
|
||||
var matChanged = HasMaterialChanged(_renderer.sharedMaterial, ref _cachedSharedMaterialId);
|
||||
var matChanged2 = HasMaterialChanged(_renderer.trailMaterial, ref _cachedTrailMaterialId);
|
||||
var modeChanged = _cachedSpritesModeAndHasTrail != isSpritesModeAndHasTrail;
|
||||
_cachedSpritesModeAndHasTrail = isSpritesModeAndHasTrail;
|
||||
|
||||
if (matChanged || matChanged2 || modeChanged)
|
||||
SetMaterialDirty();
|
||||
}
|
||||
|
||||
private void UpdateVersionIfNeeded()
|
||||
{
|
||||
if (Mathf.Approximately(m_Scale, 0)) return;
|
||||
|
||||
var parent = GetComponentInParent<UIParticle>();
|
||||
if (m_IgnoreParent || !parent)
|
||||
scale3D = m_Scale * transform.localScale;
|
||||
// refresh.
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
UnityEditor.EditorApplication.delayCall += RefreshParticles;
|
||||
else
|
||||
scale3D = transform.localScale;
|
||||
m_Scale = 0;
|
||||
#endif
|
||||
RefreshParticles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user