using System.Collections.Generic;
using UnityEngine;
namespace Chronos
{
///
/// An internal base class that applies time effects from a timeline to most built-in Unity components.
///
public abstract class TimelineEffector : MonoBehaviour
{
protected const float DefaultRecordingInterval = 0.5f;
private bool enabledOnce;
public TimelineEffector()
{
components = new List();
audioSources = new List();
}
protected abstract Timeline timeline { get; }
protected virtual void Awake()
{
CacheComponents();
}
private void Start()
{
OnStartOrReEnable();
}
private void OnEnable()
{
if (enabledOnce)
{
OnStartOrReEnable();
}
else
{
enabledOnce = true;
}
}
protected virtual void OnStartOrReEnable()
{
for (int i = 0; i < components.Count; i++)
{
var component = components[i];
component.AdjustProperties();
component.OnStartOrReEnable();
}
}
protected virtual void Update()
{
for (int i = 0; i < components.Count; i++)
{
components[i].Update();
}
}
protected virtual void FixedUpdate()
{
for (int i = 0; i < components.Count; i++)
{
components[i].FixedUpdate();
}
}
protected virtual void OnDisable()
{
for (int i = 0; i < components.Count; i++)
{
components[i].OnDisable();
}
}
#region Rewinding / Recording
[SerializeField]
private float _recordingInterval = DefaultRecordingInterval;
///
/// The interval in seconds at which snapshots will be recorder. Lower values offer more rewind precision but require more memory.
///
public float recordingInterval
{
get { return _recordingInterval; }
set
{
_recordingInterval = value;
if (recorder != null)
{
recorder.Reset();
}
}
}
#endregion
#region Components
internal List components;
public new AnimationTimeline animation { get; protected set; }
public AnimatorTimeline animator { get; protected set; }
public List audioSources { get; protected set; }
public AudioSourceTimeline audioSource { get; protected set; }
public NavMeshAgentTimeline navMeshAgent { get; protected set; }
public new IParticleSystemTimeline particleSystem { get; protected set; }
public new RigidbodyTimeline3D rigidbody { get; protected set; }
public new RigidbodyTimeline2D rigidbody2D { get; protected set; }
public new TransformTimeline transform { get; protected set; }
public WindZoneTimeline windZone { get; protected set; }
public TerrainTimeline terrain { get; protected set; }
public TrailRendererTimeline trailRenderer { get; protected set; }
public WheelColliderTimeline wheelCollider { get; protected set; }
protected IRecorder recorder
{
get
{
if (rigidbody != null) return rigidbody;
if (rigidbody2D != null) return rigidbody2D;
if (transform != null) return transform;
return null;
}
}
///
/// 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.
///
public virtual void CacheComponents()
{
components.Clear();
// Animator
var animatorComponent = GetComponent();
if (animator == null && animatorComponent != null)
{
animator = new AnimatorTimeline(timeline, animatorComponent);
animator.Initialize();
components.Add(animator);
}
else if (animator != null && animatorComponent == null)
{
animator = null;
}
// Animation
var animationComponent = GetComponent();
if (animation == null && animationComponent != null)
{
animation = new AnimationTimeline(timeline, animationComponent);
animation.Initialize();
components.Add(animation);
}
else if (animation != null && animationComponent == null)
{
animation = null;
}
// AudioSources
var audioSourceComponents = GetComponents();
// Remove timelines for absent components
for (int i = 0; i < audioSources.Count; i++)
{
var audioSource = audioSources[i];
bool audioSourceComponentExists = false;
for (int j = 0; j < audioSourceComponents.Length; j++)
{
var audioSourceComponent = audioSourceComponents[j];
if (audioSource.component == audioSourceComponent)
{
audioSourceComponentExists = true;
break;
}
}
if (!audioSourceComponentExists)
{
audioSources.Remove(audioSource);
}
}
// Add timelines for new components
for (int i = 0; i < audioSourceComponents.Length; i++)
{
var audioSourceComponent = audioSourceComponents[i];
bool audioSourceExists = false;
for (int j = 0; j < audioSources.Count; j++)
{
var audioSource = audioSources[j];
if (audioSource.component == audioSourceComponent)
{
audioSourceExists = true;
break;
}
}
if (!audioSourceExists)
{
var newAudioSource = new AudioSourceTimeline(timeline, audioSourceComponent);
newAudioSource.Initialize();
audioSources.Add(newAudioSource);
components.Add(newAudioSource);
}
}
this.audioSource = audioSources.Count > 0 ? audioSources[0] : null;
// NavMeshAgent
var navMeshAgentComponent = GetComponent();
if (navMeshAgent == null && navMeshAgentComponent != null)
{
navMeshAgent = new NavMeshAgentTimeline(timeline, navMeshAgentComponent);
navMeshAgent.Initialize();
components.Add(navMeshAgent);
}
else if (animation != null && navMeshAgentComponent == null)
{
navMeshAgent = null;
}
// ParticleSystem
var particleSystemComponent = GetComponent();
if (particleSystem == null && particleSystemComponent != null)
{
if (timeline.rewindable)
{
particleSystem = new RewindableParticleSystemTimeline(timeline, particleSystemComponent);
particleSystem.Initialize();
}
else
{
particleSystem = new NonRewindableParticleSystemTimeline(timeline, particleSystemComponent);
particleSystem.Initialize();
}
components.Add(particleSystem);
}
else if (particleSystem != null && particleSystemComponent == null)
{
particleSystem = null;
}
// WindZone
var windZoneComponent = GetComponent();
if (windZone == null && windZoneComponent != null)
{
windZone = new WindZoneTimeline(timeline, windZoneComponent);
windZone.Initialize();
components.Add(windZone);
}
else if (windZone != null && windZoneComponent == null)
{
windZone = null;
}
// Terrain
var terrainComponent = GetComponent();
if (terrain == null && terrainComponent != null)
{
terrain = new TerrainTimeline(timeline, terrainComponent);
terrain.Initialize();
components.Add(terrain);
}
else if (terrain != null && terrainComponent == null)
{
terrain = null;
}
// TrailRenderer
var trailRendererComponent = GetComponent();
if (trailRenderer == null && trailRendererComponent != null)
{
trailRenderer = new TrailRendererTimeline(timeline, trailRendererComponent);
trailRenderer.Initialize();
components.Add(trailRenderer);
}
else if (trailRenderer != null && trailRendererComponent == null)
{
trailRenderer = null;
}
// WheelCollider
var wheelColliderComponent = GetComponent();
if (wheelCollider == null && wheelColliderComponent != null)
{
wheelCollider = new WheelColliderTimeline(timeline, wheelColliderComponent);
wheelCollider.Initialize();
components.Add(wheelCollider);
}
else if (wheelCollider != null && wheelColliderComponent == null)
{
wheelCollider = null;
}
// Only activate one of Rigidbody / Rigidbody2D / Transform timelines at once
var rigidbodyComponent = GetComponent();
var rigidbody2DComponent = GetComponent();
var transformComponent = GetComponent();
if (rigidbody == null && rigidbodyComponent != null)
{
rigidbody = new RigidbodyTimeline3D(timeline, rigidbodyComponent);
rigidbody.Initialize();
components.Add(rigidbody);
rigidbody2D = null;
transform = null;
}
else if (rigidbody2D == null && rigidbody2DComponent != null)
{
rigidbody2D = new RigidbodyTimeline2D(timeline, rigidbody2DComponent);
rigidbody2D.Initialize();
components.Add(rigidbody2D);
rigidbody = null;
transform = null;
}
else if (transform == null)
{
transform = new TransformTimeline(timeline, transformComponent);
transform.Initialize();
components.Add(transform);
rigidbody = null;
rigidbody2D = null;
}
}
///
/// Attempts a full manual reset of the timeline. When using game object pooling, you should call this method after despawning or before spawning.
///
public virtual void Reset()
{
for (int i = 0; i < components.Count; i++)
{
components[i].Reset();
}
}
///
/// Resets the saved snapshots.
///
public void ResetRecording()
{
recorder.Reset();
}
///
/// Estimate the memory usage in bytes from the storage of snapshots for the current recording duration and interval.
///
public int EstimateMemoryUsage()
{
if (Application.isPlaying)
{
if (recorder == null)
{
return 0;
}
return recorder.EstimateMemoryUsage();
}
else
{
var timeline = GetComponent() ?? GetComponentInParent();
if (!timeline.rewindable)
{
return 0;
}
if (GetComponent() != null)
{
return RigidbodyTimeline3D.EstimateMemoryUsage(timeline.recordingDuration, recordingInterval);
}
else if (GetComponent() != null)
{
return RigidbodyTimeline2D.EstimateMemoryUsage(timeline.recordingDuration, recordingInterval);
}
else if (GetComponent() != null)
{
return TransformTimeline.EstimateMemoryUsage(timeline.recordingDuration, recordingInterval);
}
else
{
return 0;
}
}
}
#endregion
}
}