ParticleEffectForUGUI/Scripts/UIParticle.cs

546 lines
14 KiB
C#
Raw Normal View History

2018-06-22 18:48:14 +08:00
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.UI;
2019-02-23 22:20:30 +08:00
using ShaderPropertyType = Coffee.UIExtensions.UIParticle.AnimatableProperty.ShaderPropertyType;
2018-06-22 18:48:14 +08:00
namespace Coffee.UIExtensions
{
/// <summary>
/// Render maskable and sortable particle effect ,without Camera, RenderTexture or Canvas.
/// </summary>
[ExecuteInEditMode]
public class UIParticle : MaskableGraphic
{
//################################
// Constant or Readonly Static Members.
//################################
2018-11-28 15:36:16 +08:00
static readonly int s_IdMainTex = Shader.PropertyToID ("_MainTex");
static readonly List<Vector3> s_Vertices = new List<Vector3> ();
static readonly List<UIParticle> s_TempRelatables = new List<UIParticle> ();
static readonly List<UIParticle> s_ActiveParticles = new List<UIParticle> ();
2018-06-22 18:48:14 +08:00
//################################
// Serialize Members.
//################################
2018-11-28 15:36:16 +08:00
[Tooltip ("The ParticleSystem rendered by CanvasRenderer")]
2018-06-22 18:48:14 +08:00
[SerializeField] ParticleSystem m_ParticleSystem;
2018-11-28 15:36:16 +08:00
[Tooltip ("The UIParticle to render trail effect")]
2018-06-22 18:48:14 +08:00
[SerializeField] UIParticle m_TrailParticle;
[HideInInspector] [SerializeField] bool m_IsTrail = false;
2018-11-28 15:36:16 +08:00
[Tooltip ("Particle effect scale")]
[SerializeField] float m_Scale = 1;
2018-11-28 15:36:16 +08:00
[Tooltip ("Ignore parent scale")]
2018-11-28 13:19:33 +08:00
[SerializeField] bool m_IgnoreParent = false;
2018-06-22 18:48:14 +08:00
[Tooltip ("Animatable material properties. AnimationでParticleSystemのマテリアルプロパティを変更する場合、有効にしてください。")]
2019-02-26 10:11:48 +08:00
[SerializeField] AnimatableProperty [] m_AnimatableProperties = new AnimatableProperty [0];
static MaterialPropertyBlock s_Mpb;
[System.Serializable]
2019-02-23 22:20:30 +08:00
public class AnimatableProperty : ISerializationCallbackReceiver
{
public enum ShaderPropertyType
{
Color,
Vector,
Float,
Range,
2019-02-23 22:20:30 +08:00
Texture,
};
2019-02-23 22:20:30 +08:00
[SerializeField]
2019-02-26 10:10:47 +08:00
string m_Name = "";
2019-02-23 22:20:30 +08:00
[SerializeField]
2019-02-26 10:10:47 +08:00
ShaderPropertyType m_Type = ShaderPropertyType.Vector;
2019-02-23 22:20:30 +08:00
public int id { get; private set; }
public ShaderPropertyType type { get { return m_Type; } }
public void OnBeforeSerialize ()
{
}
public void OnAfterDeserialize ()
{
id = Shader.PropertyToID (m_Name);
}
}
2018-06-22 18:48:14 +08:00
//################################
// Public/Protected Members.
//################################
public override Texture mainTexture
{
get
{
2018-07-13 11:56:50 +08:00
Texture tex = null;
if (!m_IsTrail && cachedParticleSystem)
2018-07-13 11:56:50 +08:00
{
2018-11-28 15:36:16 +08:00
Profiler.BeginSample ("Check TextureSheetAnimation module");
var textureSheet = cachedParticleSystem.textureSheetAnimation;
2018-07-13 11:56:50 +08:00
if (textureSheet.enabled && textureSheet.mode == ParticleSystemAnimationMode.Sprites && 0 < textureSheet.spriteCount)
{
2018-11-28 15:36:16 +08:00
tex = textureSheet.GetSprite (0).texture;
2018-07-13 11:56:50 +08:00
}
2018-11-28 15:36:16 +08:00
Profiler.EndSample ();
2018-07-13 11:56:50 +08:00
}
if (!tex && _renderer)
{
2018-11-28 15:36:16 +08:00
Profiler.BeginSample ("Check material");
var mat = material;
2018-11-28 15:36:16 +08:00
if (mat && mat.HasProperty (s_IdMainTex))
2018-07-13 11:56:50 +08:00
{
tex = mat.mainTexture;
}
2018-11-28 15:36:16 +08:00
Profiler.EndSample ();
2018-07-13 11:56:50 +08:00
}
return tex ?? s_WhiteTexture;
2018-06-22 18:48:14 +08:00
}
}
public override Material material
{
get
{
return _renderer
? m_IsTrail
? _renderer.trailMaterial
: _renderer.sharedMaterial
: null;
}
set
{
if (!_renderer)
{
}
else if (m_IsTrail && _renderer.trailMaterial != value)
{
_renderer.trailMaterial = value;
SetMaterialDirty ();
}
else if (!m_IsTrail && _renderer.sharedMaterial != value)
{
_renderer.sharedMaterial = value;
SetMaterialDirty ();
}
}
}
/// <summary>
/// Particle effect scale.
/// </summary>
public float scale { get { return _parent ? _parent.scale : m_Scale; } set { m_Scale = value; } }
2018-11-28 13:19:33 +08:00
/// <summary>
/// Should the soft mask ignore parent soft masks?
/// </summary>
/// <value>If set to true the soft mask will ignore any parent soft mask settings.</value>
public bool ignoreParent
{
get { return m_IgnoreParent; }
set
{
2018-11-28 15:36:16 +08:00
if (m_IgnoreParent != value)
2018-11-28 13:19:33 +08:00
{
m_IgnoreParent = value;
2018-11-28 15:36:16 +08:00
OnTransformParentChanged ();
2018-11-28 13:19:33 +08:00
}
}
}
2018-12-21 19:50:08 +08:00
/// <summary>
/// Is this the root UIParticle?
/// </summary>
2018-11-28 18:55:56 +08:00
public bool isRoot
{
get { return !_parent; }
}
2018-12-21 19:50:08 +08:00
/// <summary>
/// Should this graphic be considered a target for raycasting?
/// </summary>
public override bool raycastTarget { get { return false; } set { base.raycastTarget = value; } }
/// <summary>
/// ParticleSystem.
/// </summary>
public ParticleSystem cachedParticleSystem { get { return m_ParticleSystem ? m_ParticleSystem : (m_ParticleSystem = GetComponent<ParticleSystem> ()); } }
2018-12-21 19:50:08 +08:00
/// <summary>
/// Perform material modification in this function.
/// </summary>
/// <returns>Modified material.</returns>
/// <param name="baseMaterial">Configured Material.</param>
2018-11-28 15:36:16 +08:00
public override Material GetModifiedMaterial (Material baseMaterial)
2018-06-22 18:48:14 +08:00
{
Material mat = null;
if (!_renderer)
mat = baseMaterial;
else if (m_AnimatableProperties.Length == 0)
mat = _renderer.sharedMaterial;
else
mat = new Material (material);
return base.GetModifiedMaterial (mat);
2018-06-22 18:48:14 +08:00
}
2018-12-21 19:50:08 +08:00
/// <summary>
/// This function is called when the object becomes enabled and active.
/// </summary>
2018-11-28 15:36:16 +08:00
protected override void OnEnable ()
2018-06-22 18:48:14 +08:00
{
2018-11-28 13:19:33 +08:00
// Register.
if (s_ActiveParticles.Count == 0)
2018-11-28 13:19:33 +08:00
{
Canvas.willRenderCanvases += UpdateMeshes;
s_Mpb = new MaterialPropertyBlock ();
2018-11-28 13:19:33 +08:00
}
s_ActiveParticles.Add (this);
2018-11-28 13:19:33 +08:00
// Reset the parent-child relation.
2018-11-28 15:36:16 +08:00
GetComponentsInChildren<UIParticle> (false, s_TempRelatables);
for (int i = s_TempRelatables.Count - 1; 0 <= i; i--)
2018-11-28 13:19:33 +08:00
{
2018-11-28 15:36:16 +08:00
s_TempRelatables [i].OnTransformParentChanged ();
2018-11-28 13:19:33 +08:00
}
2018-11-28 15:36:16 +08:00
s_TempRelatables.Clear ();
2018-11-28 13:19:33 +08:00
_renderer = cachedParticleSystem ? cachedParticleSystem.GetComponent<ParticleSystemRenderer> () : null;
if (_renderer && Application.isPlaying)
{
_renderer.enabled = false;
}
2018-06-22 18:48:14 +08:00
2018-11-28 13:19:33 +08:00
// Create objects.
2018-11-28 15:36:16 +08:00
_mesh = new Mesh ();
_mesh.MarkDynamic ();
CheckTrail ();
2018-11-28 13:19:33 +08:00
2018-11-28 15:36:16 +08:00
base.OnEnable ();
2018-11-28 13:19:33 +08:00
}
2018-12-21 19:50:08 +08:00
/// <summary>
/// This function is called when the behaviour becomes disabled.
/// </summary>
2018-11-28 15:36:16 +08:00
protected override void OnDisable ()
2018-11-28 13:19:33 +08:00
{
// Unregister.
s_ActiveParticles.Remove (this);
if (s_ActiveParticles.Count == 0)
2018-11-28 13:19:33 +08:00
{
Canvas.willRenderCanvases -= UpdateMeshes;
}
2018-11-28 13:19:33 +08:00
// Reset the parent-child relation.
2018-11-28 15:36:16 +08:00
for (int i = _children.Count - 1; 0 <= i; i--)
{
2018-11-28 15:36:16 +08:00
_children [i].SetParent (_parent);
}
2018-11-28 15:36:16 +08:00
_children.Clear ();
SetParent (null);
2018-06-22 18:48:14 +08:00
2018-11-28 13:19:33 +08:00
// Destroy objects.
2018-11-28 15:36:16 +08:00
DestroyImmediate (_mesh);
2018-06-22 18:48:14 +08:00
_mesh = null;
2018-11-28 15:36:16 +08:00
CheckTrail ();
2018-11-28 13:19:33 +08:00
2018-11-28 15:36:16 +08:00
base.OnDisable ();
2018-06-22 18:48:14 +08:00
}
2018-12-21 19:50:08 +08:00
/// <summary>
/// Call to update the geometry of the Graphic onto the CanvasRenderer.
/// </summary>
2018-11-28 15:36:16 +08:00
protected override void UpdateGeometry ()
2018-06-22 18:48:14 +08:00
{
}
2018-11-28 13:19:33 +08:00
/// <summary>
/// This function is called when the parent property of the transform of the GameObject has changed.
/// </summary>
2018-11-28 15:36:16 +08:00
protected override void OnTransformParentChanged ()
2018-11-28 13:19:33 +08:00
{
UIParticle newParent = null;
2018-11-28 15:36:16 +08:00
if (isActiveAndEnabled && !m_IgnoreParent)
2018-11-28 13:19:33 +08:00
{
var parentTransform = transform.parent;
2018-11-28 15:36:16 +08:00
while (parentTransform && (!newParent || !newParent.enabled))
2018-11-28 13:19:33 +08:00
{
2018-11-28 15:36:16 +08:00
newParent = parentTransform.GetComponent<UIParticle> ();
2018-11-28 13:19:33 +08:00
parentTransform = parentTransform.parent;
}
}
2018-11-28 15:36:16 +08:00
SetParent (newParent);
2018-12-12 20:41:11 +08:00
base.OnTransformParentChanged ();
2018-11-28 13:19:33 +08:00
}
2018-12-21 19:50:08 +08:00
/// <summary>
/// Callback for when properties have been changed by animation.
/// </summary>
2018-11-28 15:36:16 +08:00
protected override void OnDidApplyAnimationProperties ()
2018-11-28 13:19:33 +08:00
{
}
#if UNITY_EDITOR
/// <summary>
/// This function is called when the script is loaded or a value is changed in the inspector(Called in the editor only).
/// </summary>
2018-11-28 15:36:16 +08:00
protected override void OnValidate ()
2018-11-28 13:19:33 +08:00
{
2018-11-28 15:36:16 +08:00
OnTransformParentChanged ();
base.OnValidate ();
2018-11-28 13:19:33 +08:00
}
#endif
2018-06-22 18:48:14 +08:00
//################################
// Private Members.
//################################
Mesh _mesh;
ParticleSystemRenderer _renderer;
2018-11-28 13:19:33 +08:00
UIParticle _parent;
2018-11-28 15:36:16 +08:00
List<UIParticle> _children = new List<UIParticle> ();
Matrix4x4 scaleaMatrix = default (Matrix4x4);
Vector3 _worldPos;
static ParticleSystem.Particle [] s_Particles = new ParticleSystem.Particle [4096];
2018-06-22 18:48:14 +08:00
2018-12-21 19:50:08 +08:00
/// <summary>
/// Update meshes.
/// </summary>
2018-11-28 15:36:16 +08:00
static void UpdateMeshes ()
2018-11-28 13:19:33 +08:00
{
2019-01-15 20:59:27 +08:00
for (int i = 0; i < s_ActiveParticles.Count; i++)
2018-11-28 13:19:33 +08:00
{
2019-01-15 20:59:27 +08:00
if (s_ActiveParticles [i])
{
2019-01-15 20:59:27 +08:00
s_ActiveParticles [i].UpdateMesh ();
}
2018-11-28 13:19:33 +08:00
}
}
2018-12-21 19:50:08 +08:00
/// <summary>
/// Update meshe.
/// </summary>
2018-11-28 15:36:16 +08:00
void UpdateMesh ()
2018-06-22 18:48:14 +08:00
{
2018-07-13 13:47:30 +08:00
try
2018-06-22 18:48:14 +08:00
{
2018-11-28 15:36:16 +08:00
Profiler.BeginSample ("CheckTrail");
CheckTrail ();
Profiler.EndSample ();
2018-06-22 18:48:14 +08:00
if (m_ParticleSystem && canvas)
2018-06-22 18:48:14 +08:00
{
// I do not know why, but it worked fine when setting `transform.localPosition.z` to `0.01`. (#34, #39)
{
Vector3 pos = rectTransform.localPosition;
if (Mathf.Abs (pos.z) < 0.01f)
{
pos.z = 0.01f;
rectTransform.localPosition = pos;
}
}
var rootCanvas = canvas.rootCanvas;
2018-11-28 15:36:16 +08:00
Profiler.BeginSample ("Disable ParticleSystemRenderer");
2018-07-13 13:47:30 +08:00
if (Application.isPlaying)
2018-06-22 18:48:14 +08:00
{
2018-07-13 13:47:30 +08:00
_renderer.enabled = false;
2018-06-22 18:48:14 +08:00
}
2018-11-28 15:36:16 +08:00
Profiler.EndSample ();
2018-07-13 13:47:30 +08:00
2018-11-28 15:36:16 +08:00
Profiler.BeginSample ("Make Matrix");
2018-12-12 20:56:38 +08:00
scaleaMatrix = m_ParticleSystem.main.scalingMode == ParticleSystemScalingMode.Hierarchy
? Matrix4x4.Scale (scale * Vector3.one)
: Matrix4x4.Scale (scale * rootCanvas.transform.localScale);
2018-11-28 15:36:16 +08:00
Matrix4x4 matrix = default (Matrix4x4);
2018-07-13 13:47:30 +08:00
switch (m_ParticleSystem.main.simulationSpace)
2018-06-22 18:48:14 +08:00
{
2018-07-13 13:47:30 +08:00
case ParticleSystemSimulationSpace.Local:
matrix =
scaleaMatrix
* Matrix4x4.Rotate (rectTransform.rotation).inverse
* Matrix4x4.Scale (rectTransform.lossyScale).inverse;
2018-07-13 13:47:30 +08:00
break;
case ParticleSystemSimulationSpace.World:
matrix =
scaleaMatrix
* rectTransform.worldToLocalMatrix;
Vector3 newPos = rectTransform.position;
Vector3 delta = (newPos - _worldPos);
_worldPos = newPos;
if (canvas.renderMode != RenderMode.WorldSpace && !Mathf.Approximately (scale, 0) && 0 < delta.sqrMagnitude)
{
delta *= (1 - 1 / scale);
int count = m_ParticleSystem.particleCount;
if (s_Particles.Length < count)
{
s_Particles = new ParticleSystem.Particle [s_Particles.Length * 2];
}
m_ParticleSystem.GetParticles (s_Particles);
for (int i = 0; i < count; i++)
{
var p = s_Particles [i];
p.position = p.position + delta;
s_Particles [i] = p;
}
m_ParticleSystem.SetParticles (s_Particles, count);
}
2018-07-13 13:47:30 +08:00
break;
case ParticleSystemSimulationSpace.Custom:
break;
2018-06-22 18:48:14 +08:00
}
2018-11-28 15:36:16 +08:00
Profiler.EndSample ();
2018-06-22 18:48:14 +08:00
2018-11-28 15:36:16 +08:00
_mesh.Clear ();
2018-07-13 13:47:30 +08:00
if (0 < m_ParticleSystem.particleCount)
2018-06-22 18:48:14 +08:00
{
2018-11-28 15:36:16 +08:00
Profiler.BeginSample ("Bake Mesh");
var cam = rootCanvas.renderMode == RenderMode.ScreenSpaceOverlay
? UIParticleOverlayCamera.GetCameraForOvrelay (rootCanvas)
: canvas.worldCamera ?? Camera.main;
if (!cam)
{
return;
}
2018-07-13 13:47:30 +08:00
if (m_IsTrail)
{
_renderer.BakeTrailsMesh (_mesh, cam, true);
2018-07-13 13:47:30 +08:00
}
else
{
_renderer.BakeMesh (_mesh, cam, true);
2018-07-13 13:47:30 +08:00
}
2018-11-28 15:36:16 +08:00
Profiler.EndSample ();
2018-07-13 13:47:30 +08:00
// Apply matrix.
2018-11-28 15:36:16 +08:00
Profiler.BeginSample ("Apply matrix to position");
_mesh.GetVertices (s_Vertices);
2018-07-13 13:47:30 +08:00
var count = s_Vertices.Count;
for (int i = 0; i < count; i++)
{
2018-11-28 15:36:16 +08:00
s_Vertices [i] = matrix.MultiplyPoint3x4 (s_Vertices [i]);
2018-07-13 13:47:30 +08:00
}
2018-11-28 15:36:16 +08:00
_mesh.SetVertices (s_Vertices);
s_Vertices.Clear ();
Profiler.EndSample ();
2018-06-22 18:48:14 +08:00
}
2018-07-13 13:47:30 +08:00
// Set mesh to CanvasRenderer.
2018-11-28 15:36:16 +08:00
Profiler.BeginSample ("Set mesh and texture to CanvasRenderer");
canvasRenderer.SetMesh (_mesh);
canvasRenderer.SetTexture (mainTexture);
// Copy the value from MaterialPropertyBlock to CanvasRenderer (#41)
2019-02-23 22:20:30 +08:00
UpdateAnimatableMaterialProperties ();
2018-11-28 15:36:16 +08:00
Profiler.EndSample ();
2018-07-13 13:47:30 +08:00
}
}
2018-11-28 15:36:16 +08:00
catch (System.Exception e)
2018-07-13 13:47:30 +08:00
{
2018-11-28 15:36:16 +08:00
Debug.LogException (e);
2018-06-22 18:48:14 +08:00
}
}
2018-12-21 19:50:08 +08:00
/// <summary>
/// Checks the trail.
/// </summary>
2018-11-28 15:36:16 +08:00
void CheckTrail ()
2018-06-22 18:48:14 +08:00
{
if (isActiveAndEnabled && !m_IsTrail && m_ParticleSystem && m_ParticleSystem.trails.enabled)
{
if (!m_TrailParticle)
{
2018-11-28 15:36:16 +08:00
m_TrailParticle = new GameObject ("[UIParticle] Trail").AddComponent<UIParticle> ();
2018-06-22 18:48:14 +08:00
var trans = m_TrailParticle.transform;
2018-11-28 15:36:16 +08:00
trans.SetParent (transform);
2018-06-22 18:48:14 +08:00
trans.localPosition = Vector3.zero;
trans.localRotation = Quaternion.identity;
trans.localScale = Vector3.one;
2018-11-28 15:36:16 +08:00
m_TrailParticle._renderer = GetComponent<ParticleSystemRenderer> ();
m_TrailParticle.m_ParticleSystem = GetComponent<ParticleSystem> ();
2018-06-22 18:48:14 +08:00
m_TrailParticle.m_IsTrail = true;
}
m_TrailParticle.enabled = true;
}
else if (m_TrailParticle)
{
m_TrailParticle.enabled = false;
}
}
2018-11-28 13:19:33 +08:00
/// <summary>
/// Set the parent of the soft mask.
/// </summary>
/// <param name="newParent">The parent soft mask to use.</param>
2018-11-28 15:36:16 +08:00
void SetParent (UIParticle newParent)
2018-11-28 13:19:33 +08:00
{
2018-11-28 15:36:16 +08:00
if (_parent != newParent && this != newParent)
2018-11-28 13:19:33 +08:00
{
2018-11-28 15:36:16 +08:00
if (_parent && _parent._children.Contains (this))
2018-11-28 13:19:33 +08:00
{
2018-11-28 15:36:16 +08:00
_parent._children.Remove (this);
_parent._children.RemoveAll (x => x == null);
2018-11-28 13:19:33 +08:00
}
_parent = newParent;
}
2018-11-28 15:36:16 +08:00
if (_parent && !_parent._children.Contains (this))
2018-11-28 13:19:33 +08:00
{
2018-11-28 15:36:16 +08:00
_parent._children.Add (this);
2018-11-28 13:19:33 +08:00
}
}
2019-02-23 22:20:30 +08:00
/// <summary>
/// Copy the value from MaterialPropertyBlock to CanvasRenderer (#41)
/// </summary>
void UpdateAnimatableMaterialProperties ()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
return;
#endif
if (0 == m_AnimatableProperties.Length)
return;
_renderer.GetPropertyBlock (s_Mpb);
for (int i = 0; i < canvasRenderer.materialCount; i++)
{
var mat = canvasRenderer.GetMaterial (i);
foreach (var ap in m_AnimatableProperties)
{
switch (ap.type)
{
case ShaderPropertyType.Color:
mat.SetColor (ap.id, s_Mpb.GetColor (ap.id));
break;
case ShaderPropertyType.Vector:
mat.SetVector (ap.id, s_Mpb.GetVector (ap.id));
break;
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
mat.SetFloat (ap.id, s_Mpb.GetFloat (ap.id));
break;
case ShaderPropertyType.Texture:
mat.SetTexture (ap.id, s_Mpb.GetTexture (ap.id));
break;
}
}
}
}
2018-06-22 18:48:14 +08:00
}
}