163 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using UnityEngine.Playables;
 | |
| 
 | |
| namespace UnityEngine.Timeline
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Playable that synchronizes a particle system simulation.
 | |
|     /// </summary>
 | |
|     public class ParticleControlPlayable : PlayableBehaviour
 | |
|     {
 | |
|         const float kUnsetTime = float.MaxValue;
 | |
|         float m_LastPlayableTime = kUnsetTime;
 | |
|         float m_LastParticleTime = kUnsetTime;
 | |
|         uint m_RandomSeed = 1;
 | |
| 
 | |
| 
 | |
|         /// <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 system 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">The particle system to control</param>
 | |
|         /// <param name="randomSeed">A random seed to use for particle simulation</param>
 | |
|         public void Initialize(ParticleSystem ps, uint randomSeed)
 | |
|         {
 | |
|             m_RandomSeed = Math.Max(1, randomSeed);
 | |
|             particleSystem = ps;
 | |
|             SetRandomSeed(particleSystem, m_RandomSeed);
 | |
| 
 | |
| #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_LastPlayableTime = kUnsetTime;
 | |
|         }
 | |
| 
 | |
| #endif
 | |
| 
 | |
|         static void SetRandomSeed(ParticleSystem particleSystem, uint randomSeed)
 | |
|         {
 | |
|             if (particleSystem == null)
 | |
|                 return;
 | |
| 
 | |
|             particleSystem.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
 | |
|             if (particleSystem.useAutoRandomSeed)
 | |
|             {
 | |
|                 particleSystem.useAutoRandomSeed = false;
 | |
|                 particleSystem.randomSeed = randomSeed;
 | |
|             }
 | |
| 
 | |
|             for (int i = 0; i < particleSystem.subEmitters.subEmittersCount; i++)
 | |
|             {
 | |
|                 SetRandomSeed(particleSystem.subEmitters.GetSubEmitterSystem(i), ++randomSeed);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <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)
 | |
|             {
 | |
|                 // case 1212943
 | |
|                 m_LastPlayableTime = kUnsetTime;
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             var time = (float)playable.GetTime();
 | |
|             var particleTime = particleSystem.time;
 | |
| 
 | |
|             // if particle system time has changed externally, a re-sync is needed
 | |
|             if (m_LastPlayableTime > time || !Mathf.Approximately(particleTime, m_LastParticleTime))
 | |
|                 Simulate(time, true);
 | |
|             else if (m_LastPlayableTime < time)
 | |
|                 Simulate(time - m_LastPlayableTime, false);
 | |
| 
 | |
|             m_LastPlayableTime = time;
 | |
|             m_LastParticleTime = particleSystem.time;
 | |
|         }
 | |
| 
 | |
|         /// <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_LastPlayableTime = 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_LastPlayableTime = kUnsetTime;
 | |
|         }
 | |
| 
 | |
|         private void Simulate(float time, bool restart)
 | |
|         {
 | |
|             const bool withChildren = false;
 | |
|             const bool fixedTimeStep = false;
 | |
|             float maxTime = Time.maximumDeltaTime;
 | |
| 
 | |
|             if (restart)
 | |
|                 particleSystem.Simulate(0, withChildren, true, fixedTimeStep);
 | |
| 
 | |
|             // simulating by too large a time-step causes sub-emitters not to work, and loops not to
 | |
|             // simulate correctly
 | |
|             while (time > maxTime)
 | |
|             {
 | |
|                 particleSystem.Simulate(maxTime, withChildren, false, fixedTimeStep);
 | |
|                 time -= maxTime;
 | |
|             }
 | |
| 
 | |
|             if (time > 0)
 | |
|                 particleSystem.Simulate(time, withChildren, false, fixedTimeStep);
 | |
|         }
 | |
|     }
 | |
| }
 |