2022-06-09 01:05:30 +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-02-17 11:22:12 +08:00
using UnityEditor ;
2018-06-22 18:48:14 +08:00
using UnityEditor.UI ;
using UnityEngine ;
2018-11-28 18:55:56 +08:00
using System.Collections.Generic ;
2020-09-02 01:39:05 +08:00
using System.Linq ;
2020-08-28 13:38:13 +08:00
using UnityEditorInternal ;
using UnityEngine.UI ;
2022-06-09 01:05:30 +08:00
using System ;
#if UNITY_2021_2_OR_NEWER
using UnityEditor.Overlays ;
# else
using System.Reflection ;
# endif
2018-06-22 18:48:14 +08:00
namespace Coffee.UIExtensions
{
2020-02-12 20:38:06 +08:00
[CustomEditor(typeof(UIParticle))]
[CanEditMultipleObjects]
2020-08-20 03:42:16 +08:00
internal class UIParticleEditor : GraphicEditor
2020-02-12 20:38:06 +08:00
{
2022-06-09 01:05:30 +08:00
#if UNITY_2021_2_OR_NEWER
#if UNITY_2022_1_OR_NEWER
[Overlay(typeof(SceneView), "Scene View/UI Particles", "UI Particles", true, defaultDockPosition = DockPosition.Bottom, defaultDockZone = DockZone.Floating, defaultLayout = Layout.Panel)]
# else
[Overlay(typeof(SceneView), "Scene View/UI Particles", "UI Particles", true)]
# endif
private class UIParticleOverlay : IMGUIOverlay , ITransientOverlay
{
public bool visible = > s_SerializedObject ! = null ;
public override void OnGUI ( )
{
if ( visible )
{
WindowFunction ( null , null ) ;
}
}
}
# endif
2020-02-12 20:38:06 +08:00
//################################
// Constant or Static Members.
//################################
2020-08-31 08:28:38 +08:00
private static readonly GUIContent s_ContentRenderingOrder = new GUIContent ( "Rendering Order" ) ;
private static readonly GUIContent s_ContentRefresh = new GUIContent ( "Refresh" ) ;
2020-09-01 12:50:54 +08:00
private static readonly GUIContent s_ContentFix = new GUIContent ( "Fix" ) ;
2020-10-04 22:26:53 +08:00
private static readonly GUIContent s_ContentMaterial = new GUIContent ( "Material" ) ;
private static readonly GUIContent s_ContentTrailMaterial = new GUIContent ( "Trail Material" ) ;
private static readonly GUIContent s_Content3D = new GUIContent ( "3D" ) ;
2022-06-14 12:27:54 +08:00
private static readonly GUIContent s_ContentRandom = new GUIContent ( "Random" ) ;
2020-10-04 22:26:53 +08:00
private static readonly GUIContent s_ContentScale = new GUIContent ( "Scale" ) ;
2022-06-09 01:05:30 +08:00
private static SerializedObject s_SerializedObject ;
2020-08-20 03:42:16 +08:00
2022-06-09 01:05:30 +08:00
#if !SERIALIZE_FIELD_MASKABLE
2022-06-08 11:54:11 +08:00
private SerializedProperty m_Maskable ;
2022-06-09 01:05:30 +08:00
# endif
2022-06-08 11:54:11 +08:00
private SerializedProperty m_Scale3D ;
private SerializedProperty m_AnimatableProperties ;
2022-06-11 22:10:17 +08:00
private SerializedProperty m_MeshSharing ;
private SerializedProperty m_GroupId ;
2022-06-14 12:27:54 +08:00
private SerializedProperty m_GroupMaxId ;
2020-08-28 13:38:13 +08:00
private ReorderableList _ro ;
2022-06-09 01:19:26 +08:00
static private bool _xyzMode ;
2022-06-14 12:27:54 +08:00
private bool _showMax ;
2020-08-20 03:42:16 +08:00
private static readonly List < string > s_MaskablePropertyNames = new List < string >
2020-02-12 20:38:06 +08:00
{
"_Stencil" ,
"_StencilComp" ,
"_StencilOp" ,
"_StencilWriteMask" ,
"_StencilReadMask" ,
"_ColorMask" ,
} ;
2020-08-20 03:42:16 +08:00
2022-06-09 01:05:30 +08:00
[InitializeOnLoadMethod]
static void Init ( )
{
#if !UNITY_2021_2_OR_NEWER
// static void Window(GUIContent title, WindowFunction sceneViewFunc, int order, UnityEngine.Object target, WindowDisplayOption option)
var miSceneViewOverlayWindow = Type . GetType ( "UnityEditor.SceneViewOverlay, UnityEditor" )
. GetMethods ( BindingFlags . Public | BindingFlags . Static )
. First ( x = > x . Name = = "Window" & & x . GetParameters ( ) . Length = = 5 ) ;
var windowFunction = ( Action < UnityEngine . Object , SceneView > ) WindowFunction ;
var windowFunctionType = Type . GetType ( "UnityEditor.SceneViewOverlay+WindowFunction, UnityEditor" ) ;
var windowFunctionDelegate = Delegate . CreateDelegate ( windowFunctionType , windowFunction . Method ) ;
var windowTitle = new GUIContent ( ObjectNames . NicifyVariableName ( typeof ( UIParticle ) . Name ) ) ;
var sceneViewArgs = new object [ ] { windowTitle , windowFunctionDelegate , 599 , null , 2 } ;
#if UNITY_2019_1_OR_NEWER
SceneView . duringSceneGui + = _ = > miSceneViewOverlayWindow . Invoke ( null , sceneViewArgs ) ;
# else
SceneView . onSceneGUIDelegate + = _ = >
# endif
{
if ( s_SerializedObject ! = null )
{
miSceneViewOverlayWindow . Invoke ( null , sceneViewArgs ) ;
}
} ;
# endif
Func < SerializedObject > createSerializeObject = ( ) = >
{
var uiParticles = Selection . gameObjects
. Select ( x = > x . GetComponent < ParticleSystem > ( ) )
. Where ( x = > x )
. Select ( x = > x . GetComponentInParent < UIParticle > ( ) )
. Where ( x = > x )
. Concat (
Selection . gameObjects
. Select ( x = > x . GetComponent < UIParticle > ( ) )
. Where ( x = > x )
)
. Distinct ( )
. ToArray ( ) ;
return uiParticles . Any ( ) ? new SerializedObject ( uiParticles ) : null ;
} ;
s_SerializedObject = createSerializeObject ( ) ;
Selection . selectionChanged + = ( ) = > s_SerializedObject = createSerializeObject ( ) ;
}
2020-02-12 20:38:06 +08:00
//################################
// Public/Protected Members.
//################################
/// <summary>
/// This function is called when the object becomes enabled and active.
/// </summary>
protected override void OnEnable ( )
{
base . OnEnable ( ) ;
2022-06-08 11:54:11 +08:00
m_Maskable = serializedObject . FindProperty ( "m_Maskable" ) ;
m_Scale3D = serializedObject . FindProperty ( "m_Scale3D" ) ;
m_AnimatableProperties = serializedObject . FindProperty ( "m_AnimatableProperties" ) ;
2022-06-11 22:10:17 +08:00
m_MeshSharing = serializedObject . FindProperty ( "m_MeshSharing" ) ;
m_GroupId = serializedObject . FindProperty ( "m_GroupId" ) ;
2022-06-14 12:27:54 +08:00
m_GroupMaxId = serializedObject . FindProperty ( "m_GroupMaxId" ) ;
2020-08-28 13:38:13 +08:00
var sp = serializedObject . FindProperty ( "m_Particles" ) ;
2020-10-04 22:26:53 +08:00
_ro = new ReorderableList ( sp . serializedObject , sp , true , true , true , true ) ;
_ro . elementHeight = EditorGUIUtility . singleLineHeight * 3 + 4 ;
2022-06-09 01:19:26 +08:00
_ro . elementHeightCallback = _ = > 3 * ( EditorGUIUtility . singleLineHeight + 2 ) ;
2020-08-28 13:38:13 +08:00
_ro . drawElementCallback = ( rect , index , active , focused ) = >
{
2020-10-04 22:26:53 +08:00
EditorGUI . BeginDisabledGroup ( sp . hasMultipleDifferentValues ) ;
2020-08-28 13:38:13 +08:00
rect . y + = 1 ;
rect . height = EditorGUIUtility . singleLineHeight ;
2020-10-04 22:26:53 +08:00
var p = sp . GetArrayElementAtIndex ( index ) ;
EditorGUI . ObjectField ( rect , p , GUIContent . none ) ;
rect . x + = 15 ;
rect . width - = 15 ;
var ps = p . objectReferenceValue as ParticleSystem ;
var materials = ps
? new SerializedObject ( ps . GetComponent < ParticleSystemRenderer > ( ) ) . FindProperty ( "m_Materials" )
: null ;
rect . y + = rect . height + 1 ;
MaterialField ( rect , s_ContentMaterial , materials , 0 ) ;
rect . y + = rect . height + 1 ;
MaterialField ( rect , s_ContentTrailMaterial , materials , 1 ) ;
EditorGUI . EndDisabledGroup ( ) ;
if ( materials ! = null )
{
materials . serializedObject . ApplyModifiedProperties ( ) ;
}
2020-08-28 13:38:13 +08:00
} ;
2022-06-08 11:54:11 +08:00
_ro . drawHeaderCallback = rect = >
2020-08-28 13:38:13 +08:00
{
2020-11-20 14:10:30 +08:00
#if !UNITY_2019_3_OR_NEWER
rect . y - = 1 ;
# endif
2020-08-31 08:28:38 +08:00
EditorGUI . LabelField ( new Rect ( rect . x , rect . y , 150 , rect . height ) , s_ContentRenderingOrder ) ;
2020-08-28 13:38:13 +08:00
2020-11-20 14:10:30 +08:00
if ( GUI . Button ( new Rect ( rect . width - 35 , rect . y , 60 , rect . height ) , s_ContentRefresh , EditorStyles . miniButton ) )
2020-08-28 13:38:13 +08:00
{
2020-08-31 08:28:38 +08:00
foreach ( UIParticle t in targets )
{
t . RefreshParticles ( ) ;
}
2020-08-28 13:38:13 +08:00
}
} ;
2022-06-08 11:54:11 +08:00
_ro . onReorderCallback = _ = >
{
foreach ( UIParticle t in targets )
{
t . RefreshParticles ( t . particles ) ;
}
} ;
2020-02-12 20:38:06 +08:00
}
2020-10-04 22:26:53 +08:00
private static void MaterialField ( Rect rect , GUIContent label , SerializedProperty sp , int index )
{
if ( sp = = null | | sp . arraySize < = index )
{
EditorGUI . BeginDisabledGroup ( true ) ;
EditorGUI . ObjectField ( rect , label , null , typeof ( Material ) , true ) ;
EditorGUI . EndDisabledGroup ( ) ;
}
else
{
EditorGUI . PropertyField ( rect , sp . GetArrayElementAtIndex ( index ) , label ) ;
}
}
2020-02-12 20:38:06 +08:00
/// <summary>
/// Implement this function to make a custom inspector.
/// </summary>
public override void OnInspectorGUI ( )
{
2020-08-20 03:42:16 +08:00
var current = target as UIParticle ;
if ( current = = null ) return ;
2020-02-12 20:38:06 +08:00
serializedObject . Update ( ) ;
2020-10-28 21:19:15 +08:00
// Maskable
2022-06-08 11:54:11 +08:00
EditorGUILayout . PropertyField ( m_Maskable ) ;
2020-02-12 20:38:06 +08:00
2020-08-28 13:38:13 +08:00
// Scale
2022-06-11 22:10:17 +08:00
EditorGUI . BeginDisabledGroup ( ! m_MeshSharing . hasMultipleDifferentValues & & m_MeshSharing . intValue = = 4 ) ;
2022-06-08 11:54:11 +08:00
_xyzMode = DrawFloatOrVector3Field ( m_Scale3D , _xyzMode ) ;
2022-06-11 22:10:17 +08:00
EditorGUI . EndDisabledGroup ( ) ;
2020-08-28 13:38:13 +08:00
2020-02-12 20:38:06 +08:00
// AnimatableProperties
2020-09-02 01:39:05 +08:00
var mats = current . particles
. Where ( x = > x )
. Select ( x = > x . GetComponent < ParticleSystemRenderer > ( ) . sharedMaterial )
. Where ( x = > x )
. ToArray ( ) ;
2020-10-04 22:26:53 +08:00
// Animated properties
2022-06-08 11:54:11 +08:00
AnimatedPropertiesEditor . DrawAnimatableProperties ( m_AnimatableProperties , mats ) ;
2020-02-12 20:38:06 +08:00
2022-06-11 22:10:17 +08:00
// Mesh sharing
2022-06-14 12:27:54 +08:00
EditorGUI . BeginChangeCheck ( ) ;
_showMax = DrawMeshSharing ( m_MeshSharing , m_GroupId , m_GroupMaxId , _showMax ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
serializedObject . ApplyModifiedProperties ( ) ;
foreach ( var uip in targets . OfType < UIParticle > ( ) )
{
uip . ResetGroupId ( ) ;
}
}
2022-06-11 22:10:17 +08:00
2020-10-04 22:26:53 +08:00
// Target ParticleSystems.
2020-08-28 13:38:13 +08:00
_ro . DoLayoutList ( ) ;
2020-02-12 20:38:06 +08:00
2020-09-01 12:50:54 +08:00
serializedObject . ApplyModifiedProperties ( ) ;
2020-08-20 03:42:16 +08:00
// Does the shader support UI masks?
2020-08-28 13:38:13 +08:00
if ( current . maskable & & current . GetComponentInParent < Mask > ( ) )
2020-02-12 20:38:06 +08:00
{
2020-08-28 13:38:13 +08:00
foreach ( var mat in current . materials )
2020-02-12 20:38:06 +08:00
{
2020-08-31 08:28:38 +08:00
if ( ! mat | | ! mat . shader ) continue ;
2020-08-28 13:38:13 +08:00
var shader = mat . shader ;
foreach ( var propName in s_MaskablePropertyNames )
{
if ( mat . HasProperty ( propName ) ) continue ;
2020-08-20 03:42:16 +08:00
2020-08-28 13:38:13 +08:00
EditorGUILayout . HelpBox ( string . Format ( "Shader '{0}' doesn't have '{1}' property. This graphic cannot be masked." , shader . name , propName ) , MessageType . Warning ) ;
break ;
}
2020-02-12 20:38:06 +08:00
}
2020-08-12 01:10:22 +08:00
}
2020-02-12 20:38:06 +08:00
2022-06-08 11:54:11 +08:00
// UIParticle for trail should be removed.
2020-09-02 01:39:05 +08:00
if ( FixButton ( current . m_IsTrail , "This UIParticle component should be removed. The UIParticle for trails is no longer needed." ) )
2020-09-01 12:50:54 +08:00
{
DestroyUIParticle ( current ) ;
}
2022-06-10 15:12:15 +08:00
// #203: When using linear color space, the particle colors are not output correctly.
// To fix, set 'Apply Active Color Space' in renderer module to false.
var allPsRenderers = targets . OfType < UIParticle > ( )
. SelectMany ( x = > x . particles )
. Where ( x = > x )
. Select ( x = > x . GetComponent < ParticleSystemRenderer > ( ) )
. ToArray ( ) ;
if ( 0 < allPsRenderers . Length )
{
var so = new SerializedObject ( allPsRenderers ) ;
var sp = so . FindProperty ( "m_ApplyActiveColorSpace" ) ; //.boolValue = false;
if ( FixButton ( sp . boolValue | | sp . hasMultipleDifferentValues , "When using linear color space, the particle colors are not output correctly.\nTo fix, set 'Apply Active Color Space' in renderer module to false." ) )
{
sp . boolValue = false ;
so . ApplyModifiedProperties ( ) ;
}
}
2020-09-01 12:50:54 +08:00
}
2022-06-14 12:27:54 +08:00
private static bool DrawMeshSharing ( SerializedProperty spMeshSharing , SerializedProperty spGroupId , SerializedProperty spGroupMaxId , bool showMax )
2022-06-11 22:10:17 +08:00
{
2022-06-14 12:27:54 +08:00
showMax | = spGroupId . intValue ! = spGroupMaxId . intValue | |
spGroupId . hasMultipleDifferentValues | |
spGroupMaxId . hasMultipleDifferentValues ;
EditorGUILayout . BeginHorizontal ( ) ;
EditorGUILayout . PropertyField ( spMeshSharing ) ;
EditorGUI . BeginChangeCheck ( ) ;
showMax = GUILayout . Toggle ( showMax , s_ContentRandom , EditorStyles . miniButton , GUILayout . Width ( 60 ) ) ;
if ( EditorGUI . EndChangeCheck ( ) & & ! showMax )
spGroupMaxId . intValue = spGroupId . intValue ;
EditorGUILayout . EndHorizontal ( ) ;
EditorGUI . BeginDisabledGroup ( spMeshSharing . intValue = = 0 ) ;
2022-06-11 22:10:17 +08:00
EditorGUI . indentLevel + + ;
2022-06-14 12:27:54 +08:00
EditorGUILayout . PropertyField ( spGroupId ) ;
if ( showMax )
{
EditorGUILayout . PropertyField ( spGroupMaxId ) ;
}
else if ( spMeshSharing . intValue = = 1 | | spMeshSharing . intValue = = 4 )
2022-06-11 22:10:17 +08:00
{
EditorGUI . BeginDisabledGroup ( true ) ;
2022-06-14 12:27:54 +08:00
EditorGUILayout . ObjectField ( "Primary" , UIParticleUpdater . GetPrimary ( spGroupId . intValue ) , typeof ( UIParticle ) , false ) ;
2022-06-11 22:10:17 +08:00
EditorGUI . EndDisabledGroup ( ) ;
}
EditorGUI . indentLevel - - ;
EditorGUI . EndDisabledGroup ( ) ;
2022-06-14 12:27:54 +08:00
return showMax ;
2022-06-11 22:10:17 +08:00
}
2022-06-09 01:05:30 +08:00
private static void WindowFunction ( UnityEngine . Object target , SceneView sceneView )
{
try
{
if ( s_SerializedObject . targetObjects . Any ( x = > ! x ) ) return ;
s_SerializedObject . Update ( ) ;
GUILayout . BeginHorizontal ( GUILayout . Width ( 220f ) ) ;
var labelWidth = EditorGUIUtility . labelWidth ;
EditorGUIUtility . labelWidth = 60 ;
_xyzMode = DrawFloatOrVector3Field ( s_SerializedObject . FindProperty ( "m_Scale3D" ) , _xyzMode ) ;
EditorGUIUtility . labelWidth = labelWidth ;
GUILayout . EndHorizontal ( ) ;
s_SerializedObject . ApplyModifiedProperties ( ) ;
}
catch
{
}
}
2022-06-08 11:54:11 +08:00
private void DestroyUIParticle ( UIParticle p , bool ignoreCurrent = false )
2020-09-01 12:50:54 +08:00
{
if ( ! p | | ignoreCurrent & & target = = p ) return ;
var cr = p . canvasRenderer ;
DestroyImmediate ( p ) ;
DestroyImmediate ( cr ) ;
2020-09-01 13:27:06 +08:00
2022-02-16 00:47:45 +08:00
#if UNITY_2021_2_OR_NEWER
var stage = UnityEditor . SceneManagement . PrefabStageUtility . GetCurrentPrefabStage ( ) ;
#elif UNITY_2018_3_OR_NEWER
2020-09-01 15:03:06 +08:00
var stage = UnityEditor . Experimental . SceneManagement . PrefabStageUtility . GetCurrentPrefabStage ( ) ;
2022-02-16 00:47:45 +08:00
# endif
#if UNITY_2018_3_OR_NEWER
2020-09-01 13:27:06 +08:00
if ( stage ! = null & & stage . scene . isLoaded )
{
2021-08-02 13:45:22 +08:00
#if UNITY_2020_1_OR_NEWER
2021-08-02 14:09:21 +08:00
string prefabAssetPath = stage . assetPath ;
2021-08-02 13:45:22 +08:00
# else
2021-08-02 14:09:21 +08:00
string prefabAssetPath = stage . prefabAssetPath ;
2021-08-02 13:45:22 +08:00
# endif
2021-08-02 14:09:21 +08:00
PrefabUtility . SaveAsPrefabAsset ( stage . prefabContentsRoot , prefabAssetPath ) ;
2020-09-01 13:27:06 +08:00
}
2020-09-01 15:03:06 +08:00
# endif
2020-09-01 12:50:54 +08:00
}
2022-06-08 11:54:11 +08:00
private static bool FixButton ( bool show , string text )
2020-09-01 12:50:54 +08:00
{
if ( ! show ) return false ;
using ( new EditorGUILayout . HorizontalScope ( GUILayout . ExpandWidth ( true ) ) )
{
EditorGUILayout . HelpBox ( text , MessageType . Warning , true ) ;
using ( new EditorGUILayout . VerticalScope ( ) )
{
return GUILayout . Button ( s_ContentFix , GUILayout . Width ( 30 ) ) ;
}
}
2020-02-12 20:38:06 +08:00
}
2020-10-04 22:26:53 +08:00
private static bool DrawFloatOrVector3Field ( SerializedProperty sp , bool showXyz )
{
var x = sp . FindPropertyRelative ( "x" ) ;
var y = sp . FindPropertyRelative ( "y" ) ;
var z = sp . FindPropertyRelative ( "z" ) ;
showXyz | = ! Mathf . Approximately ( x . floatValue , y . floatValue ) | |
! Mathf . Approximately ( y . floatValue , z . floatValue ) | |
y . hasMultipleDifferentValues | |
z . hasMultipleDifferentValues ;
EditorGUILayout . BeginHorizontal ( ) ;
if ( showXyz )
{
EditorGUILayout . PropertyField ( sp ) ;
}
else
{
EditorGUI . BeginChangeCheck ( ) ;
EditorGUILayout . PropertyField ( x , s_ContentScale ) ;
if ( EditorGUI . EndChangeCheck ( ) )
2021-02-19 08:57:02 +08:00
{
2022-06-08 11:54:11 +08:00
y . floatValue = z . floatValue = x . floatValue ;
2021-02-19 08:57:02 +08:00
}
2020-10-04 22:26:53 +08:00
}
EditorGUI . BeginChangeCheck ( ) ;
showXyz = GUILayout . Toggle ( showXyz , s_Content3D , EditorStyles . miniButton , GUILayout . Width ( 30 ) ) ;
if ( EditorGUI . EndChangeCheck ( ) & & ! showXyz )
z . floatValue = y . floatValue = x . floatValue ;
EditorGUILayout . EndHorizontal ( ) ;
return showXyz ;
}
2020-02-12 20:38:06 +08:00
}
}