UPF/Assets/NEW VFX TO CHECK/Editor Extensions/Utilities/Particle Playback/Editor/ParticlePlayback.cs

564 lines
20 KiB
C#

// =================================
// Namespaces.
// =================================
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
// =================================
// Define namespace.
// =================================
namespace MirzaBeig
{
namespace EditorExtensions
{
namespace Utilities
{
// =================================
// Classes.
// =================================
public class ParticlePlayback
{
// =================================
// Nested classes and structures.
// =================================
// ...
public enum ParticlePlaybackState
{
NULL,
Playing, Paused, Stopped
}
// =================================
// Variables.
// =================================
// Button sizes.
public float playbackButtonHeight = 25.0f;
// Selected objects in editor and all the particle systems components.
//List<ParticleSystem> particleSystems = new List<ParticleSystem>();
// Playback.
public float particlePlaybackPosition = 0.0f;
public float particlePlaybackPositionSliderMax = 2.0f;
// Sets the entire particle system set to a single randomized value.
bool randomizeParticleSeed = false;
// Sets different random seeds for each particle system.
bool randomizeMultiLevelParticleSeed = false;
int particleRandomSeed = 0;
// Because of interval jumps when adjusting the slider,
// the value Unity applies to the slider tends to overflow
// the actual maximum and resets itself.
// Simple solution: I limit the max to half.
// Downside? It's only HALF as random as before... which doesn't even make a difference.
// EDIT: There's another issue where calling inSlider with a large value
// causes it to change slightly when passed through itself.
// The issue was apparent when using the randomize button.
// Setting it to max / 32 seems to solve the problem...
const int intSliderMaxValue = int.MaxValue / 32;
// Clear all particles on screen from selection when selection changes?
public bool clearParticlesOnSelectionChange = false;
// Button pressed state.
ParticlePlaybackState particlePlaybackState = ParticlePlaybackState.NULL;
// For calculating deltaTime in Update().
float lastUpdateTime = 0.0f;
// Active editor windows.
EditorWindow gameView;
EditorWindow sceneView;
// Scrolling view position for selected objects.
Vector2 scrollPosition;
// For labeling and tooltips.
GUIContent guiContentLabel;
// =================================
// Functions.
// =================================
// ...
// Return an editor window by name.
public EditorWindow getEditorWindow(string name)
{
System.Reflection.Assembly assembly = typeof(UnityEditor.EditorWindow).Assembly;
Object[] editorWindows = Resources.FindObjectsOfTypeAll(assembly.GetType(name));
// If window doesn't exist, GetWindow() creates it.
// This is not what I want. So if the window doesn't exist, skip this.
if (editorWindows.Length != 0)
{
return EditorWindow.GetWindow(assembly.GetType(name), false, null, false);
}
return null;
}
// Update/refresh/repaint all NON-EDITOR windows.
public void repaintEditorCameraWindows()
{
if (gameView)
{
gameView.Repaint();
}
if (sceneView)
{
sceneView.Repaint();
}
}
// ...
public void updateEditorCameraWindowReferences()
{
gameView = getEditorWindow("UnityEditor.GameView");
sceneView = getEditorWindow("UnityEditor.SceneView");
}
// ...
public void pause(List<ParticleSystem> particleSystems)
{
lastUpdateTime = 0.0f;
for (int i = 0; i < particleSystems.Count; i++)
{
particleSystems[i].Pause(false);
}
}
// Simulates full-circle back to current position.
public void loopback(ParticleSystem particleSystem)
{
// Lock random seed.
bool autoRandomSeed = particleSystem.useAutoRandomSeed;
// Save state before set.
ParticlePlaybackState state;
if (particleSystem.isPlaying)
{
state = ParticlePlaybackState.Playing;
}
else if (particleSystem.isPaused)
{
state = ParticlePlaybackState.Paused;
}
else
{
state = ParticlePlaybackState.Stopped;
}
// DON'T RESTART the simulation.
// Instead, stop, play, then simulate to time.
// Keep last false in Simulate to prevent restarts.
// Else, particles will pop in and out...
// Also requires clear in that case.
particleSystem.Stop(false);
particleSystem.Clear(false);
particleSystem.randomSeed = (uint)particleRandomSeed;
particleSystem.Play(false);
particleSystem.Simulate(particlePlaybackPosition, false, false);
// Resume from saved playback state.
particleSystem.Pause();
particleSystem.useAutoRandomSeed = autoRandomSeed;
switch (state)
{
case ParticlePlaybackState.Playing:
{
particleSystem.Play(false);
break;
}
case ParticlePlaybackState.Paused:
{
particleSystem.Pause(false);
break;
}
case ParticlePlaybackState.Stopped:
{
particleSystem.Stop(false);
break;
}
}
}
// ...
public void GUIPlaybackSettings(List<ParticleSystem> particleSystems)
{
// Get windows.
updateEditorCameraWindowReferences();
// Playback settings.
EditorGUILayout.LabelField("- Playback Settings:", EditorStyles.boldLabel);
EditorGUILayout.Separator();
// Timer options.
GUI.enabled = particleSystems.Count != 0;
EditorGUI.BeginChangeCheck();
{
guiContentLabel = new GUIContent("Playback Position (s)", "Playback position of all selected particle systems in seconds.");
particlePlaybackPosition = EditorGUILayout.Slider(guiContentLabel, particlePlaybackPosition, 0.0f, particlePlaybackPositionSliderMax);
}
// Only update if slider changed.
if (EditorGUI.EndChangeCheck())
{
// Set playback position.
for (int i = 0; i < particleSystems.Count; i++)
{
loopback(particleSystems[i]);
}
// Refresh.
repaintEditorCameraWindows();
}
GUI.enabled = true;
EditorGUILayout.Separator();
guiContentLabel = new GUIContent("Playback Position Max (s)", "Playback position maximum value in seconds.");
particlePlaybackPositionSliderMax = EditorGUILayout.Slider(guiContentLabel, particlePlaybackPositionSliderMax, 0.0f, 32.0f);
EditorGUILayout.Separator();
// Playback buttons.
GUI.enabled = particleSystems.Count != 0;
EditorGUILayout.BeginHorizontal();
{
guiContentLabel = new GUIContent("Play",
"Play all selected particles.");
if (GUILayout.Button(guiContentLabel, GUILayout.Height(playbackButtonHeight)))
{
if (randomizeParticleSeed)
{
particleRandomSeed = Random.Range(0, intSliderMaxValue);
}
for (int i = 0; i < particleSystems.Count; i++)
{
if (randomizeParticleSeed && randomizeMultiLevelParticleSeed)
{
particleRandomSeed = Random.Range(0, intSliderMaxValue);
}
bool autoRandomSeed = particleSystems[i].useAutoRandomSeed;
particleSystems[i].randomSeed = (uint)particleRandomSeed;
particleSystems[i].useAutoRandomSeed = autoRandomSeed;
if (particlePlaybackState != ParticlePlaybackState.Paused)
{
particleSystems[i].Stop(false);
particleSystems[i].Clear(false);
}
particleSystems[i].Play(false);
}
particlePlaybackState = ParticlePlaybackState.Playing;
}
guiContentLabel = new GUIContent("Pause",
"Pause all selected particles.");
if (GUILayout.Button(guiContentLabel, GUILayout.Height(playbackButtonHeight)))
{
for (int i = 0; i < particleSystems.Count; i++)
{
particleSystems[i].Pause(false);
lastUpdateTime = 0.0f;
}
particlePlaybackState = ParticlePlaybackState.Paused;
}
guiContentLabel = new GUIContent("Stop",
"Stop all selected particles.");
if (GUILayout.Button(guiContentLabel, GUILayout.Height(playbackButtonHeight)))
{
for (int i = 0; i < particleSystems.Count; i++)
{
particleSystems[i].Stop(false);
particleSystems[i].Clear(false);
lastUpdateTime = 0.0f;
}
particlePlaybackState = ParticlePlaybackState.Stopped;
}
}
EditorGUILayout.EndHorizontal();
//EditorGUILayout.Separator();
// Button to clear all particles.
guiContentLabel = new GUIContent("Clear",
"Clear all particles on screen.");
if (GUILayout.Button(guiContentLabel, GUILayout.Height(playbackButtonHeight)))
{
for (int i = 0; i < particleSystems.Count; i++)
{
particleSystems[i].Clear(false);
particleSystems[i].Stop();
}
repaintEditorCameraWindows();
}
GUI.enabled = true;
EditorGUILayout.Separator();
// Display playback state.
//EditorGUILayout.HelpBox("> " + particlePlaybackState.ToString(), MessageType.None);
//EditorGUILayout.Separator();
// Particle seed options.
guiContentLabel = new GUIContent("Randomize Seed", "Set all particle systems' seed to a single random value.");
randomizeParticleSeed = EditorGUILayout.Toggle(guiContentLabel, randomizeParticleSeed);
GUI.enabled = randomizeParticleSeed;
guiContentLabel = new GUIContent("Multi-Level Random Seed", "Randomize particle seed per system.");
randomizeMultiLevelParticleSeed = EditorGUILayout.Toggle(guiContentLabel, randomizeMultiLevelParticleSeed);
GUI.enabled = !randomizeParticleSeed;
EditorGUILayout.Separator();
EditorGUI.BeginChangeCheck();
{
guiContentLabel = new GUIContent("Particle Random Seed", "Set the \"random\" particle seed. 0 == auto-randomize on awake.");
particleRandomSeed = EditorGUILayout.IntSlider(guiContentLabel, particleRandomSeed, 0, intSliderMaxValue);
}
if (EditorGUI.EndChangeCheck())
{
for (int i = 0; i < particleSystems.Count; i++)
{
loopback(particleSystems[i]);
}
}
// Randomize seed.
guiContentLabel = new GUIContent("Randomize Seed",
"Generate a random seed value for the slider.");
if (GUILayout.Button(guiContentLabel, GUILayout.Height(playbackButtonHeight)))
{
particleRandomSeed = Random.Range(0, intSliderMaxValue);
for (int i = 0; i < particleSystems.Count; i++)
{
loopback(particleSystems[i]);
}
}
GUI.enabled = true;
EditorGUILayout.Separator();
// Clear emitted particles on-screen from selection on selection change?
guiContentLabel = new GUIContent("Auto-Clear Particles", "Clear all emitted particles from previous selection on selection change.");
clearParticlesOnSelectionChange = EditorGUILayout.Toggle(guiContentLabel, clearParticlesOnSelectionChange);
}
// ...
public void GUIParticleSelection(List<GameObject> selectedGameObjectsWithParticleSystems)
{
EditorGUILayout.Separator();
// Selected objects.
guiContentLabel = new GUIContent("- Selected:",
"Selected GameObjects must be active and contain at least one active ParticleSystem component in their hierarchy.");
EditorGUILayout.LabelField(guiContentLabel, EditorStyles.boldLabel);
EditorGUILayout.Separator();
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
{
if (selectedGameObjectsWithParticleSystems.Count == 0)
{
//EditorGUILayout.LabelField("Please select GameObjects with at least one ParticleSystem component.", EditorStyles.miniBoldLabel);
EditorGUILayout.LabelField("No particle systems selected.", EditorStyles.miniBoldLabel);
EditorGUILayout.HelpBox("WARNING: " + "Please select GameObjects with at least one ParticleSystem component.", MessageType.Warning);
}
else
{
// Selection will be ordered based on scene hierarchy.
// Reverse here, else they will be listed in reverse.
for (int i = selectedGameObjectsWithParticleSystems.Count - 1; i != -1; i--)
{
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.LabelField("> " + selectedGameObjectsWithParticleSystems[i].name, EditorStyles.miniLabel);
}
EditorGUILayout.EndHorizontal();
}
}
}
EditorGUILayout.EndScrollView();
}
// ...
public void update(List<ParticleSystem> particleSystems)
{
if (particleSystems.Count != 0 &&
particlePlaybackState == ParticlePlaybackState.Playing)
{
// Get windows.
//updateEditorCameraWindowReferences();
// Calculate time passed since last call to function.
float deltaTime = Time.realtimeSinceStartup - lastUpdateTime;
// Force game view to refresh if not in play mode
// so ALL particles will play (and not just selected ones).
if (!Application.isPlaying && lastUpdateTime != 0.0f)
{
for (int i = 0; i < particleSystems.Count; i++)
{
particleSystems[i].Simulate(deltaTime, false, false);
}
// Update slider.
//particlePlaybackPosition += deltaTime;
// Update window displays.
repaintEditorCameraWindows();
//switch (particlePlaybackState)
//{
// case ParticlePlaybackState.Playing:
// {
// for (int i = 0; i < particleSystems.Count; i++)
// {
// particleSystems[i].Simulate(1.0f / 60.0f, false, false);
// }
// repaintCameraViews();
// break;
// }
// case ParticlePlaybackState.Paused:
// {
// break;
// }
// default:
// {
// break;
// }
//}
}
// Set to time at end of function.
lastUpdateTime = Time.realtimeSinceStartup;
}
}
// =================================
// End functions.
// =================================
}
// =================================
// End namespace.
// =================================
}
}
}
// =================================
// --END-- //
// =================================