Files
aibis-dream/Assets/Scripts/FrameAnimation/Timeline/FrameAnimationTimelineClip.cs
T

77 lines
2.4 KiB
C#

using System;
using System.ComponentModel;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace AibisDream.FrameAnimation.Timeline
{
[Serializable]
[DisplayName("Frame Animation Clip")]
public sealed class FrameAnimationTimelineClip : PlayableAsset, ITimelineClipAsset
{
private const double FallbackDurationSeconds = 1d;
[SerializeField, Tooltip("要由 Timeline 播放的独立 FrameClip 资产。")]
private FrameClip frameClip;
[SerializeField, HideInInspector] private FrameClip synchronizedDurationSource;
[SerializeField, HideInInspector] private bool hasSynchronizedDuration;
public FrameClip FrameClip => frameClip;
public override double duration => GetEffectiveDuration(frameClip);
public ClipCaps clipCaps
{
get
{
var caps = ClipCaps.ClipIn | ClipCaps.SpeedMultiplier;
if (frameClip != null && frameClip.DefaultEndBehavior == FrameClipEndBehavior.Loop)
{
caps |= ClipCaps.Looping;
}
return caps;
}
}
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
var playable = ScriptPlayable<FrameAnimationTimelineBehaviour>.Create(graph);
playable.GetBehaviour().FrameClip = frameClip;
return playable;
}
internal void SetFrameClipForTests(FrameClip value)
{
frameClip = value;
}
internal bool TrySynchronizeSourceDuration(out double sourceDuration)
{
sourceDuration = duration;
if (hasSynchronizedDuration && synchronizedDurationSource == frameClip)
{
return false;
}
synchronizedDurationSource = frameClip;
hasSynchronizedDuration = true;
return true;
}
internal static double GetEffectiveDuration(FrameClip value)
{
if (value == null || value.TotalDurationMs <= 0 || value.Speed <= 0f)
{
return FallbackDurationSeconds;
}
return value.TotalDurationMs / 1000d / value.Speed;
}
}
internal sealed class FrameAnimationTimelineBehaviour : PlayableBehaviour
{
internal FrameClip FrameClip { get; set; }
}
}