├── Documentation~ ├── Images │ └── preview.gif ├── README.pdf └── README.pdf.meta ├── Editor.meta ├── Editor ├── OverrideEditor.cs ├── OverrideEditor.cs.meta ├── ParticleSystemEditorUtilsReflect.cs ├── ParticleSystemEditorUtilsReflect.cs.meta ├── ParticleSystemGameObjectEditor.cs ├── ParticleSystemGameObjectEditor.cs.meta ├── ParticleSystemPreview.cs ├── ParticleSystemPreview.cs.meta ├── WuHuan.ParticleSystemPreview.Editor.asmdef └── WuHuan.ParticleSystemPreview.Editor.asmdef.meta ├── LICENSE.md ├── LICENSE.md.meta ├── README.md ├── README.md.meta ├── Samples~ ├── demo.unity ├── demo.unity.meta ├── example1.prefab ├── example1.prefab.meta ├── example2.prefab ├── example2.prefab.meta ├── example3.mat ├── example3.mat.meta ├── example3.prefab ├── example3.prefab.meta ├── example4.prefab ├── example4.prefab.meta ├── example5.prefab ├── example5.prefab.meta ├── example6.prefab ├── example6.prefab.meta ├── example7.prefab └── example7.prefab.meta ├── package.json └── package.json.meta /Documentation~/Images/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akof1314/UnityParticleSystemPreview/cd7afaea995578ac005c927ffdee977b14677c2d/Documentation~/Images/preview.gif -------------------------------------------------------------------------------- /Documentation~/README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akof1314/UnityParticleSystemPreview/cd7afaea995578ac005c927ffdee977b14677c2d/Documentation~/README.pdf -------------------------------------------------------------------------------- /Documentation~/README.pdf.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4e7b423605df5a74f81375d224a53534 3 | timeCreated: 1476355176 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cdd01a48737c5914bab4824faa2d6ea0 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/OverrideEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using UnityEditor; 3 | using UnityEngine; 4 | 5 | namespace WuHuan 6 | { 7 | // thanks to Sprite Animation Preview 8 | public abstract class OverrideEditor : Editor 9 | { 10 | readonly MethodInfo methodInfo = typeof(Editor).GetMethod("OnHeaderGUI", 11 | BindingFlags.NonPublic | BindingFlags.Instance); 12 | 13 | private Editor m_BaseEditor; 14 | 15 | protected Editor baseEditor 16 | { 17 | get { return m_BaseEditor ?? (m_BaseEditor = GetBaseEditor()); } 18 | set { m_BaseEditor = value; } 19 | } 20 | 21 | protected abstract Editor GetBaseEditor(); 22 | 23 | protected bool HasBaseEditor() 24 | { 25 | return m_BaseEditor != null; 26 | } 27 | 28 | public override void OnInspectorGUI() 29 | { 30 | baseEditor.OnInspectorGUI(); 31 | } 32 | 33 | public override bool HasPreviewGUI() 34 | { 35 | return baseEditor.HasPreviewGUI(); 36 | } 37 | 38 | public override GUIContent GetPreviewTitle() 39 | { 40 | return baseEditor.GetPreviewTitle(); 41 | } 42 | 43 | public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height) 44 | { 45 | return baseEditor.RenderStaticPreview(assetPath, subAssets, width, height); 46 | } 47 | 48 | public override void OnPreviewGUI(Rect r, GUIStyle background) 49 | { 50 | baseEditor.OnPreviewGUI(r, background); 51 | } 52 | 53 | public override void OnInteractivePreviewGUI(Rect r, GUIStyle background) 54 | { 55 | baseEditor.OnInteractivePreviewGUI(r, background); 56 | } 57 | 58 | public override void OnPreviewSettings() 59 | { 60 | baseEditor.OnPreviewSettings(); 61 | } 62 | 63 | public override string GetInfoString() 64 | { 65 | return baseEditor.GetInfoString(); 66 | } 67 | 68 | public override void ReloadPreviewInstances() 69 | { 70 | baseEditor.ReloadPreviewInstances(); 71 | } 72 | 73 | protected override void OnHeaderGUI() 74 | { 75 | methodInfo.Invoke(baseEditor, new object[0]); 76 | } 77 | 78 | public override bool RequiresConstantRepaint() 79 | { 80 | return baseEditor.RequiresConstantRepaint(); 81 | } 82 | 83 | public override bool UseDefaultMargins() 84 | { 85 | return baseEditor.UseDefaultMargins(); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /Editor/OverrideEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1ec43f7eac1e8b9468470ec9959d640a 3 | timeCreated: 1469665822 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/ParticleSystemEditorUtilsReflect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using System.Reflection; 4 | using UnityEditor; 5 | 6 | namespace WuHuan 7 | { 8 | public class ParticleSystemEditorUtilsReflect 9 | { 10 | private static Type realType; 11 | private static Type realType2; 12 | private static PropertyInfo property_editorResimulation; 13 | private static PropertyInfo property_editorPlaybackTime; 14 | private static Func getFunc_editorPlaybackTime; 15 | private static PropertyInfo property_editorIsScrubbing; 16 | private static PropertyInfo property_lockedParticleSystem; 17 | private static MethodInfo method_StopEffect; 18 | 19 | public static void InitType() 20 | { 21 | if (realType == null) 22 | { 23 | var assembly = Assembly.GetAssembly(typeof(Editor)); 24 | realType = assembly.GetType("UnityEditor.ParticleSystemEditorUtils"); 25 | 26 | #if UNITY_2018_1_OR_NEWER 27 | property_editorResimulation = realType.GetProperty("resimulation", BindingFlags.Static | BindingFlags.NonPublic); 28 | property_editorPlaybackTime = realType.GetProperty("playbackTime", BindingFlags.Static | BindingFlags.NonPublic); 29 | 30 | getFunc_editorPlaybackTime = (Func)Delegate.CreateDelegate(typeof(Func), property_editorPlaybackTime.GetGetMethod(true)); 31 | property_editorIsScrubbing = realType.GetProperty("playbackIsScrubbing", BindingFlags.Static | BindingFlags.NonPublic); 32 | property_lockedParticleSystem = realType.GetProperty("lockedParticleSystem", BindingFlags.Static | BindingFlags.NonPublic); 33 | 34 | realType2 = assembly.GetType("UnityEditor.ParticleSystemEffectUtils"); 35 | method_StopEffect = realType2.GetMethod("StopEffect", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { }, new ParameterModifier[] { }); 36 | #else 37 | property_editorResimulation = realType.GetProperty("editorResimulation", BindingFlags.Static | BindingFlags.NonPublic); 38 | property_editorPlaybackTime = realType.GetProperty("editorPlaybackTime", BindingFlags.Static | BindingFlags.NonPublic); 39 | 40 | getFunc_editorPlaybackTime = (Func)Delegate.CreateDelegate(typeof(Func), property_editorPlaybackTime.GetGetMethod(true)); 41 | property_editorIsScrubbing = realType.GetProperty("editorIsScrubbing", BindingFlags.Static | BindingFlags.NonPublic); 42 | property_lockedParticleSystem = realType.GetProperty("lockedParticleSystem", BindingFlags.Static | BindingFlags.NonPublic); 43 | method_StopEffect = realType.GetMethod("StopEffect", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { }, new ParameterModifier[] { }); 44 | #endif 45 | } 46 | } 47 | 48 | public static bool editorResimulation 49 | { 50 | set 51 | { 52 | InitType(); 53 | property_editorResimulation.SetValue(null, value, null); 54 | } 55 | } 56 | 57 | public static float editorPlaybackTime 58 | { 59 | get 60 | { 61 | InitType(); 62 | return getFunc_editorPlaybackTime(); 63 | } 64 | set 65 | { 66 | InitType(); 67 | property_editorPlaybackTime.SetValue(null, value, null); 68 | } 69 | } 70 | 71 | public static bool editorIsScrubbing 72 | { 73 | set 74 | { 75 | InitType(); 76 | property_editorIsScrubbing.SetValue(null, value, null); 77 | } 78 | } 79 | 80 | public static ParticleSystem lockedParticleSystem 81 | { 82 | get 83 | { 84 | InitType(); 85 | return (ParticleSystem)property_lockedParticleSystem.GetValue(null, null); 86 | } 87 | set 88 | { 89 | InitType(); 90 | property_lockedParticleSystem.SetValue(null, value, null); 91 | } 92 | } 93 | 94 | public static void StopEffect() 95 | { 96 | InitType(); 97 | method_StopEffect.Invoke(null, null); 98 | } 99 | 100 | public static ParticleSystem GetRoot(ParticleSystem ps) 101 | { 102 | if (ps == null) 103 | { 104 | return null; 105 | } 106 | Transform transform = ps.transform; 107 | while (transform.parent && transform.parent.gameObject.GetComponent() != null) 108 | { 109 | transform = transform.parent; 110 | } 111 | return transform.gameObject.GetComponent(); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /Editor/ParticleSystemEditorUtilsReflect.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 850c59d07692c844ebefae5434fe510e 3 | timeCreated: 1469584368 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/ParticleSystemGameObjectEditor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using UnityEngine; 4 | using UnityEditor; 5 | 6 | namespace WuHuan 7 | { 8 | [CustomEditor(typeof(GameObject)), CanEditMultipleObjects] 9 | public class ParticleSystemGameObjectEditor : OverrideEditor 10 | { 11 | private static Type s_GameObjectType; 12 | private static MethodInfo s_OnSceneDragMethodInfo; 13 | private static FieldInfo s_PreviewCacheFieldInfo; 14 | private static PropertyInfo s_TargetIndexPropertyInfo; 15 | 16 | private class Styles 17 | { 18 | public GUIContent ps = new GUIContent("PS", "Show particle system preview"); 19 | #if UNITY_2019_3_OR_NEWER 20 | public GUIStyle preButton = EditorStyles.toolbarButton; 21 | #else 22 | public GUIStyle preButton = "preButton"; 23 | #endif 24 | } 25 | 26 | private bool m_ShowParticlePreview; 27 | 28 | private int m_DefaultHasPreview; 29 | 30 | private ParticleSystemPreview m_Preview; 31 | 32 | private static Styles s_Styles; 33 | 34 | private ParticleSystemPreview preview 35 | { 36 | get 37 | { 38 | if (m_Preview == null) 39 | { 40 | m_Preview = new ParticleSystemPreview(); 41 | m_Preview.SetEditor(this); 42 | m_Preview.Initialize(targets); 43 | } 44 | 45 | return m_Preview; 46 | } 47 | } 48 | 49 | protected override Editor GetBaseEditor() 50 | { 51 | if (s_GameObjectType == null) 52 | { 53 | var assembly = typeof(Editor).Assembly; 54 | s_GameObjectType = assembly.GetType("UnityEditor.GameObjectInspector"); 55 | s_OnSceneDragMethodInfo = s_GameObjectType.GetMethod("OnSceneDrag", 56 | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 57 | s_PreviewCacheFieldInfo = s_GameObjectType.GetField("m_PreviewCache", 58 | BindingFlags.Instance | BindingFlags.NonPublic); 59 | Type realTypeEditor = assembly.GetType("UnityEditor.Editor"); 60 | s_TargetIndexPropertyInfo = realTypeEditor.GetProperty("referenceTargetIndex", 61 | BindingFlags.NonPublic | BindingFlags.Instance); 62 | } 63 | 64 | Editor editor = null; 65 | var baseType = s_GameObjectType; 66 | CreateCachedEditor(targets, baseType, ref editor); 67 | return editor; 68 | } 69 | 70 | void OnEnable() 71 | { 72 | m_ShowParticlePreview = true; 73 | } 74 | 75 | void OnDisable() 76 | { 77 | if (m_Preview != null) 78 | { 79 | m_Preview.OnDestroy(); 80 | #if UNITY_2021_1_OR_NEWER 81 | m_Preview.Cleanup(); 82 | #endif 83 | m_Preview = null; 84 | } 85 | 86 | if (HasBaseEditor() && IsPreviewCacheNotNull) 87 | { 88 | DestroyImmediate(baseEditor); 89 | baseEditor = null; 90 | } 91 | } 92 | 93 | private bool HasParticleSystemPreview() 94 | { 95 | return preview.HasPreviewGUI(); 96 | } 97 | 98 | private bool HasBasePreview() 99 | { 100 | if (m_DefaultHasPreview == 0) 101 | { 102 | m_DefaultHasPreview = baseEditor.HasPreviewGUI() ? 1 : -1; 103 | } 104 | 105 | return m_DefaultHasPreview == 1; 106 | } 107 | 108 | private bool IsShowParticleSystemPreview() 109 | { 110 | return HasParticleSystemPreview() && m_ShowParticlePreview; 111 | } 112 | 113 | public override bool HasPreviewGUI() 114 | { 115 | return HasParticleSystemPreview() || HasBasePreview(); 116 | } 117 | 118 | public override GUIContent GetPreviewTitle() 119 | { 120 | return IsShowParticleSystemPreview() ? preview.GetPreviewTitle() : baseEditor.GetPreviewTitle(); 121 | } 122 | 123 | public override void OnPreviewGUI(Rect r, GUIStyle background) 124 | { 125 | if (IsShowParticleSystemPreview()) 126 | { 127 | preview.OnPreviewGUI(r, background); 128 | } 129 | else 130 | { 131 | SetEditorTargetIndex(); 132 | baseEditor.OnPreviewGUI(r, background); 133 | } 134 | } 135 | 136 | public override void OnInteractivePreviewGUI(Rect r, GUIStyle background) 137 | { 138 | if (IsShowParticleSystemPreview()) 139 | { 140 | preview.OnInteractivePreviewGUI(r, background); 141 | } 142 | else 143 | { 144 | SetEditorTargetIndex(); 145 | baseEditor.OnInteractivePreviewGUI(r, background); 146 | } 147 | } 148 | 149 | public override void OnPreviewSettings() 150 | { 151 | if (s_Styles == null) 152 | { 153 | s_Styles = new Styles(); 154 | } 155 | 156 | if (HasBasePreview() && HasParticleSystemPreview()) 157 | { 158 | m_ShowParticlePreview = GUILayout.Toggle(m_ShowParticlePreview, s_Styles.ps, s_Styles.preButton); 159 | } 160 | 161 | if (IsShowParticleSystemPreview()) 162 | { 163 | preview.OnPreviewSettings(); 164 | } 165 | else 166 | { 167 | baseEditor.OnPreviewSettings(); 168 | } 169 | } 170 | 171 | public override string GetInfoString() 172 | { 173 | return IsShowParticleSystemPreview() ? preview.GetInfoString() : baseEditor.GetInfoString(); 174 | } 175 | 176 | public override void ReloadPreviewInstances() 177 | { 178 | if (IsShowParticleSystemPreview()) 179 | { 180 | preview.ReloadPreviewInstances(); 181 | } 182 | else 183 | { 184 | baseEditor.ReloadPreviewInstances(); 185 | } 186 | } 187 | 188 | /// 189 | /// 需要调用 GameObjectInspector 的场景拖曳,否则无法拖动物体到 Scene 视图 190 | /// 191 | #if UNITY_2020_2_OR_NEWER 192 | public void OnSceneDrag(SceneView sceneView, int index) 193 | { 194 | if (s_OnSceneDragMethodInfo != null) 195 | { 196 | s_OnSceneDragMethodInfo.Invoke(baseEditor, new object[] { sceneView, index }); 197 | } 198 | } 199 | #else 200 | public void OnSceneDrag(SceneView sceneView) 201 | { 202 | if (s_OnSceneDragMethodInfo != null) 203 | { 204 | s_OnSceneDragMethodInfo.Invoke(baseEditor, new object[] { sceneView }); 205 | } 206 | } 207 | #endif 208 | 209 | private bool IsPreviewCacheNotNull 210 | { 211 | get 212 | { 213 | if (null != s_PreviewCacheFieldInfo) 214 | { 215 | var value = s_PreviewCacheFieldInfo.GetValue(baseEditor); 216 | return null != value; 217 | } 218 | return false; 219 | } 220 | } 221 | 222 | private void SetEditorTargetIndex() 223 | { 224 | var newVal = s_TargetIndexPropertyInfo.GetValue(this, null); 225 | s_TargetIndexPropertyInfo.SetValue(baseEditor, newVal, null); 226 | } 227 | } 228 | } -------------------------------------------------------------------------------- /Editor/ParticleSystemGameObjectEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aa2b0a85cb4b79a4ba36d6bfda87b4ae 3 | timeCreated: 1469665822 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/ParticleSystemPreview.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using UnityEditor; 4 | using UnityEditorInternal; 5 | 6 | namespace WuHuan 7 | { 8 | // Disable it 9 | //[CustomPreview(typeof(GameObject))] 10 | public class ParticleSystemPreview : ObjectPreview 11 | { 12 | private class Styles 13 | { 14 | public GUIContent speedScale = IconContent("SpeedScale", "Changes particle preview speed"); 15 | public GUIContent pivot = IconContent("AvatarPivot", "Displays avatar's pivot and mass center"); 16 | public GUIContent floor = IconContent("CheckerFloor", "Displays floor plane"); 17 | 18 | public GUIContent[] play = new GUIContent[2] 19 | { 20 | IconContent("preAudioPlayOff", "Play"), 21 | IconContent("preAudioPlayOn", "Stop") 22 | }; 23 | 24 | public GUIContent[] autoPlayIcons = new GUIContent[2] 25 | { 26 | IconContent("preAudioAutoPlayOff", "Turn Auto Play on/off"), 27 | IconContent("preAudioAutoPlayOff", "Turn Auto Play on/off") 28 | }; 29 | 30 | public GUIContent lockParticleSystem = IconContent("IN LockButton", "Lock the current particle"); 31 | public GUIContent reload = new GUIContent("Reload", "Reload particle preview"); 32 | #if UNITY_2019_3_OR_NEWER 33 | public GUIStyle preButton = EditorStyles.toolbarButton; 34 | #else 35 | public GUIStyle preButton = "preButton"; 36 | #endif 37 | public GUIStyle preSlider = "preSlider"; 38 | public GUIStyle preSliderThumb = "preSliderThumb"; 39 | public GUIStyle preLabel = "preLabel"; 40 | } 41 | 42 | /// 43 | /// 视图工具 44 | /// 45 | protected enum ViewTool 46 | { 47 | None, 48 | 49 | /// 50 | /// 平移 51 | /// 52 | Pan, 53 | 54 | /// 55 | /// 缩放 56 | /// 57 | Zoom, 58 | 59 | /// 60 | /// 旋转 61 | /// 62 | Orbit 63 | } 64 | 65 | private PreviewRenderUtility m_PreviewUtility; 66 | 67 | // 预览实例对象 68 | private GameObject m_PreviewInstance; 69 | 70 | // 原点圆形标识 71 | private GameObject m_ReferenceInstance; 72 | 73 | // 方向箭头标识 74 | private GameObject m_DirectionInstance; 75 | 76 | // 轴三箭头标识 77 | private GameObject m_PivotInstance; 78 | 79 | // 根三箭头标识 80 | private GameObject m_RootInstance; 81 | private Vector2 m_PreviewDir = new Vector2(120f, -20f); 82 | private Mesh m_FloorPlane; 83 | private Texture2D m_FloorTexture; 84 | private Material m_FloorMaterial; 85 | private float m_AvatarScale = 1f; 86 | private float m_ZoomFactor = 1f; 87 | private Vector3 m_PivotPositionOffset = Vector3.zero; 88 | private float m_BoundingVolumeScale; 89 | protected ViewTool m_ViewTool; 90 | private bool m_ShowReference; 91 | private bool m_ShowFloor; 92 | private bool m_AutoPlay; 93 | private bool m_Loaded; 94 | private int m_PreviewHint = "Preview".GetHashCode(); 95 | private int m_PreviewSceneHint = "PreviewSene".GetHashCode(); 96 | 97 | private bool m_Playing; 98 | private float m_RunningTime; 99 | private double m_PreviousTime; 100 | private float m_PlaybackSpeed = 1f; 101 | private bool m_IsLockParticleSystem = true; 102 | private bool m_HasPreview; 103 | private Editor m_CacheEditor; 104 | private const float kDuration = 99f; 105 | private static int PreviewCullingLayer = 31; 106 | private static Styles s_Styles; 107 | private static bool s_IsStopParticlePlay; 108 | 109 | public Vector3 bodyPosition 110 | { 111 | get { return m_PreviewInstance.transform.position; } 112 | } 113 | 114 | protected ViewTool viewTool 115 | { 116 | get 117 | { 118 | Event current = Event.current; 119 | if (m_ViewTool == ViewTool.None) 120 | { 121 | bool flag = current.control && Application.platform == RuntimePlatform.OSXEditor; 122 | bool actionKey = EditorGUI.actionKey; 123 | bool flag2 = !actionKey && !flag && !current.alt; 124 | if ((current.button <= 0 && flag2) || (current.button <= 0 && actionKey) || current.button == 2) 125 | { 126 | m_ViewTool = ViewTool.Pan; 127 | } 128 | else 129 | { 130 | if ((current.button <= 0 && flag) || (current.button == 1 && current.alt)) 131 | { 132 | m_ViewTool = ViewTool.Zoom; 133 | } 134 | else 135 | { 136 | if ((current.button <= 0 && current.alt) || current.button == 1) 137 | { 138 | m_ViewTool = ViewTool.Orbit; 139 | } 140 | } 141 | } 142 | } 143 | 144 | return m_ViewTool; 145 | } 146 | } 147 | 148 | protected MouseCursor currentCursor 149 | { 150 | get 151 | { 152 | switch (m_ViewTool) 153 | { 154 | case ViewTool.Pan: 155 | return MouseCursor.Pan; 156 | case ViewTool.Zoom: 157 | return MouseCursor.Zoom; 158 | case ViewTool.Orbit: 159 | return MouseCursor.Orbit; 160 | default: 161 | return MouseCursor.Arrow; 162 | } 163 | } 164 | } 165 | 166 | public void SetEditor(Editor editor) 167 | { 168 | m_CacheEditor = editor; 169 | } 170 | 171 | public override void Initialize(UnityEngine.Object[] targets) 172 | { 173 | base.Initialize(targets); 174 | 175 | if (m_CacheEditor == null) 176 | { 177 | var editors = ActiveEditorTracker.sharedTracker.activeEditors; 178 | foreach (var editor in editors) 179 | { 180 | if (editor.target == target) 181 | { 182 | m_CacheEditor = editor; 183 | break; 184 | } 185 | } 186 | 187 | if (m_CacheEditor == null && editors.Length > 0) 188 | { 189 | m_CacheEditor = editors[0]; 190 | } 191 | } 192 | 193 | m_HasPreview = EditorUtility.IsPersistent(target) && HasStaticPreview(); 194 | if (m_Targets.Length != 1) 195 | { 196 | m_HasPreview = false; 197 | } 198 | } 199 | 200 | public override bool HasPreviewGUI() 201 | { 202 | return m_HasPreview; 203 | } 204 | 205 | public override void OnPreviewGUI(Rect r, GUIStyle background) 206 | { 207 | InitPreview(); 208 | if (m_PreviewUtility == null) 209 | { 210 | return; 211 | } 212 | 213 | Rect rect2 = r; 214 | int controlID = GUIUtility.GetControlID(m_PreviewHint, FocusType.Passive, rect2); 215 | Event current = Event.current; 216 | EventType typeForControl = current.GetTypeForControl(controlID); 217 | 218 | if (typeForControl == EventType.Repaint) 219 | { 220 | m_PreviewUtility.BeginPreview(rect2, background); 221 | DoRenderPreview(); 222 | m_PreviewUtility.EndAndDrawPreview(rect2); 223 | } 224 | 225 | int controlID2 = GUIUtility.GetControlID(m_PreviewSceneHint, FocusType.Passive); 226 | typeForControl = current.GetTypeForControl(controlID2); 227 | HandleViewTool(current, typeForControl, controlID2, rect2); 228 | DoAvatarPreviewFrame(current, typeForControl, rect2); 229 | EditorGUI.DropShadowLabel(new Rect(r.x, r.yMax - 20f, r.width, 20f), 230 | (r.width > 140f ? "Playback Time:" : string.Empty) + String.Format("{0:F}", m_RunningTime)); 231 | 232 | if (current.type == EventType.Repaint) 233 | { 234 | EditorGUIUtility.AddCursorRect(rect2, currentCursor); 235 | } 236 | } 237 | 238 | public override GUIContent GetPreviewTitle() 239 | { 240 | GUIContent content = base.GetPreviewTitle(); 241 | content.text = "Particle Preview"; 242 | return content; 243 | } 244 | 245 | public override void OnPreviewSettings() 246 | { 247 | if (s_Styles == null) 248 | { 249 | s_Styles = new Styles(); 250 | } 251 | 252 | InitPreview(); 253 | if (m_PreviewUtility == null) 254 | { 255 | if (GUILayout.Button(s_Styles.reload, s_Styles.preButton)) 256 | { 257 | m_Loaded = false; 258 | } 259 | 260 | return; 261 | } 262 | 263 | EditorGUI.BeginChangeCheck(); 264 | m_ShowReference = GUILayout.Toggle(m_ShowReference, s_Styles.pivot, s_Styles.preButton); 265 | if (EditorGUI.EndChangeCheck()) 266 | { 267 | EditorPrefs.SetBool("AvatarpreviewShowReference", m_ShowReference); 268 | } 269 | 270 | EditorGUI.BeginChangeCheck(); 271 | m_ShowFloor = GUILayout.Toggle(m_ShowFloor, s_Styles.floor, s_Styles.preButton); 272 | if (EditorGUI.EndChangeCheck()) 273 | { 274 | EditorPrefs.SetBool("AvatarpreviewShowFloor", m_ShowFloor); 275 | } 276 | 277 | //EditorGUI.BeginChangeCheck(); 278 | //m_IsLockParticleSystem = GUILayout.Toggle(m_IsLockParticleSystem, s_Styles.lockParticleSystem, s_Styles.preButton); 279 | //if (EditorGUI.EndChangeCheck()) 280 | //{ 281 | // SetSimulateMode(); 282 | //} 283 | 284 | bool flag = CycleButton(!m_Playing ? 0 : 1, s_Styles.play, s_Styles.preButton) != 0; 285 | if (flag != m_Playing) 286 | { 287 | if (flag) 288 | { 289 | SimulateEnable(); 290 | } 291 | else 292 | { 293 | SimulateDisable(); 294 | } 295 | } 296 | EditorGUI.BeginChangeCheck(); 297 | m_AutoPlay = CycleButton(m_AutoPlay ? 1 : 0, s_Styles.autoPlayIcons, s_Styles.preButton) == 1; 298 | if (EditorGUI.EndChangeCheck()) 299 | { 300 | EditorPrefs.SetBool("AvatarpreviewAutoPlayAudio", m_AutoPlay); 301 | } 302 | 303 | GUILayout.Box(s_Styles.speedScale, s_Styles.preLabel); 304 | EditorGUI.BeginChangeCheck(); 305 | m_PlaybackSpeed = PreviewSlider(m_PlaybackSpeed, 0.03f); 306 | if (EditorGUI.EndChangeCheck() && m_PreviewInstance) 307 | { 308 | ParticleSystem[] particleSystems = m_PreviewInstance.GetComponentsInChildren(true); 309 | foreach (var particleSystem in particleSystems) 310 | { 311 | #if UNITY_5_5_OR_NEWER 312 | ParticleSystem.MainModule main = particleSystem.main; 313 | main.simulationSpeed = m_PlaybackSpeed; 314 | #else 315 | particleSystem.playbackSpeed = m_PlaybackSpeed; 316 | #endif 317 | } 318 | } 319 | 320 | GUILayout.Label(m_PlaybackSpeed.ToString("f2"), s_Styles.preLabel); 321 | } 322 | 323 | public override void ReloadPreviewInstances() 324 | { 325 | Debug.Log("reload"); 326 | if (m_PreviewUtility == null) 327 | { 328 | return; 329 | } 330 | 331 | CreatePreviewInstances(); 332 | } 333 | 334 | public override string GetInfoString() 335 | { 336 | return " "; 337 | } 338 | 339 | private void InitPreview() 340 | { 341 | if (m_Loaded) 342 | { 343 | return; 344 | } 345 | 346 | m_Loaded = true; 347 | 348 | if (m_PreviewUtility == null) 349 | { 350 | m_PreviewUtility = new PreviewRenderUtility(true); 351 | #if UNITY_2017_1_OR_NEWER 352 | m_PreviewUtility.cameraFieldOfView = 30f; 353 | #else 354 | m_PreviewUtility.m_CameraFieldOfView = 30f; 355 | #endif 356 | GetPreviewCamera().cullingMask = 1 << PreviewCullingLayer; 357 | #if UNITY_5_6_OR_NEWER 358 | GetPreviewCamera().allowHDR = false; 359 | GetPreviewCamera().allowMSAA = false; 360 | #endif 361 | CreatePreviewInstances(); 362 | } 363 | 364 | if (m_FloorPlane == null) 365 | { 366 | m_FloorPlane = (Resources.GetBuiltinResource(typeof(Mesh), "New-Plane.fbx") as Mesh); 367 | } 368 | 369 | if (m_FloorTexture == null) 370 | { 371 | m_FloorTexture = (Texture2D)EditorGUIUtility.Load("Avatar/Textures/AvatarFloor.png"); 372 | } 373 | 374 | if (m_FloorMaterial == null) 375 | { 376 | Shader shader = EditorGUIUtility.LoadRequired("Previews/PreviewPlaneWithShadow.shader") as Shader; 377 | m_FloorMaterial = new Material(shader); 378 | m_FloorMaterial.mainTexture = m_FloorTexture; 379 | m_FloorMaterial.mainTextureScale = Vector2.one * 5f * 4f; 380 | m_FloorMaterial.SetVector("_Alphas", new Vector4(0.5f, 0.3f, 0f, 0f)); 381 | m_FloorMaterial.hideFlags = HideFlags.HideAndDontSave; 382 | } 383 | 384 | if (m_ReferenceInstance == null) 385 | { 386 | GameObject original = (GameObject)EditorGUIUtility.Load("Avatar/dial_flat.prefab"); 387 | m_ReferenceInstance = 388 | (GameObject)UnityEngine.Object.Instantiate(original, Vector3.zero, Quaternion.identity); 389 | InitInstantiatedPreviewRecursive(m_ReferenceInstance); 390 | AddSingleGO(m_ReferenceInstance); 391 | } 392 | 393 | if (m_DirectionInstance == null) 394 | { 395 | GameObject original2 = (GameObject)EditorGUIUtility.Load("Avatar/arrow.fbx"); 396 | m_DirectionInstance = 397 | (GameObject)UnityEngine.Object.Instantiate(original2, Vector3.zero, Quaternion.identity); 398 | InitInstantiatedPreviewRecursive(m_DirectionInstance); 399 | AddSingleGO(m_DirectionInstance); 400 | } 401 | 402 | if (m_PivotInstance == null) 403 | { 404 | GameObject original3 = (GameObject)EditorGUIUtility.Load("Avatar/root.fbx"); 405 | m_PivotInstance = 406 | (GameObject)UnityEngine.Object.Instantiate(original3, Vector3.zero, Quaternion.identity); 407 | InitInstantiatedPreviewRecursive(m_PivotInstance); 408 | AddSingleGO(m_PivotInstance); 409 | } 410 | 411 | if (m_RootInstance == null) 412 | { 413 | GameObject original4 = (GameObject)EditorGUIUtility.Load("Avatar/root.fbx"); 414 | m_RootInstance = 415 | (GameObject)UnityEngine.Object.Instantiate(original4, Vector3.zero, Quaternion.identity); 416 | InitInstantiatedPreviewRecursive(m_RootInstance); 417 | AddSingleGO(m_RootInstance); 418 | } 419 | 420 | m_ShowReference = EditorPrefs.GetBool("AvatarpreviewShowReference", true); 421 | m_ShowFloor = EditorPrefs.GetBool("AvatarpreviewShowFloor", true); 422 | m_AutoPlay = EditorPrefs.GetBool("AvatarpreviewAutoPlayAudio", false); 423 | SetPreviewCharacterEnabled(false, false); 424 | 425 | if (m_AutoPlay) 426 | { 427 | SimulateEnable(); 428 | } 429 | } 430 | 431 | private bool HasStaticPreview() 432 | { 433 | if (target == null) 434 | { 435 | return false; 436 | } 437 | 438 | GameObject gameObject = target as GameObject; 439 | return gameObject.GetComponentInChildren(true); 440 | } 441 | 442 | private void DoRenderPreview() 443 | { 444 | Vector3 bodyPosition = this.bodyPosition; 445 | Quaternion quaternion = Quaternion.identity; 446 | Vector3 vector = Vector3.zero; 447 | Quaternion quaternion2 = Quaternion.identity; 448 | Vector3 pivotPos = Vector3.zero; 449 | 450 | bool oldFog = SetupPreviewLightingAndFx(); 451 | Vector3 forward = quaternion2 * Vector3.forward; 452 | forward[1] = 0f; 453 | Quaternion directionRot = Quaternion.LookRotation(forward); 454 | Vector3 directionPos = vector; 455 | Quaternion pivotRot = quaternion; 456 | PositionPreviewObjects(pivotRot, pivotPos, quaternion2, bodyPosition, directionRot, quaternion, vector, 457 | directionPos, m_AvatarScale); 458 | 459 | GetPreviewCamera().nearClipPlane = 0.5f * m_ZoomFactor; 460 | GetPreviewCamera().farClipPlane = 100f * m_AvatarScale; 461 | Quaternion rotation = Quaternion.Euler(-m_PreviewDir.y, -m_PreviewDir.x, 0f); 462 | Vector3 position2 = rotation * (Vector3.forward * -5.5f * m_ZoomFactor) + bodyPosition + 463 | m_PivotPositionOffset; 464 | GetPreviewCamera().transform.position = position2; 465 | GetPreviewCamera().transform.rotation = rotation; 466 | 467 | SetPreviewCharacterEnabled(true, m_ShowReference); 468 | m_PreviewUtility.Render(true); 469 | SetPreviewCharacterEnabled(false, false); 470 | 471 | if (m_ShowFloor) 472 | { 473 | Quaternion identity = Quaternion.identity; 474 | Vector3 position = new Vector3(0f, 0f, 0f); 475 | position = m_ReferenceInstance.transform.position; 476 | Material floorMaterial = m_FloorMaterial; 477 | Matrix4x4 matrix2 = Matrix4x4.TRS(position, identity, Vector3.one * 5f * m_AvatarScale); 478 | floorMaterial.mainTextureOffset = -new Vector2(position.x, position.z) * 5f * 0.08f * (1f / m_AvatarScale); 479 | floorMaterial.SetVector("_Alphas", new Vector4(0.5f * 1f, 0.3f * 1f, 0f, 0f)); 480 | floorMaterial.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Background; 481 | Graphics.DrawMesh(m_FloorPlane, matrix2, floorMaterial, PreviewCullingLayer, GetPreviewCamera(), 0); 482 | } 483 | 484 | var clearMode = GetPreviewCamera().clearFlags; 485 | GetPreviewCamera().clearFlags = CameraClearFlags.Nothing; 486 | m_PreviewUtility.Render(false); 487 | GetPreviewCamera().clearFlags = clearMode; 488 | TeardownPreviewLightingAndFx(oldFog); 489 | } 490 | 491 | private void CreatePreviewInstances() 492 | { 493 | DestroyPreviewInstances(); 494 | GameObject gameObject = UnityEngine.Object.Instantiate(target) as GameObject; 495 | InitInstantiatedPreviewRecursive(gameObject); 496 | AddSingleGO(gameObject); 497 | Animator component = gameObject.GetComponent(); 498 | if (component) 499 | { 500 | component.enabled = false; 501 | component.cullingMode = AnimatorCullingMode.AlwaysAnimate; 502 | component.logWarnings = false; 503 | component.fireEvents = false; 504 | } 505 | 506 | // 解决同级多个粒子系统 507 | if (gameObject.GetComponent() == null) 508 | { 509 | int particleCount = 0; 510 | var tf = gameObject.transform; 511 | for (int i = 0; i < tf.childCount; i++) 512 | { 513 | if (tf.GetChild(i).GetComponentInChildren(true)) 514 | { 515 | particleCount++; 516 | } 517 | 518 | if (particleCount > 1) 519 | { 520 | var ps = gameObject.AddComponent(); 521 | var em = ps.emission; 522 | em.enabled = false; 523 | break; 524 | } 525 | } 526 | } 527 | 528 | m_PreviewInstance = gameObject; 529 | //Debug.Log("OnCreate"); 530 | 531 | Bounds bounds = new Bounds(m_PreviewInstance.transform.position, Vector3.zero); 532 | GetRenderableBoundsRecurse(ref bounds, m_PreviewInstance); 533 | if (bounds.size == Vector3.zero) 534 | { 535 | bounds.size = Vector3.one; 536 | } 537 | 538 | m_BoundingVolumeScale = Mathf.Max(bounds.size.x, Mathf.Max(bounds.size.y, bounds.size.z)); 539 | m_AvatarScale = (m_ZoomFactor = m_BoundingVolumeScale / 2f); 540 | } 541 | 542 | private void DestroyPreviewInstances() 543 | { 544 | if (m_PreviewInstance == null) 545 | { 546 | return; 547 | } 548 | 549 | UnityEngine.Object.DestroyImmediate(m_PreviewInstance); 550 | UnityEngine.Object.DestroyImmediate(m_FloorMaterial); 551 | UnityEngine.Object.DestroyImmediate(m_ReferenceInstance); 552 | UnityEngine.Object.DestroyImmediate(m_RootInstance); 553 | UnityEngine.Object.DestroyImmediate(m_PivotInstance); 554 | UnityEngine.Object.DestroyImmediate(m_DirectionInstance); 555 | } 556 | 557 | private void AddSingleGO(GameObject go) 558 | { 559 | #if UNITY_2017_1_OR_NEWER 560 | m_PreviewUtility.AddSingleGO(go); 561 | #endif 562 | } 563 | 564 | private Camera GetPreviewCamera() 565 | { 566 | #if UNITY_2017_1_OR_NEWER 567 | return m_PreviewUtility.camera; 568 | #else 569 | return m_PreviewUtility.m_Camera; 570 | #endif 571 | } 572 | 573 | private bool SetupPreviewLightingAndFx() 574 | { 575 | #if UNITY_2017_1_OR_NEWER 576 | Light[] lights = m_PreviewUtility.lights; 577 | #else 578 | Light[] lights = m_PreviewUtility.m_Light; 579 | #endif 580 | lights[0].intensity = 1.4f; 581 | lights[0].transform.rotation = Quaternion.Euler(40f, 40f, 0f); 582 | lights[1].intensity = 1.4f; 583 | Color ambient = new Color(0.1f, 0.1f, 0.1f, 0f); 584 | InternalEditorUtility.SetCustomLighting(lights, ambient); 585 | bool fog = RenderSettings.fog; 586 | Unsupported.SetRenderSettingsUseFogNoDirty(false); 587 | return fog; 588 | } 589 | 590 | private static void TeardownPreviewLightingAndFx(bool oldFog) 591 | { 592 | Unsupported.SetRenderSettingsUseFogNoDirty(oldFog); 593 | InternalEditorUtility.RemoveCustomLighting(); 594 | } 595 | 596 | private void SetPreviewCharacterEnabled(bool enabled, bool showReference) 597 | { 598 | if (m_PreviewInstance != null) 599 | { 600 | SetEnabledRecursive(m_PreviewInstance, enabled); 601 | } 602 | 603 | SetEnabledRecursive(m_ReferenceInstance, showReference && enabled); 604 | SetEnabledRecursive(m_DirectionInstance, showReference && enabled); 605 | SetEnabledRecursive(m_PivotInstance, showReference && enabled); 606 | SetEnabledRecursive(m_RootInstance, showReference && enabled); 607 | } 608 | 609 | private void PositionPreviewObjects(Quaternion pivotRot, Vector3 pivotPos, Quaternion bodyRot, Vector3 bodyPos, 610 | Quaternion directionRot, Quaternion rootRot, Vector3 rootPos, Vector3 directionPos, float scale) 611 | { 612 | m_ReferenceInstance.transform.position = rootPos; 613 | m_ReferenceInstance.transform.rotation = rootRot; 614 | m_ReferenceInstance.transform.localScale = Vector3.one * scale * 1.25f; 615 | m_DirectionInstance.transform.position = directionPos; 616 | m_DirectionInstance.transform.rotation = directionRot; 617 | m_DirectionInstance.transform.localScale = Vector3.one * scale * 2f; 618 | m_PivotInstance.transform.position = pivotPos; 619 | m_PivotInstance.transform.rotation = pivotRot; 620 | m_PivotInstance.transform.localScale = Vector3.one * scale * 0.1f; 621 | m_RootInstance.transform.position = bodyPos; 622 | m_RootInstance.transform.rotation = bodyRot; 623 | m_RootInstance.transform.localScale = Vector3.one * scale * 0.25f; 624 | } 625 | 626 | /// 627 | /// 最后的销毁方法 628 | /// 不能被Unity自动调用,目前只能在点下一个对象时,调用销毁 629 | /// 630 | public void OnDestroy() 631 | { 632 | ClearLockedParticle(); 633 | SimulateDisable(); 634 | DestroyPreviewInstances(); 635 | if (m_PreviewUtility != null) 636 | { 637 | //Debug.Log("OnDestroy"); 638 | m_PreviewUtility.Cleanup(); 639 | m_PreviewUtility = null; 640 | } 641 | } 642 | 643 | /// 644 | /// 模拟-开启 645 | /// 646 | private void SimulateEnable() 647 | { 648 | SetSimulateMode(); 649 | if (m_IsLockParticleSystem) 650 | { 651 | ParticleSystem particleSystem = m_PreviewInstance.GetComponentInChildren(true); 652 | if (particleSystem) 653 | { 654 | particleSystem.Play(); 655 | s_IsStopParticlePlay = false; 656 | ParticleSystemEditorUtilsReflect.editorIsScrubbing = false; 657 | } 658 | } 659 | 660 | m_PreviousTime = EditorApplication.timeSinceStartup; 661 | 662 | EditorApplication.update -= InspectorUpdate; 663 | EditorApplication.update += InspectorUpdate; 664 | m_RunningTime = 0f; 665 | m_Playing = true; 666 | } 667 | 668 | /// 669 | /// 模拟-停止 670 | /// 671 | private void SimulateDisable() 672 | { 673 | if (m_IsLockParticleSystem) 674 | { 675 | ParticleSystemEditorUtilsReflect.editorIsScrubbing = false; 676 | ParticleSystemEditorUtilsReflect.editorPlaybackTime = 0f; 677 | ParticleSystemEditorUtilsReflect.StopEffect(); 678 | s_IsStopParticlePlay = true; 679 | } 680 | 681 | EditorApplication.update -= InspectorUpdate; 682 | m_RunningTime = 0f; 683 | m_Playing = false; 684 | } 685 | 686 | /// 687 | /// 模拟-更新方法 688 | /// 锁定粒子方式的话,只需要刷新编辑器即可 689 | /// 690 | private void SimulateUpdate() 691 | { 692 | if (m_IsLockParticleSystem) 693 | { 694 | if (s_IsStopParticlePlay) 695 | { 696 | // ObjectSelector 的时候,先创建新的再销毁旧的,导致新的停止播放,这里尝试一次 697 | s_IsStopParticlePlay = false; 698 | SimulateEnable(); 699 | } 700 | Repaint(); 701 | return; 702 | } 703 | 704 | GameObject gameObject = m_PreviewInstance; 705 | ParticleSystem particleSystem = gameObject.GetComponentInChildren(true); 706 | if (particleSystem) 707 | { 708 | particleSystem.Simulate(m_RunningTime, true); 709 | Repaint(); 710 | } 711 | } 712 | 713 | private void InspectorUpdate() 714 | { 715 | var delta = EditorApplication.timeSinceStartup - m_PreviousTime; 716 | m_PreviousTime = EditorApplication.timeSinceStartup; 717 | 718 | if (m_Playing) 719 | { 720 | m_RunningTime = Mathf.Clamp(m_RunningTime + (float)delta, 0f, kDuration); 721 | SimulateUpdate(); 722 | } 723 | } 724 | 725 | /// 726 | /// 设置模拟方式 727 | /// 一种直接调用Simulate方法,效果跟直接运行播放不一定一样 728 | /// 一种调用锁定粒子,再调用play方法,效果跟直接运行播放一样 729 | /// 730 | private void SetSimulateMode() 731 | { 732 | SimulateDisable(); 733 | if (m_PreviewInstance) 734 | { 735 | ParticleSystem particleSystem = m_PreviewInstance.GetComponentInChildren(true); 736 | if (particleSystem) 737 | { 738 | if (m_IsLockParticleSystem) 739 | { 740 | if (ParticleSystemEditorUtilsReflect.lockedParticleSystem != particleSystem) 741 | { 742 | ParticleSystemEditorUtilsReflect.lockedParticleSystem = particleSystem; 743 | } 744 | } 745 | else 746 | { 747 | ParticleSystemEditorUtilsReflect.lockedParticleSystem = null; 748 | } 749 | } 750 | } 751 | } 752 | 753 | private void Repaint() 754 | { 755 | // 让选择对象框也能刷新 756 | EditorWindow ew = EditorWindow.focusedWindow; 757 | if (ew && ew.titleContent.text.StartsWith("Select ", StringComparison.Ordinal)) 758 | { 759 | ew.Repaint(); 760 | return; 761 | } 762 | if (m_CacheEditor) 763 | { 764 | m_CacheEditor.Repaint(); 765 | } 766 | } 767 | 768 | /// 769 | /// 解锁粒子 770 | /// 771 | private void ClearLockedParticle() 772 | { 773 | if (m_PreviewInstance) 774 | { 775 | ParticleSystem particleSystem = m_PreviewInstance.GetComponentInChildren(true); 776 | if (particleSystem) 777 | { 778 | if (m_IsLockParticleSystem && ParticleSystemEditorUtilsReflect.lockedParticleSystem == particleSystem) 779 | { 780 | ParticleSystemEditorUtilsReflect.lockedParticleSystem = null; 781 | } 782 | } 783 | } 784 | } 785 | 786 | protected void HandleMouseDown(Event evt, int id, Rect previewRect) 787 | { 788 | if (viewTool != ViewTool.None && previewRect.Contains(evt.mousePosition)) 789 | { 790 | EditorGUIUtility.SetWantsMouseJumping(1); 791 | evt.Use(); 792 | GUIUtility.hotControl = id; 793 | } 794 | } 795 | 796 | protected void HandleMouseUp(Event evt, int id) 797 | { 798 | if (GUIUtility.hotControl == id) 799 | { 800 | m_ViewTool = ViewTool.None; 801 | GUIUtility.hotControl = 0; 802 | EditorGUIUtility.SetWantsMouseJumping(0); 803 | evt.Use(); 804 | } 805 | } 806 | 807 | protected void HandleMouseDrag(Event evt, int id, Rect previewRect) 808 | { 809 | if (m_PreviewInstance == null) 810 | { 811 | return; 812 | } 813 | 814 | if (GUIUtility.hotControl == id) 815 | { 816 | switch (m_ViewTool) 817 | { 818 | case ViewTool.Pan: 819 | DoAvatarPreviewPan(evt); 820 | break; 821 | case ViewTool.Zoom: 822 | DoAvatarPreviewZoom(evt, -HandleUtility.niceMouseDeltaZoom * ((!evt.shift) ? 0.5f : 2f)); 823 | break; 824 | case ViewTool.Orbit: 825 | DoAvatarPreviewOrbit(evt, previewRect); 826 | break; 827 | default: 828 | Debug.Log("Enum value not handled"); 829 | break; 830 | } 831 | } 832 | } 833 | 834 | protected void HandleViewTool(Event evt, EventType eventType, int id, Rect previewRect) 835 | { 836 | switch (eventType) 837 | { 838 | case EventType.MouseDown: 839 | HandleMouseDown(evt, id, previewRect); 840 | break; 841 | case EventType.MouseUp: 842 | HandleMouseUp(evt, id); 843 | break; 844 | case EventType.MouseDrag: 845 | HandleMouseDrag(evt, id, previewRect); 846 | break; 847 | case EventType.ScrollWheel: 848 | DoAvatarPreviewZoom(evt, HandleUtility.niceMouseDeltaZoom * ((!evt.shift) ? 0.5f : 2f)); 849 | break; 850 | } 851 | } 852 | 853 | public void DoAvatarPreviewOrbit(Event evt, Rect previewRect) 854 | { 855 | m_PreviewDir -= evt.delta * (float)((!evt.shift) ? 1 : 3) / 856 | Mathf.Min(previewRect.width, previewRect.height) * 140f; 857 | m_PreviewDir.y = Mathf.Clamp(m_PreviewDir.y, -90f, 90f); 858 | evt.Use(); 859 | } 860 | 861 | public void DoAvatarPreviewPan(Event evt) 862 | { 863 | Camera camera = GetPreviewCamera(); 864 | Vector3 vector = camera.WorldToScreenPoint(bodyPosition + m_PivotPositionOffset); 865 | Vector3 a = new Vector3(-evt.delta.x, evt.delta.y, 0f); 866 | vector += a * Mathf.Lerp(0.25f, 2f, m_ZoomFactor * 0.5f); 867 | Vector3 b = camera.ScreenToWorldPoint(vector) - (bodyPosition + m_PivotPositionOffset); 868 | m_PivotPositionOffset += b; 869 | evt.Use(); 870 | } 871 | 872 | /// 873 | /// 定位事件 874 | /// 按F近距离查看对象 875 | /// 按G视图平移到鼠标位置 876 | /// 877 | /// 878 | /// 879 | /// 880 | public void DoAvatarPreviewFrame(Event evt, EventType type, Rect previewRect) 881 | { 882 | if (type == EventType.KeyDown && evt.keyCode == KeyCode.F) 883 | { 884 | m_PivotPositionOffset = Vector3.zero; 885 | m_ZoomFactor = m_AvatarScale; 886 | evt.Use(); 887 | } 888 | 889 | if (type == EventType.KeyDown && Event.current.keyCode == KeyCode.G) 890 | { 891 | m_PivotPositionOffset = GetCurrentMouseWorldPosition(evt, previewRect) - bodyPosition; 892 | evt.Use(); 893 | } 894 | } 895 | 896 | protected Vector3 GetCurrentMouseWorldPosition(Event evt, Rect previewRect) 897 | { 898 | Camera camera = GetPreviewCamera(); 899 | float scaleFactor = m_PreviewUtility.GetScaleFactor(previewRect.width, previewRect.height); 900 | return camera.ScreenToWorldPoint(new Vector3((evt.mousePosition.x - previewRect.x) * scaleFactor, 901 | (previewRect.height - (evt.mousePosition.y - previewRect.y)) * scaleFactor, 0f) 902 | { 903 | z = Vector3.Distance(bodyPosition, camera.transform.position) 904 | }); 905 | } 906 | 907 | public void DoAvatarPreviewZoom(Event evt, float delta) 908 | { 909 | float num = -delta * 0.05f; 910 | m_ZoomFactor += m_ZoomFactor * num; 911 | m_ZoomFactor = Mathf.Max(m_ZoomFactor, m_AvatarScale / 10f); 912 | evt.Use(); 913 | } 914 | 915 | private float PreviewSlider(float val, float snapThreshold) 916 | { 917 | val = GUILayout.HorizontalSlider(val, 0.1f, 3f, s_Styles.preSlider, s_Styles.preSliderThumb, 918 | GUILayout.MaxWidth(64f)); 919 | if (val > 0.25f - snapThreshold && val < 0.25f + snapThreshold) 920 | { 921 | val = 0.25f; 922 | } 923 | else 924 | { 925 | if (val > 0.5f - snapThreshold && val < 0.5f + snapThreshold) 926 | { 927 | val = 0.5f; 928 | } 929 | else 930 | { 931 | if (val > 0.75f - snapThreshold && val < 0.75f + snapThreshold) 932 | { 933 | val = 0.75f; 934 | } 935 | else 936 | { 937 | if (val > 1f - snapThreshold && val < 1f + snapThreshold) 938 | { 939 | val = 1f; 940 | } 941 | else 942 | { 943 | if (val > 1.25f - snapThreshold && val < 1.25f + snapThreshold) 944 | { 945 | val = 1.25f; 946 | } 947 | else 948 | { 949 | if (val > 1.5f - snapThreshold && val < 1.5f + snapThreshold) 950 | { 951 | val = 1.5f; 952 | } 953 | else 954 | { 955 | if (val > 1.75f - snapThreshold && val < 1.75f + snapThreshold) 956 | { 957 | val = 1.75f; 958 | } 959 | } 960 | } 961 | } 962 | } 963 | } 964 | } 965 | 966 | return val; 967 | } 968 | 969 | public static void SetEnabledRecursive(GameObject go, bool enabled) 970 | { 971 | Renderer[] componentsInChildren = go.GetComponentsInChildren(); 972 | for (int i = 0; i < componentsInChildren.Length; i++) 973 | { 974 | Renderer renderer = componentsInChildren[i]; 975 | renderer.enabled = enabled; 976 | } 977 | } 978 | 979 | public static void GetRenderableBoundsRecurse(ref Bounds bounds, GameObject go) 980 | { 981 | MeshRenderer meshRenderer = go.GetComponent(typeof(MeshRenderer)) as MeshRenderer; 982 | MeshFilter meshFilter = go.GetComponent(typeof(MeshFilter)) as MeshFilter; 983 | if (meshRenderer && meshFilter && meshFilter.sharedMesh) 984 | { 985 | if (bounds.extents == Vector3.zero) 986 | { 987 | bounds = meshRenderer.bounds; 988 | } 989 | else 990 | { 991 | bounds.Encapsulate(meshRenderer.bounds); 992 | } 993 | } 994 | 995 | SkinnedMeshRenderer skinnedMeshRenderer = 996 | go.GetComponent(typeof(SkinnedMeshRenderer)) as SkinnedMeshRenderer; 997 | if (skinnedMeshRenderer && skinnedMeshRenderer.sharedMesh) 998 | { 999 | if (bounds.extents == Vector3.zero) 1000 | { 1001 | bounds = skinnedMeshRenderer.bounds; 1002 | } 1003 | else 1004 | { 1005 | bounds.Encapsulate(skinnedMeshRenderer.bounds); 1006 | } 1007 | } 1008 | 1009 | SpriteRenderer spriteRenderer = go.GetComponent(typeof(SpriteRenderer)) as SpriteRenderer; 1010 | if (spriteRenderer && spriteRenderer.sprite) 1011 | { 1012 | if (bounds.extents == Vector3.zero) 1013 | { 1014 | bounds = spriteRenderer.bounds; 1015 | } 1016 | else 1017 | { 1018 | bounds.Encapsulate(spriteRenderer.bounds); 1019 | } 1020 | } 1021 | 1022 | foreach (Transform transform in go.transform) 1023 | { 1024 | GetRenderableBoundsRecurse(ref bounds, transform.gameObject); 1025 | } 1026 | } 1027 | 1028 | private static void InitInstantiatedPreviewRecursive(GameObject go) 1029 | { 1030 | go.hideFlags = HideFlags.HideAndDontSave; 1031 | go.layer = PreviewCullingLayer; 1032 | foreach (Transform transform in go.transform) 1033 | { 1034 | InitInstantiatedPreviewRecursive(transform.gameObject); 1035 | } 1036 | } 1037 | 1038 | private static int CycleButton(int selected, GUIContent[] contents, GUIStyle style) 1039 | { 1040 | #if UNITY_2019_3_OR_NEWER 1041 | bool flag = GUILayout.Toggle(selected == 1, contents[selected], style); 1042 | return flag ? 1 : 0; 1043 | #else 1044 | bool flag = GUILayout.Button(contents[selected], style); 1045 | if (flag) 1046 | { 1047 | int num = selected; 1048 | selected = num + 1; 1049 | bool flag2 = selected >= contents.Length; 1050 | if (flag2) 1051 | { 1052 | selected = 0; 1053 | } 1054 | } 1055 | 1056 | return selected; 1057 | #endif 1058 | } 1059 | 1060 | private static GUIContent IconContent(string name, string tooltip) 1061 | { 1062 | GUIContent content = EditorGUIUtility.IconContent(name, tooltip); 1063 | content.tooltip = tooltip; 1064 | return content; 1065 | } 1066 | } 1067 | } -------------------------------------------------------------------------------- /Editor/ParticleSystemPreview.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c5a7baa5a54836e49acaf39275c1e888 3 | timeCreated: 1469432735 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/WuHuan.ParticleSystemPreview.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WuHuan.ParticleSystemPreview.Editor", 3 | "references": [], 4 | "optionalUnityReferences": [], 5 | "includePlatforms": [ 6 | "Editor" 7 | ], 8 | "excludePlatforms": [] 9 | } -------------------------------------------------------------------------------- /Editor/WuHuan.ParticleSystemPreview.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8e1319b391e0f15428d74aea6ec67d5d 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 WuHuan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4efc9e5d3c56d3d4cb5561edc43019d7 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity ParticleSystem Preview 粒子系统预览工具 2 | 3 | UnityParticleSystemPreview 是一个像模型动作动画那样可以直接在检视器里进行预览粒子的工具。 4 | 5 | ![](./Documentation~/Images/preview.gif) 6 | 7 | - Show - 显示粒子系统预览窗口 8 | - Pivot - 显示avatar的轴心和质心 9 | - Floor - 显示地板 10 | - Play - 播放粒子系统 11 | - Auto Play - 自动播放 12 | - Speed Slider - 改变粒子预览速度 13 | 14 | ## 安装 15 | 16 | - 通过【Package Manager】包管理器,添加git URL地址:https://github.com/akof1314/UnityParticleSystemPreview.git 17 | 18 | ## 源码 19 | AssetStore 地址:https://assetstore.unity.com/packages/tools/utilities/particle-system-preview-73346 20 | 21 | Github 地址:https://github.com/akof1314/UnityParticleSystemPreview -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d7675c82d20a8b94d9d224c4d59e87e1 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Samples~/demo.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akof1314/UnityParticleSystemPreview/cd7afaea995578ac005c927ffdee977b14677c2d/Samples~/demo.unity -------------------------------------------------------------------------------- /Samples~/demo.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5dd9c5e81efa93d48bf93f45ad5558b6 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Samples~/example1.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akof1314/UnityParticleSystemPreview/cd7afaea995578ac005c927ffdee977b14677c2d/Samples~/example1.prefab -------------------------------------------------------------------------------- /Samples~/example1.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c5f84209cf0070141adc836d4ecad2bf 3 | timeCreated: 1469497170 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Samples~/example2.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akof1314/UnityParticleSystemPreview/cd7afaea995578ac005c927ffdee977b14677c2d/Samples~/example2.prefab -------------------------------------------------------------------------------- /Samples~/example2.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 523ee265430a2bf43a1776d7e231dc0d 3 | timeCreated: 1469432697 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Samples~/example3.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akof1314/UnityParticleSystemPreview/cd7afaea995578ac005c927ffdee977b14677c2d/Samples~/example3.mat -------------------------------------------------------------------------------- /Samples~/example3.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 970ca87aaee450e4497838d70f5f688a 3 | timeCreated: 1474798200 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Samples~/example3.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akof1314/UnityParticleSystemPreview/cd7afaea995578ac005c927ffdee977b14677c2d/Samples~/example3.prefab -------------------------------------------------------------------------------- /Samples~/example3.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2f4c805c26426b841973afc98d75e079 3 | timeCreated: 1474798154 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Samples~/example4.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akof1314/UnityParticleSystemPreview/cd7afaea995578ac005c927ffdee977b14677c2d/Samples~/example4.prefab -------------------------------------------------------------------------------- /Samples~/example4.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7545b76843b169d46bac5fa8df58a531 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Samples~/example5.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akof1314/UnityParticleSystemPreview/cd7afaea995578ac005c927ffdee977b14677c2d/Samples~/example5.prefab -------------------------------------------------------------------------------- /Samples~/example5.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b629144ce63b39740afc446b5ddaf604 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Samples~/example6.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akof1314/UnityParticleSystemPreview/cd7afaea995578ac005c927ffdee977b14677c2d/Samples~/example6.prefab -------------------------------------------------------------------------------- /Samples~/example6.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cb919381d9ae13a4c8265f9590616f60 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Samples~/example7.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akof1314/UnityParticleSystemPreview/cd7afaea995578ac005c927ffdee977b14677c2d/Samples~/example7.prefab -------------------------------------------------------------------------------- /Samples~/example7.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 795160c7fb7932640ae4d4ebb776d226 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.wuhuan.particlesystempreview", 3 | "displayName": "Particle System Preview", 4 | "version": "1.0.0", 5 | "documentationUrl": "https://github.com/akof1314/UnityParticleSystemPreview", 6 | "description": "Unity ParticleSystem Preview 粒子预览插件", 7 | "category": "Editor", 8 | "author": { 9 | "name": "Wuhuan", 10 | "url": "https://github.com/akof1314" 11 | }, 12 | "samples": [ 13 | { 14 | "displayName": "Simple Demo", 15 | "description": "一个简单的示例。", 16 | "path": "Samples~" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7f5ff1ed6acd2ba498df6ca1ba828fe7 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------