diff options
Diffstat (limited to 'Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Playables/ParticleControlPlayable.cs')
| -rw-r--r-- | Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Playables/ParticleControlPlayable.cs | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Playables/ParticleControlPlayable.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Playables/ParticleControlPlayable.cs new file mode 100644 index 0000000..a513d7c --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Playables/ParticleControlPlayable.cs @@ -0,0 +1,177 @@ +using System; +using UnityEngine.Playables; + +namespace UnityEngine.Timeline +{ + /// <summary> + /// Playable that synchronizes a particle system simulation. + /// </summary> + public class ParticleControlPlayable : PlayableBehaviour + { + const float kUnsetTime = -1; + float m_LastTime = kUnsetTime; + uint m_RandomSeed = 1; + + // particleSystem.time can not be relied on for an accurate time. It does not advance until a delta threshold is reached(fixedUpdate) and until the start delay has elapsed. + float m_SystemTime; + + /// <summary> + /// Creates a Playable with a ParticleControlPlayable behaviour attached + /// </summary> + /// <param name="graph">The PlayableGraph to inject the Playable into.</param> + /// <param name="component">The particle systtem to control</param> + /// <param name="randomSeed">A random seed to use for particle simulation</param> + /// <returns>Returns the created Playable.</returns> + public static ScriptPlayable<ParticleControlPlayable> Create(PlayableGraph graph, ParticleSystem component, uint randomSeed) + { + if (component == null) + return ScriptPlayable<ParticleControlPlayable>.Null; + + var handle = ScriptPlayable<ParticleControlPlayable>.Create(graph); + handle.GetBehaviour().Initialize(component, randomSeed); + return handle; + } + + /// <summary> + /// The particle system to control + /// </summary> + public ParticleSystem particleSystem { get; private set; } + + /// <summary> + /// Initializes the behaviour with a particle system and random seed. + /// </summary> + /// <param name="ps"></param> + /// <param name="randomSeed"></param> + public void Initialize(ParticleSystem ps, uint randomSeed) + { + m_RandomSeed = Math.Max(1, randomSeed); + particleSystem = ps; + m_SystemTime = 0; + SetRandomSeed(); + + #if UNITY_EDITOR + if (!Application.isPlaying && UnityEditor.PrefabUtility.IsPartOfPrefabInstance(ps)) + UnityEditor.PrefabUtility.prefabInstanceUpdated += OnPrefabUpdated; + #endif + } + + #if UNITY_EDITOR + /// <summary> + /// This function is called when the Playable that owns the PlayableBehaviour is destroyed. + /// </summary> + /// <param name="playable">The playable this behaviour is attached to.</param> + public override void OnPlayableDestroy(Playable playable) + { + if (!Application.isPlaying) + UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabUpdated; + } + + void OnPrefabUpdated(GameObject go) + { + // When the instance is updated from, this will cause the next evaluate to resimulate. + if (UnityEditor.PrefabUtility.GetRootGameObject(particleSystem) == go) + m_LastTime = kUnsetTime; + } + + #endif + + void SetRandomSeed() + { + particleSystem.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); + var systems = particleSystem.gameObject.GetComponentsInChildren<ParticleSystem>(); + uint seed = m_RandomSeed; + foreach (var ps in systems) + { + // don't overwrite user set random seeds + if (ps.useAutoRandomSeed) + { + ps.useAutoRandomSeed = false; + ps.randomSeed = seed; + seed++; + } + } + } + + /// <summary> + /// This function is called during the PrepareFrame phase of the PlayableGraph. + /// </summary> + /// <param name="playable">The Playable that owns the current PlayableBehaviour.</param> + /// <param name="data">A FrameData structure that contains information about the current frame context.</param> + public override void PrepareFrame(Playable playable, FrameData data) + { + if (particleSystem == null || !particleSystem.gameObject.activeInHierarchy) + return; + + float localTime = (float)playable.GetTime(); + bool shouldUpdate = Mathf.Approximately(m_LastTime, kUnsetTime) || + !Mathf.Approximately(m_LastTime, localTime); + if (shouldUpdate) + { + float epsilon = Time.fixedDeltaTime * 0.5f; + float simTime = localTime; + float expectedDelta = simTime - m_LastTime; + + // The first iteration includes the start delay. Evaluate(particleSystem.randomSeed) is how the particle system generates the random value internally. + float startDelay = particleSystem.main.startDelay.Evaluate(particleSystem.randomSeed); + float particleSystemDurationLoop0 = particleSystem.main.duration + startDelay; + + // The particle system time does not include the start delay so we need to remove this for our own system time. + float expectedSystemTime = simTime > particleSystemDurationLoop0 ? m_SystemTime : m_SystemTime - startDelay; + + // if it's not looping, then the system time won't advance past the end of the duration + if (!particleSystem.main.loop) + expectedSystemTime = Math.Min(expectedSystemTime, particleSystem.main.duration); + + + // conditions for restart + bool restart = (simTime < m_LastTime) || // time went backwards + (simTime < epsilon) || // time is set to 0 + Mathf.Approximately(m_LastTime, kUnsetTime) || // object disabled + (expectedDelta > particleSystem.main.duration) || // large jump (bug workaround) + !(Mathf.Abs(expectedSystemTime - particleSystem.time) < Time.maximumParticleDeltaTime); // particle system isn't where we left it + if (restart) + { + // work around for a bug where simulate(simTime, true, true) doesn't work on loops + particleSystem.Simulate(0, true, true); + particleSystem.Simulate(simTime, true, false); + m_SystemTime = simTime; + } + else + { + // ps.time will wrap, so we need to account for that in computing delta time + float particleSystemDuration = simTime > particleSystemDurationLoop0 ? particleSystem.main.duration : particleSystemDurationLoop0; + float fracTime = simTime % particleSystemDuration; + float deltaTime = fracTime - m_SystemTime; + + if (deltaTime < -epsilon) // detect wrapping of ps.time + deltaTime = fracTime + particleSystemDurationLoop0 - m_SystemTime; + + particleSystem.Simulate(deltaTime, true, false); + m_SystemTime += deltaTime; + } + + m_LastTime = localTime; + } + } + + /// <summary> + /// This function is called when the Playable play state is changed to Playables.PlayState.Playing. + /// </summary> + /// <param name="playable">The Playable that owns the current PlayableBehaviour.</param> + /// <param name="info">A FrameData structure that contains information about the current frame context.</param> + public override void OnBehaviourPlay(Playable playable, FrameData info) + { + m_LastTime = kUnsetTime; + } + + /// <summary> + /// This function is called when the Playable play state is changed to PlayState.Paused. + /// </summary> + /// <param name="playable">The playable this behaviour is attached to.</param> + /// <param name="info">A FrameData structure that contains information about the current frame context.</param> + public override void OnBehaviourPause(Playable playable, FrameData info) + { + m_LastTime = kUnsetTime; + } + } +} |
