A game about forced loneliness, made by TACStudios
at master 162 lines 6.5 kB view raw
1using System; 2using UnityEngine.Playables; 3 4namespace UnityEngine.Timeline 5{ 6 /// <summary> 7 /// Playable that synchronizes a particle system simulation. 8 /// </summary> 9 public class ParticleControlPlayable : PlayableBehaviour 10 { 11 const float kUnsetTime = float.MaxValue; 12 float m_LastPlayableTime = kUnsetTime; 13 float m_LastParticleTime = kUnsetTime; 14 uint m_RandomSeed = 1; 15 16 17 /// <summary> 18 /// Creates a Playable with a ParticleControlPlayable behaviour attached 19 /// </summary> 20 /// <param name="graph">The PlayableGraph to inject the Playable into.</param> 21 /// <param name="component">The particle systtem to control</param> 22 /// <param name="randomSeed">A random seed to use for particle simulation</param> 23 /// <returns>Returns the created Playable.</returns> 24 public static ScriptPlayable<ParticleControlPlayable> Create(PlayableGraph graph, ParticleSystem component, uint randomSeed) 25 { 26 if (component == null) 27 return ScriptPlayable<ParticleControlPlayable>.Null; 28 29 var handle = ScriptPlayable<ParticleControlPlayable>.Create(graph); 30 handle.GetBehaviour().Initialize(component, randomSeed); 31 return handle; 32 } 33 34 /// <summary> 35 /// The particle system to control 36 /// </summary> 37 public ParticleSystem particleSystem { get; private set; } 38 39 /// <summary> 40 /// Initializes the behaviour with a particle system and random seed. 41 /// </summary> 42 /// <param name="ps"></param> 43 /// <param name="randomSeed"></param> 44 public void Initialize(ParticleSystem ps, uint randomSeed) 45 { 46 m_RandomSeed = Math.Max(1, randomSeed); 47 particleSystem = ps; 48 SetRandomSeed(particleSystem, m_RandomSeed); 49 50#if UNITY_EDITOR 51 if (!Application.isPlaying && UnityEditor.PrefabUtility.IsPartOfPrefabInstance(ps)) 52 UnityEditor.PrefabUtility.prefabInstanceUpdated += OnPrefabUpdated; 53#endif 54 } 55 56#if UNITY_EDITOR 57 /// <summary> 58 /// This function is called when the Playable that owns the PlayableBehaviour is destroyed. 59 /// </summary> 60 /// <param name="playable">The playable this behaviour is attached to.</param> 61 public override void OnPlayableDestroy(Playable playable) 62 { 63 if (!Application.isPlaying) 64 UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabUpdated; 65 } 66 67 void OnPrefabUpdated(GameObject go) 68 { 69 // When the instance is updated from, this will cause the next evaluate to resimulate. 70 if (UnityEditor.PrefabUtility.GetRootGameObject(particleSystem) == go) 71 m_LastPlayableTime = kUnsetTime; 72 } 73 74#endif 75 76 static void SetRandomSeed(ParticleSystem particleSystem, uint randomSeed) 77 { 78 if (particleSystem == null) 79 return; 80 81 particleSystem.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); 82 if (particleSystem.useAutoRandomSeed) 83 { 84 particleSystem.useAutoRandomSeed = false; 85 particleSystem.randomSeed = randomSeed; 86 } 87 88 for (int i = 0; i < particleSystem.subEmitters.subEmittersCount; i++) 89 { 90 SetRandomSeed(particleSystem.subEmitters.GetSubEmitterSystem(i), ++randomSeed); 91 } 92 } 93 94 /// <summary> 95 /// This function is called during the PrepareFrame phase of the PlayableGraph. 96 /// </summary> 97 /// <param name="playable">The Playable that owns the current PlayableBehaviour.</param> 98 /// <param name="data">A FrameData structure that contains information about the current frame context.</param> 99 public override void PrepareFrame(Playable playable, FrameData data) 100 { 101 if (particleSystem == null || !particleSystem.gameObject.activeInHierarchy) 102 { 103 // case 1212943 104 m_LastPlayableTime = kUnsetTime; 105 return; 106 } 107 108 var time = (float)playable.GetTime(); 109 var particleTime = particleSystem.time; 110 111 // if particle system time has changed externally, a re-sync is needed 112 if (m_LastPlayableTime > time || !Mathf.Approximately(particleTime, m_LastParticleTime)) 113 Simulate(time, true); 114 else if (m_LastPlayableTime < time) 115 Simulate(time - m_LastPlayableTime, false); 116 117 m_LastPlayableTime = time; 118 m_LastParticleTime = particleSystem.time; 119 } 120 121 /// <summary> 122 /// This function is called when the Playable play state is changed to Playables.PlayState.Playing. 123 /// </summary> 124 /// <param name="playable">The Playable that owns the current PlayableBehaviour.</param> 125 /// <param name="info">A FrameData structure that contains information about the current frame context.</param> 126 public override void OnBehaviourPlay(Playable playable, FrameData info) 127 { 128 m_LastPlayableTime = kUnsetTime; 129 } 130 131 /// <summary> 132 /// This function is called when the Playable play state is changed to PlayState.Paused. 133 /// </summary> 134 /// <param name="playable">The playable this behaviour is attached to.</param> 135 /// <param name="info">A FrameData structure that contains information about the current frame context.</param> 136 public override void OnBehaviourPause(Playable playable, FrameData info) 137 { 138 m_LastPlayableTime = kUnsetTime; 139 } 140 141 private void Simulate(float time, bool restart) 142 { 143 const bool withChildren = false; 144 const bool fixedTimeStep = false; 145 float maxTime = Time.maximumDeltaTime; 146 147 if (restart) 148 particleSystem.Simulate(0, withChildren, true, fixedTimeStep); 149 150 // simulating by too large a time-step causes sub-emitters not to work, and loops not to 151 // simulate correctly 152 while (time > maxTime) 153 { 154 particleSystem.Simulate(maxTime, withChildren, false, fixedTimeStep); 155 time -= maxTime; 156 } 157 158 if (time > 0) 159 particleSystem.Simulate(time, withChildren, false, fixedTimeStep); 160 } 161 } 162}