2018-06-22 18:48:14 +08:00
using UnityEngine ;
2020-08-20 03:42:16 +08:00
using UnityEngine.Rendering ;
2018-06-22 18:48:14 +08:00
using UnityEngine.UI ;
namespace Coffee.UIExtensions
{
2020-02-12 20:38:06 +08:00
/// <summary>
/// Render maskable and sortable particle effect ,without Camera, RenderTexture or Canvas.
/// </summary>
[ExecuteInEditMode]
2020-08-11 23:09:55 +08:00
[RequireComponent(typeof(CanvasRenderer))]
2020-02-12 20:38:06 +08:00
public class UIParticle : MaskableGraphic
{
//################################
// Serialize Members.
//################################
2020-08-12 22:19:08 +08:00
[Tooltip("The ParticleSystem rendered by CanvasRenderer")] [ SerializeField ]
ParticleSystem m_ParticleSystem ;
[Tooltip("The UIParticle to render trail effect")] [ SerializeField ]
2020-08-20 03:42:16 +08:00
internal UIParticle m_TrailParticle ;
2020-08-12 22:19:08 +08:00
2020-02-12 20:38:06 +08:00
[HideInInspector] [ SerializeField ] bool m_IsTrail = false ;
2020-08-20 03:42:16 +08:00
[Tooltip("Ignore canvas scaler")] [ SerializeField ]
bool m_IgnoreCanvasScaler = false ;
2020-08-12 22:19:08 +08:00
[Tooltip("Ignore parent scale")] [ SerializeField ]
bool m_IgnoreParent = false ;
2020-08-20 03:42:16 +08:00
[Tooltip("Particle effect scale")] [ SerializeField ]
float m_Scale = 0 ;
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
[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 ] ;
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
[Tooltip("Particle effect scale")] [ SerializeField ]
internal Vector3 m_Scale3D = Vector3 . one ;
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
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 ;
2020-02-12 20:38:06 +08:00
//################################
// Public/Protected Members.
//################################
2020-08-20 03:42:16 +08:00
/// <summary>
/// Should this graphic be considered a target for raycasting?
/// </summary>
public override bool raycastTarget
2020-02-12 20:38:06 +08:00
{
2020-08-20 03:42:16 +08:00
get { return false ; }
set { base . raycastTarget = value ; }
}
2020-08-12 22:19:08 +08:00
2020-08-20 03:42:16 +08:00
/// <summary>
/// Cached ParticleSystem.
/// </summary>
public ParticleSystem cachedParticleSystem
{
get { return m_ParticleSystem ? m_ParticleSystem : ( m_ParticleSystem = GetComponent < ParticleSystem > ( ) ) ; }
2020-02-12 20:38:06 +08:00
}
2020-08-20 03:42:16 +08:00
/// <summary>
/// Cached ParticleSystem.
/// </summary>
internal ParticleSystemRenderer cachedRenderer
2020-02-12 20:38:06 +08:00
{
2020-08-20 03:42:16 +08:00
get { return _renderer ; }
}
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
public bool ignoreCanvasScaler
{
get { return m_IgnoreCanvasScaler ; }
set { m_IgnoreCanvasScaler = value ; }
2020-02-12 20:38:06 +08:00
}
/// <summary>
/// Particle effect scale.
/// </summary>
2020-08-12 22:19:08 +08:00
public float scale
{
2020-08-20 03:42:16 +08:00
get { return m_Scale3D . x ; }
set { m_Scale3D . Set ( value , value , value ) ; }
2020-08-12 22:19:08 +08:00
}
2020-02-12 20:38:06 +08:00
/// <summary>
2020-08-20 03:42:16 +08:00
/// Particle effect scale.
2020-02-12 20:38:06 +08:00
/// </summary>
2020-08-20 03:42:16 +08:00
public Vector3 scale3D
2020-02-12 20:38:06 +08:00
{
2020-08-20 03:42:16 +08:00
get { return m_Scale3D ; }
set { m_Scale3D = value ; }
2020-02-12 20:38:06 +08:00
}
2020-08-20 03:42:16 +08:00
internal bool isTrailParticle
2020-02-12 20:38:06 +08:00
{
2020-08-20 03:42:16 +08:00
get { return m_IsTrail ; }
2020-02-12 20:38:06 +08:00
}
2020-08-20 03:42:16 +08:00
internal bool isSpritesMode
2020-08-12 22:19:08 +08:00
{
2020-08-20 03:42:16 +08:00
get { return textureSheetAnimationModule . enabled & & textureSheetAnimationModule . mode = = ParticleSystemAnimationMode . Sprites ; }
2020-08-12 22:19:08 +08:00
}
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
private bool isSpritesModeAndHasTrail
2020-08-12 22:19:08 +08:00
{
2020-08-20 03:42:16 +08:00
get { return isSpritesMode & & trailModule . enabled ; }
2020-08-12 22:19:08 +08:00
}
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
private ParticleSystem . TextureSheetAnimationModule textureSheetAnimationModule
2020-02-12 20:38:06 +08:00
{
2020-08-20 03:42:16 +08:00
get { return cachedParticleSystem . textureSheetAnimation ; }
}
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
internal ParticleSystem . TrailModule trailModule
{
get { return cachedParticleSystem . trails ; }
2020-02-12 20:38:06 +08:00
}
2020-08-20 03:42:16 +08:00
internal ParticleSystem . MainModule mainModule
{
get { return cachedParticleSystem . main ; }
}
public bool isValid
{
get { return m_ParticleSystem & & _renderer & & canvas ; }
}
public Mesh bakedMesh
{
get { return _bakedMesh ; }
}
protected override void UpdateMaterial ( )
2020-02-12 20:38:06 +08:00
{
2020-08-20 03:42:16 +08:00
if ( ! _renderer ) return ;
if ( ! isSpritesMode ) // Non sprite mode: canvas renderer has main and trail materials.
{
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.
2020-02-12 20:38:06 +08:00
{
2020-08-20 03:42:16 +08:00
canvasRenderer . materialCount = 1 ;
canvasRenderer . SetMaterial ( GetModifiedMaterial ( _renderer . sharedMaterial , 0 ) , 0 ) ;
2020-02-12 20:38:06 +08:00
}
2020-08-20 03:42:16 +08:00
}
private Material GetModifiedMaterial ( Material baseMaterial , int index )
{
if ( ! baseMaterial | | 1 < index | | ! isActiveAndEnabled ) return null ;
2020-08-12 22:19:08 +08:00
2020-08-20 03:42:16 +08:00
var hasAnimatableProperties = 0 < m_AnimatableProperties . Length & & index = = 0 ;
if ( hasAnimatableProperties | | isTrailParticle )
baseMaterial = new Material ( baseMaterial ) ;
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
var baseMat = baseMaterial ;
if ( m_ShouldRecalculateStencil )
2020-02-12 20:38:06 +08:00
{
2020-08-20 03:42:16 +08:00
m_ShouldRecalculateStencil = false ;
if ( maskable )
{
var sortOverrideCanvas = MaskUtilities . FindRootSortOverrideCanvas ( transform ) ;
m_StencilValue = MaskUtilities . GetStencilDepth ( transform , sortOverrideCanvas ) + index ;
}
else
{
m_StencilValue = 0 ;
}
2020-02-12 20:38:06 +08:00
}
2020-08-12 22:19:08 +08:00
2020-08-20 03:42:16 +08:00
var component = GetComponent < Mask > ( ) ;
if ( m_StencilValue < = 0 | | ( component ! = null & & component . IsActive ( ) ) ) return baseMat ;
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 ] ;
return baseMat ;
}
/// <summary>
/// This function is called when the object becomes enabled and active.
/// </summary>
protected override void OnEnable ( )
{
UpdateVersionIfNeeded ( ) ;
_tracker . Add ( this , rectTransform , DrivenTransformProperties . Scale ) ;
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
// Initialize.
2020-02-12 20:38:06 +08:00
_renderer = cachedParticleSystem ? cachedParticleSystem . GetComponent < ParticleSystemRenderer > ( ) : null ;
2020-08-20 03:42:16 +08:00
if ( _renderer ! = null )
2020-02-12 20:38:06 +08:00
_renderer . enabled = false ;
2020-08-20 03:42:16 +08:00
CheckMaterials ( ) ;
2020-02-12 20:38:06 +08:00
// Create objects.
2020-08-20 03:42:16 +08:00
_bakedMesh = new Mesh ( ) ;
_bakedMesh . MarkDynamic ( ) ;
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
MeshHelper . Register ( ) ;
BakingCamera . Register ( ) ;
UIParticleUpdater . Register ( this ) ;
2020-02-12 20:38:06 +08:00
base . OnEnable ( ) ;
}
/// <summary>
/// This function is called when the behaviour becomes disabled.
/// </summary>
protected override void OnDisable ( )
{
2020-08-20 03:42:16 +08:00
_tracker . Clear ( ) ;
2020-08-12 22:19:08 +08:00
2020-08-20 03:42:16 +08:00
// Destroy object.
DestroyImmediate ( _bakedMesh ) ;
_bakedMesh = null ;
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
MeshHelper . Unregister ( ) ;
BakingCamera . Unregister ( ) ;
UIParticleUpdater . Unregister ( this ) ;
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
CheckMaterials ( ) ;
2018-06-22 18:48:14 +08:00
2020-08-20 03:42:16 +08:00
// Remove mask materials.
for ( var i = 0 ; i < _maskMaterials . Length ; i + + )
2020-02-12 20:38:06 +08:00
{
2020-08-20 03:42:16 +08:00
StencilMaterial . Remove ( _maskMaterials [ i ] ) ;
_maskMaterials [ i ] = null ;
2020-02-12 20:38:06 +08:00
}
2020-08-12 22:19:08 +08:00
2020-08-20 03:42:16 +08:00
base . OnDisable ( ) ;
2020-02-12 20:38:06 +08:00
}
2019-02-26 10:26:56 +08:00
2020-02-12 20:38:06 +08:00
/// <summary>
/// Call to update the geometry of the Graphic onto the CanvasRenderer.
/// </summary>
protected override void UpdateGeometry ( )
{
}
/// <summary>
/// This function is called when the parent property of the transform of the GameObject has changed.
/// </summary>
protected override void OnTransformParentChanged ( )
{
}
/// <summary>
/// Callback for when properties have been changed by animation.
/// </summary>
protected override void OnDidApplyAnimationProperties ( )
{
}
2018-11-28 13:19:33 +08:00
2020-02-12 20:38:06 +08:00
//################################
// Private Members.
//################################
2020-08-20 03:42:16 +08:00
private static bool HasMaterialChanged ( Material material , ref int current )
2020-02-12 20:38:06 +08:00
{
2020-08-20 03:42:16 +08:00
var old = current ;
current = material ? material . GetInstanceID ( ) : 0 ;
return current ! = old ;
2020-02-12 20:38:06 +08:00
}
2020-08-20 03:42:16 +08:00
internal void UpdateTrailParticle ( )
2020-02-12 20:38:06 +08:00
{
2020-08-20 03:42:16 +08:00
// Should have a UIParticle for trail particle?
if ( isActiveAndEnabled & & isValid & & ! isTrailParticle & & isSpritesModeAndHasTrail )
2020-02-12 20:38:06 +08:00
{
if ( ! m_TrailParticle )
{
2020-08-20 03:42:16 +08:00
// Create new UIParticle for trail particle
2020-02-12 20:38:06 +08:00
m_TrailParticle = new GameObject ( "[UIParticle] Trail" ) . AddComponent < UIParticle > ( ) ;
var trans = m_TrailParticle . transform ;
2020-08-20 03:42:16 +08:00
trans . SetPositionAndRotation ( Vector3 . zero , Quaternion . identity ) ;
2020-02-12 20:38:06 +08:00
trans . localScale = Vector3 . one ;
2020-08-20 03:42:16 +08:00
trans . SetParent ( transform , false ) ;
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
m_TrailParticle . _renderer = _renderer ;
m_TrailParticle . m_ParticleSystem = m_ParticleSystem ;
2020-02-12 20:38:06 +08:00
m_TrailParticle . m_IsTrail = true ;
}
2020-08-12 22:19:08 +08:00
2020-08-20 03:42:16 +08:00
m_TrailParticle . gameObject . hideFlags = HideFlags . DontSave ;
2020-02-12 20:38:06 +08:00
}
else if ( m_TrailParticle )
{
2020-08-20 03:42:16 +08:00
// Destroy a UIParticle for trail particle.
2019-02-23 22:20:30 +08:00
#if UNITY_EDITOR
2020-08-20 03:42:16 +08:00
if ( ! Application . isPlaying )
DestroyImmediate ( m_TrailParticle . gameObject ) ;
else
2019-02-23 22:20:30 +08:00
# endif
2020-08-20 03:42:16 +08:00
Destroy ( m_TrailParticle . gameObject ) ;
2020-02-12 20:38:06 +08:00
2020-08-20 03:42:16 +08:00
m_TrailParticle = null ;
2020-02-12 20:38:06 +08:00
}
}
2020-08-12 22:19:08 +08:00
2020-08-20 03:42:16 +08:00
internal void CheckMaterials ( )
2020-08-12 22:19:08 +08:00
{
2020-08-20 03:42:16 +08:00
if ( ! _renderer ) return ;
var matChanged = HasMaterialChanged ( _renderer . sharedMaterial , ref _cachedSharedMaterialId ) ;
var matChanged2 = HasMaterialChanged ( _renderer . trailMaterial , ref _cachedTrailMaterialId ) ;
var modeChanged = _cachedSpritesModeAndHasTrail ! = isSpritesModeAndHasTrail ;
_cachedSpritesModeAndHasTrail = isSpritesModeAndHasTrail ;
2020-08-12 22:19:08 +08:00
2020-08-20 03:42:16 +08:00
if ( matChanged | | matChanged2 | | modeChanged )
SetMaterialDirty ( ) ;
2020-08-12 22:19:08 +08:00
}
2020-08-20 03:42:16 +08:00
private void UpdateVersionIfNeeded ( )
2020-08-12 22:19:08 +08:00
{
2020-08-20 03:42:16 +08:00
if ( Mathf . Approximately ( m_Scale , 0 ) ) return ;
var parent = GetComponentInParent < UIParticle > ( ) ;
if ( m_IgnoreParent | | ! parent )
scale3D = m_Scale * transform . localScale ;
else
scale3D = transform . localScale ;
m_Scale = 0 ;
2020-08-12 22:19:08 +08:00
}
2020-02-12 20:38:06 +08:00
}
2020-04-30 11:28:48 +08:00
}