TimelineEffector.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. namespace Chronos
  4. {
  5. /// <summary>
  6. /// An internal base class that applies time effects from a timeline to most built-in Unity components.
  7. /// </summary>
  8. public abstract class TimelineEffector : MonoBehaviour
  9. {
  10. protected const float DefaultRecordingInterval = 0.5f;
  11. private bool enabledOnce;
  12. public TimelineEffector()
  13. {
  14. components = new List<IComponentTimeline>();
  15. audioSources = new List<AudioSourceTimeline>();
  16. }
  17. protected abstract Timeline timeline { get; }
  18. protected virtual void Awake()
  19. {
  20. CacheComponents();
  21. }
  22. private void Start()
  23. {
  24. OnStartOrReEnable();
  25. }
  26. private void OnEnable()
  27. {
  28. if (enabledOnce)
  29. {
  30. OnStartOrReEnable();
  31. }
  32. else
  33. {
  34. enabledOnce = true;
  35. }
  36. }
  37. protected virtual void OnStartOrReEnable()
  38. {
  39. for (int i = 0; i < components.Count; i++)
  40. {
  41. var component = components[i];
  42. component.AdjustProperties();
  43. component.OnStartOrReEnable();
  44. }
  45. }
  46. protected virtual void Update()
  47. {
  48. for (int i = 0; i < components.Count; i++)
  49. {
  50. components[i].Update();
  51. }
  52. }
  53. protected virtual void FixedUpdate()
  54. {
  55. for (int i = 0; i < components.Count; i++)
  56. {
  57. components[i].FixedUpdate();
  58. }
  59. }
  60. protected virtual void OnDisable()
  61. {
  62. for (int i = 0; i < components.Count; i++)
  63. {
  64. components[i].OnDisable();
  65. }
  66. }
  67. #region Rewinding / Recording
  68. [SerializeField]
  69. private float _recordingInterval = DefaultRecordingInterval;
  70. /// <summary>
  71. /// The interval in seconds at which snapshots will be recorder. Lower values offer more rewind precision but require more memory.
  72. /// </summary>
  73. public float recordingInterval
  74. {
  75. get { return _recordingInterval; }
  76. set
  77. {
  78. _recordingInterval = value;
  79. if (recorder != null)
  80. {
  81. recorder.Reset();
  82. }
  83. }
  84. }
  85. #endregion
  86. #region Components
  87. internal List<IComponentTimeline> components;
  88. public new AnimationTimeline animation { get; protected set; }
  89. public AnimatorTimeline animator { get; protected set; }
  90. public List<AudioSourceTimeline> audioSources { get; protected set; }
  91. public AudioSourceTimeline audioSource { get; protected set; }
  92. public NavMeshAgentTimeline navMeshAgent { get; protected set; }
  93. public new IParticleSystemTimeline particleSystem { get; protected set; }
  94. public new RigidbodyTimeline3D rigidbody { get; protected set; }
  95. public new RigidbodyTimeline2D rigidbody2D { get; protected set; }
  96. public new TransformTimeline transform { get; protected set; }
  97. public WindZoneTimeline windZone { get; protected set; }
  98. public TerrainTimeline terrain { get; protected set; }
  99. public TrailRendererTimeline trailRenderer { get; protected set; }
  100. public WheelColliderTimeline wheelCollider { get; protected set; }
  101. protected IRecorder recorder
  102. {
  103. get
  104. {
  105. if (rigidbody != null) return rigidbody;
  106. if (rigidbody2D != null) return rigidbody2D;
  107. if (transform != null) return transform;
  108. return null;
  109. }
  110. }
  111. /// <summary>
  112. /// The components used by the timeline are cached for performance. If you add or remove built-in Unity components on the GameObject, you need to call this method to update the timeline accordingly.
  113. /// </summary>
  114. public virtual void CacheComponents()
  115. {
  116. components.Clear();
  117. // Animator
  118. var animatorComponent = GetComponent<Animator>();
  119. if (animator == null && animatorComponent != null)
  120. {
  121. animator = new AnimatorTimeline(timeline, animatorComponent);
  122. animator.Initialize();
  123. components.Add(animator);
  124. }
  125. else if (animator != null && animatorComponent == null)
  126. {
  127. animator = null;
  128. }
  129. // Animation
  130. var animationComponent = GetComponent<Animation>();
  131. if (animation == null && animationComponent != null)
  132. {
  133. animation = new AnimationTimeline(timeline, animationComponent);
  134. animation.Initialize();
  135. components.Add(animation);
  136. }
  137. else if (animation != null && animationComponent == null)
  138. {
  139. animation = null;
  140. }
  141. // AudioSources
  142. var audioSourceComponents = GetComponents<AudioSource>();
  143. // Remove timelines for absent components
  144. for (int i = 0; i < audioSources.Count; i++)
  145. {
  146. var audioSource = audioSources[i];
  147. bool audioSourceComponentExists = false;
  148. for (int j = 0; j < audioSourceComponents.Length; j++)
  149. {
  150. var audioSourceComponent = audioSourceComponents[j];
  151. if (audioSource.component == audioSourceComponent)
  152. {
  153. audioSourceComponentExists = true;
  154. break;
  155. }
  156. }
  157. if (!audioSourceComponentExists)
  158. {
  159. audioSources.Remove(audioSource);
  160. }
  161. }
  162. // Add timelines for new components
  163. for (int i = 0; i < audioSourceComponents.Length; i++)
  164. {
  165. var audioSourceComponent = audioSourceComponents[i];
  166. bool audioSourceExists = false;
  167. for (int j = 0; j < audioSources.Count; j++)
  168. {
  169. var audioSource = audioSources[j];
  170. if (audioSource.component == audioSourceComponent)
  171. {
  172. audioSourceExists = true;
  173. break;
  174. }
  175. }
  176. if (!audioSourceExists)
  177. {
  178. var newAudioSource = new AudioSourceTimeline(timeline, audioSourceComponent);
  179. newAudioSource.Initialize();
  180. audioSources.Add(newAudioSource);
  181. components.Add(newAudioSource);
  182. }
  183. }
  184. this.audioSource = audioSources.Count > 0 ? audioSources[0] : null;
  185. // NavMeshAgent
  186. var navMeshAgentComponent = GetComponent<UnityEngine.AI.NavMeshAgent>();
  187. if (navMeshAgent == null && navMeshAgentComponent != null)
  188. {
  189. navMeshAgent = new NavMeshAgentTimeline(timeline, navMeshAgentComponent);
  190. navMeshAgent.Initialize();
  191. components.Add(navMeshAgent);
  192. }
  193. else if (animation != null && navMeshAgentComponent == null)
  194. {
  195. navMeshAgent = null;
  196. }
  197. // ParticleSystem
  198. var particleSystemComponent = GetComponent<ParticleSystem>();
  199. if (particleSystem == null && particleSystemComponent != null)
  200. {
  201. if (timeline.rewindable)
  202. {
  203. particleSystem = new RewindableParticleSystemTimeline(timeline, particleSystemComponent);
  204. particleSystem.Initialize();
  205. }
  206. else
  207. {
  208. particleSystem = new NonRewindableParticleSystemTimeline(timeline, particleSystemComponent);
  209. particleSystem.Initialize();
  210. }
  211. components.Add(particleSystem);
  212. }
  213. else if (particleSystem != null && particleSystemComponent == null)
  214. {
  215. particleSystem = null;
  216. }
  217. // WindZone
  218. var windZoneComponent = GetComponent<WindZone>();
  219. if (windZone == null && windZoneComponent != null)
  220. {
  221. windZone = new WindZoneTimeline(timeline, windZoneComponent);
  222. windZone.Initialize();
  223. components.Add(windZone);
  224. }
  225. else if (windZone != null && windZoneComponent == null)
  226. {
  227. windZone = null;
  228. }
  229. // Terrain
  230. var terrainComponent = GetComponent<Terrain>();
  231. if (terrain == null && terrainComponent != null)
  232. {
  233. terrain = new TerrainTimeline(timeline, terrainComponent);
  234. terrain.Initialize();
  235. components.Add(terrain);
  236. }
  237. else if (terrain != null && terrainComponent == null)
  238. {
  239. terrain = null;
  240. }
  241. // TrailRenderer
  242. var trailRendererComponent = GetComponent<TrailRenderer>();
  243. if (trailRenderer == null && trailRendererComponent != null)
  244. {
  245. trailRenderer = new TrailRendererTimeline(timeline, trailRendererComponent);
  246. trailRenderer.Initialize();
  247. components.Add(trailRenderer);
  248. }
  249. else if (trailRenderer != null && trailRendererComponent == null)
  250. {
  251. trailRenderer = null;
  252. }
  253. // WheelCollider
  254. var wheelColliderComponent = GetComponent<WheelCollider>();
  255. if (wheelCollider == null && wheelColliderComponent != null)
  256. {
  257. wheelCollider = new WheelColliderTimeline(timeline, wheelColliderComponent);
  258. wheelCollider.Initialize();
  259. components.Add(wheelCollider);
  260. }
  261. else if (wheelCollider != null && wheelColliderComponent == null)
  262. {
  263. wheelCollider = null;
  264. }
  265. // Only activate one of Rigidbody / Rigidbody2D / Transform timelines at once
  266. var rigidbodyComponent = GetComponent<Rigidbody>();
  267. var rigidbody2DComponent = GetComponent<Rigidbody2D>();
  268. var transformComponent = GetComponent<Transform>();
  269. if (rigidbody == null && rigidbodyComponent != null)
  270. {
  271. rigidbody = new RigidbodyTimeline3D(timeline, rigidbodyComponent);
  272. rigidbody.Initialize();
  273. components.Add(rigidbody);
  274. rigidbody2D = null;
  275. transform = null;
  276. }
  277. else if (rigidbody2D == null && rigidbody2DComponent != null)
  278. {
  279. rigidbody2D = new RigidbodyTimeline2D(timeline, rigidbody2DComponent);
  280. rigidbody2D.Initialize();
  281. components.Add(rigidbody2D);
  282. rigidbody = null;
  283. transform = null;
  284. }
  285. else if (transform == null)
  286. {
  287. transform = new TransformTimeline(timeline, transformComponent);
  288. transform.Initialize();
  289. components.Add(transform);
  290. rigidbody = null;
  291. rigidbody2D = null;
  292. }
  293. }
  294. /// <summary>
  295. /// Attempts a full manual reset of the timeline. When using game object pooling, you should call this method after despawning or before spawning.
  296. /// </summary>
  297. public virtual void Reset()
  298. {
  299. for (int i = 0; i < components.Count; i++)
  300. {
  301. components[i].Reset();
  302. }
  303. }
  304. /// <summary>
  305. /// Resets the saved snapshots.
  306. /// </summary>
  307. public void ResetRecording()
  308. {
  309. recorder.Reset();
  310. }
  311. /// <summary>
  312. /// Estimate the memory usage in bytes from the storage of snapshots for the current recording duration and interval.
  313. /// </summary>
  314. public int EstimateMemoryUsage()
  315. {
  316. if (Application.isPlaying)
  317. {
  318. if (recorder == null)
  319. {
  320. return 0;
  321. }
  322. return recorder.EstimateMemoryUsage();
  323. }
  324. else
  325. {
  326. var timeline = GetComponent<Timeline>() ?? GetComponentInParent<Timeline>();
  327. if (!timeline.rewindable)
  328. {
  329. return 0;
  330. }
  331. if (GetComponent<Rigidbody>() != null)
  332. {
  333. return RigidbodyTimeline3D.EstimateMemoryUsage(timeline.recordingDuration, recordingInterval);
  334. }
  335. else if (GetComponent<Rigidbody2D>() != null)
  336. {
  337. return RigidbodyTimeline2D.EstimateMemoryUsage(timeline.recordingDuration, recordingInterval);
  338. }
  339. else if (GetComponent<Transform>() != null)
  340. {
  341. return TransformTimeline.EstimateMemoryUsage(timeline.recordingDuration, recordingInterval);
  342. }
  343. else
  344. {
  345. return 0;
  346. }
  347. }
  348. }
  349. #endregion
  350. }
  351. }