using System; using System.Linq; using System.Collections.Generic; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; namespace UnityEditor.Timeline { /// /// The user-defined options for drawing a track." /// public struct TrackDrawOptions { /// /// Text that indicates if the track should display an error. /// /// /// If the error text is not empty or null, then the track displays a warning. The error text is used as the tooltip. /// public string errorText { get; set; } /// /// The highlight color of the track. /// public Color trackColor { get; set; } /// /// The minimum height of the track. /// public float minimumHeight { get; set; } /// /// The icon displayed on the track header. /// /// /// If this value is null, then the default icon for the track is used. /// public Texture2D icon { get; set; } public override bool Equals(object obj) { if (!(obj is TrackDrawOptions)) return false; return Equals((TrackDrawOptions)obj); } public bool Equals(TrackDrawOptions other) { return errorText == other.errorText && trackColor == other.trackColor && minimumHeight == other.minimumHeight && icon == other.icon; } public override int GetHashCode() { return HashUtility.CombineHash( errorText != null ? errorText.GetHashCode() : 0, trackColor.GetHashCode(), minimumHeight.GetHashCode(), icon != null ? icon.GetHashCode() : 0 ); } public static bool operator==(TrackDrawOptions options1, TrackDrawOptions options2) { return options1.Equals(options2); } public static bool operator!=(TrackDrawOptions options1, TrackDrawOptions options2) { return !options1.Equals(options2); } } /// /// The errors displayed for the track binding. /// public enum TrackBindingErrors { /// /// Select no errors. /// None = 0, /// /// The bound GameObject is disabled. /// BoundGameObjectDisabled = 1 << 0, /// /// The bound GameObject does not have a valid component. /// NoValidComponent = 1 << 1, /// /// The bound Object is a disabled Behaviour. /// BehaviourIsDisabled = 1 << 2, /// /// The bound Object is not of the correct type. /// InvalidBinding = 1 << 3, /// /// The bound Object is part of a prefab, and not an instance. /// PrefabBound = 1 << 4, /// /// Select all errors. /// All = Int32.MaxValue } /// /// Use this class to customize track types in the TimelineEditor. /// public class TrackEditor { static readonly string k_BoundGameObjectDisabled = LocalizationDatabase.GetLocalizedString("The bound GameObject is disabled."); static readonly string k_NoValidComponent = LocalizationDatabase.GetLocalizedString("Could not find appropriate component on this gameObject"); static readonly string k_RequiredComponentIsDisabled = LocalizationDatabase.GetLocalizedString("The component is disabled"); static readonly string k_InvalidBinding = LocalizationDatabase.GetLocalizedString("The bound object is not the correct type."); static readonly string k_PrefabBound = LocalizationDatabase.GetLocalizedString("The bound object is a Prefab"); readonly Dictionary m_BindingCache = new Dictionary(); /// /// The default height of a track. /// public static readonly float DefaultTrackHeight = 30.0f; /// /// The minimum unscaled height of a track. /// public static readonly float MinimumTrackHeight = 10.0f; /// /// The maximum height of a track. /// public static readonly float MaximumTrackHeight = 256.0f; /// /// Implement this method to override the default options for drawing a track. /// /// The track from which track options are retrieved. /// The binding for the track. /// The options for drawing the track. public virtual TrackDrawOptions GetTrackOptions(TrackAsset track, UnityEngine.Object binding) { return new TrackDrawOptions() { errorText = GetErrorText(track, binding, TrackBindingErrors.All), minimumHeight = DefaultTrackHeight, trackColor = GetTrackColor(track), icon = null }; } /// /// Gets the error text for the specified track. /// /// The track to retrieve options for. /// The binding for the track. /// The errors to check for. /// An error to be displayed on the track, or string.Empty if there is no error. public string GetErrorText(TrackAsset track, UnityEngine.Object boundObject, TrackBindingErrors detectErrors) { if (track == null || boundObject == null) return string.Empty; var bindingType = GetBindingType(track); if (bindingType != null) { // bound to a prefab asset if (HasFlag(detectErrors, TrackBindingErrors.PrefabBound) && PrefabUtility.IsPartOfPrefabAsset(boundObject)) { return k_PrefabBound; } // If we are a component, allow for bound game objects (legacy) if (typeof(Component).IsAssignableFrom(bindingType)) { var gameObject = boundObject as GameObject; var component = boundObject as Component; if (component != null) gameObject = component.gameObject; // game object is bound with no component if (HasFlag(detectErrors, TrackBindingErrors.NoValidComponent) && gameObject != null && component == null) { component = gameObject.GetComponent(bindingType); if (component == null) { return k_NoValidComponent; } } // attached gameObject is disables (ignores Activation Track) if (HasFlag(detectErrors, TrackBindingErrors.BoundGameObjectDisabled) && gameObject != null && !gameObject.activeInHierarchy) { return k_BoundGameObjectDisabled; } // component is disabled var behaviour = component as Behaviour; if (HasFlag(detectErrors, TrackBindingErrors.BehaviourIsDisabled) && behaviour != null && !behaviour.enabled) { return k_RequiredComponentIsDisabled; } // mismatched binding if (HasFlag(detectErrors, TrackBindingErrors.InvalidBinding) && component != null && !bindingType.IsAssignableFrom(component.GetType())) { return k_InvalidBinding; } } // Mismatched binding (non-component) else if (HasFlag(detectErrors, TrackBindingErrors.InvalidBinding) && !bindingType.IsAssignableFrom(boundObject.GetType())) { return k_InvalidBinding; } } return string.Empty; } /// /// Gets the color information of a track. /// /// /// Returns the color for the specified track. public Color GetTrackColor(TrackAsset track) { return TrackResourceCache.GetTrackColor(track); } /// /// Gets the binding type for a track. /// /// The track to retrieve the binding type from. /// Returns the binding type for the specified track. Returns null if the track does not have binding. public System.Type GetBindingType(TrackAsset track) { if (track == null) return null; System.Type result = null; if (m_BindingCache.TryGetValue(track, out result)) return result; result = track.outputs.Select(x => x.outputTargetType).FirstOrDefault(); m_BindingCache[track] = result; return result; } /// /// Callback for when a track is created. /// /// The track that is created. /// The source that the track is copied from. This can be set to null if the track is not a copy. public virtual void OnCreate(TrackAsset track, TrackAsset copiedFrom) { } /// /// Callback for when a track is changed. /// /// The track that is changed. public virtual void OnTrackChanged(TrackAsset track) { } private static bool HasFlag(TrackBindingErrors errors, TrackBindingErrors flag) { return (errors & flag) != 0; } } }