2020-11-10 00:42:28 +08:00
#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
2020-08-28 13:38:13 +08:00
using System.Collections.Generic ;
2022-07-01 14:55:59 +08:00
using System.Linq ;
2020-08-28 13:38:13 +08:00
using System.Runtime.CompilerServices ;
2020-09-28 20:35:40 +08:00
using Coffee.UIParticleExtensions ;
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 ;
2020-08-28 13:38:13 +08:00
[assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")]
2018-06-22 18:48:14 +08:00
namespace Coffee.UIExtensions
{
2020-02-12 20:38:06 +08:00
/// <summary>
/// Render maskable and sortable particle effect ,without Camera, RenderTexture or Canvas.
/// </summary>
2022-02-18 03:57:27 +08:00
[ExecuteAlways]
2020-08-28 13:38:13 +08:00
[RequireComponent(typeof(RectTransform))]
2020-08-11 23:09:55 +08:00
[RequireComponent(typeof(CanvasRenderer))]
2020-08-29 11:10:31 +08:00
public class UIParticle : MaskableGraphic
2020-02-12 20:38:06 +08:00
{
2022-06-11 22:10:17 +08:00
public enum MeshSharing
{
None ,
Auto ,
Primary ,
PrimarySimulator ,
2023-08-14 14:32:26 +08:00
Replica ,
2022-06-11 22:10:17 +08:00
}
2022-06-08 11:54:11 +08:00
[HideInInspector] [ SerializeField ] internal bool m_IsTrail = false ;
2020-02-12 20:38:06 +08:00
2022-06-08 11:54:11 +08:00
[Tooltip("Particle effect scale")]
[SerializeField]
private Vector3 m_Scale3D = new Vector3 ( 10 , 10 , 10 ) ;
2020-10-04 22:26:53 +08:00
2022-06-08 11:54:11 +08:00
[Tooltip("Animatable material properties. If you want to change the material properties of the ParticleSystem in Animation, enable it.")]
[SerializeField]
2020-08-20 03:42:16 +08:00
internal AnimatableProperty [ ] m_AnimatableProperties = new AnimatableProperty [ 0 ] ;
2020-02-12 20:38:06 +08:00
2022-06-08 11:54:11 +08:00
[Tooltip("Particles")]
[SerializeField]
2020-08-28 13:38:13 +08:00
private List < ParticleSystem > m_Particles = new List < ParticleSystem > ( ) ;
2020-02-12 20:38:06 +08:00
2023-08-14 14:32:26 +08:00
[Tooltip("Mesh sharing.None: disable mesh sharing.\nAuto: automatically select Primary/Replica.\nPrimary: provides particle simulation results to the same group.\nPrimary Simulator: Primary, but do not render the particle (simulation only).\nReplica: render simulation results provided by the primary.")]
2022-06-11 22:10:17 +08:00
[SerializeField]
private MeshSharing m_MeshSharing = MeshSharing . None ;
[Tooltip("Mesh sharing group ID. If non-zero is specified, particle simulation results are shared within the group.")]
[SerializeField]
private int m_GroupId = 0 ;
2022-06-14 12:27:54 +08:00
[SerializeField]
private int m_GroupMaxId = 0 ;
2022-06-25 01:04:23 +08:00
[SerializeField]
[Tooltip("The particles will be emitted at the ParticleSystem position.\nMove the UIParticle/ParticleSystem to move the particle.")]
private bool m_AbsoluteMode = false ;
2022-06-08 11:54:11 +08:00
private List < UIParticleRenderer > m_Renderers = new List < UIParticleRenderer > ( ) ;
2020-11-20 12:11:08 +08:00
2020-11-10 00:42:28 +08:00
#if !SERIALIZE_FIELD_MASKABLE
2020-11-16 02:07:35 +08:00
[SerializeField] private bool m_Maskable = true ;
2020-10-28 21:19:15 +08:00
# endif
2020-08-20 03:42:16 +08:00
private DrivenRectTransformTracker _tracker ;
2022-06-08 11:54:11 +08:00
private Camera _orthoCamera ;
2022-06-14 12:27:54 +08:00
private int _groupId ;
2020-08-28 13:38:13 +08:00
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 ; }
2020-08-28 13:38:13 +08:00
set { }
2020-08-20 03:42:16 +08:00
}
2020-02-12 20:38:06 +08:00
2022-06-11 22:10:17 +08:00
/// <summary>
/// Mesh sharing.None: disable mesh sharing.
2023-08-14 14:32:26 +08:00
/// Auto: automatically select Primary/Replica.
2022-06-11 22:10:17 +08:00
/// Primary: provides particle simulation results to the same group.
/// Primary Simulator: Primary, but do not render the particle (simulation only).
2023-08-14 14:32:26 +08:00
/// Replica: render simulation results provided by the primary.
2022-06-11 22:10:17 +08:00
/// </summary>
public MeshSharing meshSharing
{
get { return m_MeshSharing ; }
set { m_MeshSharing = value ; }
}
/// <summary>
/// Mesh sharing group ID. If non-zero is specified, particle simulation results are shared within the group.
/// </summary>
public int groupId
{
2022-06-14 12:27:54 +08:00
get { return _groupId ; }
set
{
if ( m_GroupId = = value ) return ;
m_GroupId = value ;
if ( m_GroupId ! = m_GroupMaxId )
ResetGroupId ( ) ;
}
}
public int groupMaxId
{
get { return m_GroupMaxId ; }
set
{
if ( m_GroupMaxId = = value ) return ;
m_GroupMaxId = value ;
ResetGroupId ( ) ;
}
2022-06-11 22:10:17 +08:00
}
2022-06-25 01:04:23 +08:00
/// <summary>
/// Absolute particle position mode.
/// The particles will be emitted at the ParticleSystem position.
/// Move the UIParticle/ParticleSystem to move the particle.
/// </summary>
public bool absoluteMode
{
get { return m_AbsoluteMode ; }
set { m_AbsoluteMode = value ; }
}
2022-06-11 22:10:17 +08:00
internal bool useMeshSharing
{
get { return m_MeshSharing ! = MeshSharing . None ; }
}
internal bool isPrimary
{
get { return m_MeshSharing = = MeshSharing . Primary | | m_MeshSharing = = MeshSharing . PrimarySimulator ; }
}
internal bool canSimulate
{
get { return m_MeshSharing = = MeshSharing . None | | m_MeshSharing = = MeshSharing . Auto | | m_MeshSharing = = MeshSharing . Primary | | m_MeshSharing = = MeshSharing . PrimarySimulator ; }
}
internal bool canRender
{
2023-08-14 14:32:26 +08:00
get { return m_MeshSharing = = MeshSharing . None | | m_MeshSharing = = MeshSharing . Auto | | m_MeshSharing = = MeshSharing . Primary | | m_MeshSharing = = MeshSharing . Replica ; }
2022-06-11 22:10:17 +08:00
}
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-10-04 22:26:53 +08:00
get { return m_Scale3D . x ; }
2022-06-08 11:54:11 +08:00
set { m_Scale3D = new Vector3 ( value , value , value ) ; }
2020-10-04 22:26:53 +08:00
}
/// <summary>
/// Particle effect scale.
/// </summary>
public Vector3 scale3D
{
get { return m_Scale3D ; }
2022-06-08 11:54:11 +08:00
set { m_Scale3D = value ; }
2020-02-12 20:38:06 +08:00
}
2020-08-28 13:38:13 +08:00
public List < ParticleSystem > particles
2020-02-12 20:38:06 +08:00
{
2020-08-28 13:38:13 +08:00
get { return m_Particles ; }
2020-02-12 20:38:06 +08:00
}
2022-06-08 11:54:11 +08:00
/// <summary>
/// Get all base materials to render.
/// </summary>
2020-08-28 13:38:13 +08:00
public IEnumerable < Material > materials
2020-08-12 22:19:08 +08:00
{
2022-06-08 11:54:11 +08:00
get
2020-08-28 13:38:13 +08:00
{
2022-06-08 11:54:11 +08:00
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 ;
2020-08-28 13:38:13 +08:00
}
2020-08-12 22:19:08 +08:00
}
2020-02-12 20:38:06 +08:00
2022-06-08 11:54:11 +08:00
public override Material materialForRendering
2020-02-12 20:38:06 +08:00
{
2022-06-08 11:54:11 +08:00
get { return null ; }
2020-08-20 03:42:16 +08:00
}
2020-02-12 20:38:06 +08:00
2022-06-08 11:54:11 +08:00
/// <summary>
/// Paused.
/// </summary>
public bool isPaused { get ; internal set ; }
2020-08-28 13:38:13 +08:00
public void Play ( )
2020-08-20 03:42:16 +08:00
{
2022-06-08 11:54:11 +08:00
particles . Exec ( p = > p . Simulate ( 0 , false , true ) ) ;
isPaused = false ;
2020-02-12 20:38:06 +08:00
}
2020-08-28 13:38:13 +08:00
public void Pause ( )
2020-08-20 03:42:16 +08:00
{
2020-08-28 13:38:13 +08:00
particles . Exec ( p = > p . Pause ( ) ) ;
2022-06-08 11:54:11 +08:00
isPaused = true ;
}
public void Resume ( )
{
isPaused = false ;
2020-08-20 03:42:16 +08:00
}
2020-08-28 13:38:13 +08:00
public void Stop ( )
2020-08-20 03:42:16 +08:00
{
2020-08-28 13:38:13 +08:00
particles . Exec ( p = > p . Stop ( ) ) ;
2022-06-08 11:54:11 +08:00
isPaused = true ;
2020-08-20 03:42:16 +08:00
}
2021-02-23 12:30:46 +08:00
public void Clear ( )
{
particles . Exec ( p = > p . Clear ( ) ) ;
2022-06-08 11:54:11 +08:00
isPaused = true ;
2021-02-23 12:30:46 +08:00
}
2020-09-02 13:15:30 +08:00
public void SetParticleSystemInstance ( GameObject instance )
{
SetParticleSystemInstance ( instance , true ) ;
}
2020-09-03 00:02:59 +08:00
public void SetParticleSystemInstance ( GameObject instance , bool destroyOldParticles )
2020-09-02 13:15:30 +08:00
{
if ( ! instance ) return ;
foreach ( Transform child in transform )
{
var go = child . gameObject ;
go . SetActive ( false ) ;
2020-09-03 00:02:59 +08:00
if ( ! destroyOldParticles ) continue ;
2020-09-02 13:15:30 +08:00
#if UNITY_EDITOR
if ( ! Application . isPlaying )
DestroyImmediate ( go ) ;
else
# endif
Destroy ( go ) ;
}
var tr = instance . transform ;
tr . SetParent ( transform , false ) ;
tr . localPosition = Vector3 . zero ;
2020-09-03 00:02:59 +08:00
RefreshParticles ( instance ) ;
2020-09-02 13:15:30 +08:00
}
public void SetParticleSystemPrefab ( GameObject prefab )
{
if ( ! prefab ) return ;
SetParticleSystemInstance ( Instantiate ( prefab . gameObject ) , true ) ;
}
2020-08-28 13:38:13 +08:00
public void RefreshParticles ( )
2020-08-20 03:42:16 +08:00
{
2020-09-03 00:02:59 +08:00
RefreshParticles ( gameObject ) ;
}
2022-06-17 12:35:19 +08:00
private void RefreshParticles ( GameObject root )
2020-09-03 00:02:59 +08:00
{
if ( ! root ) return ;
root . GetComponentsInChildren ( particles ) ;
2021-02-01 23:38:19 +08:00
particles . RemoveAll ( x = > x . GetComponentInParent < UIParticle > ( ) ! = this ) ;
2020-08-28 13:38:13 +08:00
2020-08-28 15:50:13 +08:00
foreach ( var ps in particles )
{
var tsa = ps . textureSheetAnimation ;
2022-06-08 11:54:11 +08:00
if ( tsa . mode = = ParticleSystemAnimationMode . Sprites & & tsa . uvChannelMask = = 0 )
2020-08-28 15:50:13 +08:00
tsa . uvChannelMask = UVChannelFlags . UV0 ;
}
2022-06-08 11:54:11 +08:00
RefreshParticles ( particles ) ;
2020-08-20 03:42:16 +08:00
}
2022-06-08 11:54:11 +08:00
public void RefreshParticles ( List < ParticleSystem > particles )
2020-02-12 20:38:06 +08:00
{
2022-06-08 11:54:11 +08:00
GetComponentsInChildren ( m_Renderers ) ;
2020-08-28 13:38:13 +08:00
var j = 0 ;
for ( var i = 0 ; i < particles . Count ; i + + )
2020-02-12 20:38:06 +08:00
{
2022-07-01 14:40:06 +08:00
if ( ! particles [ i ] ) continue ;
2022-06-08 11:54:11 +08:00
GetRenderer ( j + + ) . Set ( this , particles [ i ] , false ) ;
if ( particles [ i ] . trails . enabled )
2020-08-20 03:42:16 +08:00
{
2022-06-08 11:54:11 +08:00
GetRenderer ( j + + ) . Set ( this , particles [ i ] , true ) ;
2020-08-20 03:42:16 +08:00
}
2020-02-12 20:38:06 +08:00
}
2022-06-21 18:47:08 +08:00
for ( ; j < m_Renderers . Count ; j + + )
{
GetRenderer ( j ) . Clear ( j ) ;
}
2020-08-20 03:42:16 +08:00
}
2022-06-11 21:32:14 +08:00
internal void UpdateTransformScale ( )
2020-09-14 17:29:16 +08:00
{
2022-06-11 21:32:14 +08:00
//var newScale = Vector3.one;
2022-06-09 00:47:48 +08:00
//if (uiScaling)
2022-06-11 21:32:14 +08:00
//{
// newScale = transform.parent.lossyScale.Inverse();
//}
var newScale = transform . parent . lossyScale . Inverse ( ) ;
2022-06-08 11:54:11 +08:00
if ( transform . localScale ! = newScale )
2020-09-14 17:29:16 +08:00
{
2022-06-08 11:54:11 +08:00
transform . localScale = newScale ;
2020-09-14 17:29:16 +08:00
}
2022-06-11 21:32:14 +08:00
}
2020-08-29 02:55:46 +08:00
2022-06-11 21:32:14 +08:00
internal void UpdateRenderers ( )
{
2022-06-17 12:35:19 +08:00
if ( ! isActiveAndEnabled ) return ;
2023-04-28 07:17:24 +08:00
foreach ( var rend in m_Renderers )
2022-07-01 14:55:59 +08:00
{
2023-04-28 07:17:24 +08:00
if ( ! rend )
{
RefreshParticles ( particles ) ;
break ;
}
2022-07-01 14:55:59 +08:00
}
2022-06-08 11:54:11 +08:00
var bakeCamera = GetBakeCamera ( ) ;
for ( var i = 0 ; i < m_Renderers . Count ; i + + )
2020-08-29 02:55:46 +08:00
{
2022-07-01 14:55:59 +08:00
if ( ! m_Renderers [ i ] ) continue ;
2022-06-08 11:54:11 +08:00
m_Renderers [ i ] . UpdateMesh ( bakeCamera ) ;
2020-08-29 02:55:46 +08:00
}
}
2022-06-16 21:43:23 +08:00
internal void UpdateParticleCount ( )
{
for ( var i = 0 ; i < m_Renderers . Count ; i + + )
{
2022-07-01 14:55:59 +08:00
if ( ! m_Renderers [ i ] ) continue ;
2022-06-16 21:43:23 +08:00
m_Renderers [ i ] . UpdateParticleCount ( ) ;
}
}
2020-08-20 03:42:16 +08:00
protected override void OnEnable ( )
{
2020-11-10 00:42:28 +08:00
#if !SERIALIZE_FIELD_MASKABLE
2020-10-28 21:19:15 +08:00
maskable = m_Maskable ;
# endif
2022-06-14 12:27:54 +08:00
ResetGroupId ( ) ;
2022-06-08 11:54:11 +08:00
_tracker . Add ( this , rectTransform , DrivenTransformProperties . Scale ) ;
2020-08-28 13:38:13 +08:00
UIParticleUpdater . Register ( this ) ;
2022-06-08 11:54:11 +08:00
RegisterDirtyMaterialCallback ( UpdateRendererMaterial ) ;
2022-06-17 12:35:19 +08:00
if ( 0 < particles . Count )
{
RefreshParticles ( particles ) ;
}
else
{
RefreshParticles ( ) ;
}
2020-02-12 20:38:06 +08:00
base . OnEnable ( ) ;
2021-02-18 00:55:53 +08:00
}
2022-06-14 12:27:54 +08:00
internal void ResetGroupId ( )
{
if ( m_GroupId = = m_GroupMaxId )
{
_groupId = m_GroupId ;
}
else
{
_groupId = Random . Range ( m_GroupId , m_GroupMaxId + 1 ) ;
}
}
2020-02-12 20:38:06 +08:00
/// <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 ( ) ;
2022-06-08 11:54:11 +08:00
UIParticleUpdater . Unregister ( this ) ;
2022-06-11 21:32:14 +08:00
m_Renderers . ForEach ( r = > r . Clear ( ) ) ;
2022-06-08 11:54:11 +08:00
UnregisterDirtyMaterialCallback ( UpdateRendererMaterial ) ;
2020-02-12 20:38:06 +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
2022-06-08 11:54:11 +08:00
protected override void UpdateMaterial ( )
{
}
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>
/// Callback for when properties have been changed by animation.
/// </summary>
protected override void OnDidApplyAnimationProperties ( )
{
}
2018-11-28 13:19:33 +08:00
2022-06-08 11:54:11 +08:00
private void UpdateRendererMaterial ( )
2020-02-12 20:38:06 +08:00
{
2022-06-08 11:54:11 +08:00
for ( var i = 0 ; i < m_Renderers . Count ; i + + )
2020-02-12 20:38:06 +08:00
{
2022-06-08 11:54:11 +08:00
if ( ! m_Renderers [ i ] ) continue ;
m_Renderers [ i ] . maskable = maskable ;
m_Renderers [ i ] . SetMaterialDirty ( ) ;
2020-02-12 20:38:06 +08:00
}
2020-08-12 22:19:08 +08:00
}
2020-08-28 15:50:13 +08:00
2022-06-11 21:32:14 +08:00
internal UIParticleRenderer GetRenderer ( int index )
2020-10-04 22:26:53 +08:00
{
2022-06-08 11:54:11 +08:00
if ( m_Renderers . Count < = index )
{
2022-06-11 21:32:14 +08:00
m_Renderers . Add ( UIParticleRenderer . AddRenderer ( this , index ) ) ;
2022-06-08 11:54:11 +08:00
}
2022-07-01 14:55:59 +08:00
if ( ! m_Renderers [ index ] )
{
m_Renderers [ index ] = UIParticleRenderer . AddRenderer ( this , index ) ;
}
2022-06-08 11:54:11 +08:00
return m_Renderers [ index ] ;
2020-10-04 22:26:53 +08:00
}
2022-06-08 11:54:11 +08:00
private Camera GetBakeCamera ( )
2020-08-28 15:50:13 +08:00
{
2022-06-08 11:54:11 +08:00
if ( ! canvas ) return Camera . main ;
2020-08-28 15:50:13 +08:00
2022-06-08 11:54:11 +08:00
// World camera.
var root = canvas . rootCanvas ;
if ( root . renderMode ! = RenderMode . ScreenSpaceOverlay ) return root . worldCamera ? root . worldCamera : Camera . main ;
// Create ortho-camera.
if ( ! _orthoCamera )
2020-10-04 22:26:53 +08:00
{
2022-06-08 11:54:11 +08:00
_orthoCamera = GetComponentInChildren < Camera > ( ) ;
if ( ! _orthoCamera )
{
var go = new GameObject ( "UIParticleOverlayCamera" )
{
hideFlags = HideFlags . DontSave ,
} ;
go . SetActive ( false ) ;
go . transform . SetParent ( transform , false ) ;
_orthoCamera = go . AddComponent < Camera > ( ) ;
_orthoCamera . enabled = false ;
}
2020-10-04 22:26:53 +08:00
}
2022-06-08 11:54:11 +08:00
//
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 ;
2020-08-28 15:50:13 +08:00
}
2020-02-12 20:38:06 +08:00
}
2020-04-30 11:28:48 +08:00
}