feat: FlowAsync调整
This commit is contained in:
@@ -90,12 +90,15 @@ namespace AibisDream.FrameAnimation
|
||||
[SerializeField] private string id = string.Empty;
|
||||
[SerializeField] private string displayName = string.Empty;
|
||||
[SerializeField] private string entryNodeId = string.Empty;
|
||||
[SerializeField] private AnimationFlowAsyncCompletionMode asyncCompletionMode =
|
||||
AnimationFlowAsyncCompletionMode.CompleteOnTerminalLoopStart;
|
||||
[SerializeField] private bool hasEndBehaviorOverride;
|
||||
[SerializeField] private FrameClipEndBehavior endBehaviorOverride;
|
||||
|
||||
public string Id => id;
|
||||
public string DisplayName => displayName;
|
||||
public string EntryNodeId => entryNodeId;
|
||||
public AnimationFlowAsyncCompletionMode AsyncCompletionMode => asyncCompletionMode;
|
||||
public FrameClipEndBehavior? EndBehaviorOverride =>
|
||||
hasEndBehaviorOverride ? endBehaviorOverride : (FrameClipEndBehavior?)null;
|
||||
|
||||
@@ -107,11 +110,14 @@ namespace AibisDream.FrameAnimation
|
||||
string id,
|
||||
string displayName,
|
||||
string entryNodeId,
|
||||
FrameClipEndBehavior? endBehaviorOverride = null)
|
||||
FrameClipEndBehavior? endBehaviorOverride = null,
|
||||
AnimationFlowAsyncCompletionMode asyncCompletionMode =
|
||||
AnimationFlowAsyncCompletionMode.CompleteOnTerminalLoopStart)
|
||||
{
|
||||
this.id = id ?? string.Empty;
|
||||
this.displayName = displayName ?? this.id;
|
||||
this.entryNodeId = entryNodeId ?? string.Empty;
|
||||
this.asyncCompletionMode = asyncCompletionMode;
|
||||
SetEndBehaviorOverride(endBehaviorOverride);
|
||||
}
|
||||
|
||||
@@ -135,6 +141,11 @@ namespace AibisDream.FrameAnimation
|
||||
{
|
||||
entryNodeId = value ?? string.Empty;
|
||||
}
|
||||
|
||||
internal void SetAsyncCompletionMode(AnimationFlowAsyncCompletionMode value)
|
||||
{
|
||||
asyncCompletionMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -5,6 +5,25 @@ using UnityEngine;
|
||||
|
||||
namespace AibisDream.FrameAnimation
|
||||
{
|
||||
public sealed class FrameAnimationPlaybackAwaitable : CustomYieldInstruction
|
||||
{
|
||||
private readonly FrameAnimationPlayer player;
|
||||
|
||||
public FrameAnimationPlaybackHandle PlaybackHandle { get; }
|
||||
|
||||
internal FrameAnimationPlaybackAwaitable(
|
||||
FrameAnimationPlayer player,
|
||||
FrameAnimationPlaybackHandle playbackHandle)
|
||||
{
|
||||
this.player = player;
|
||||
PlaybackHandle = playbackHandle ??
|
||||
throw new ArgumentNullException(nameof(playbackHandle));
|
||||
}
|
||||
|
||||
public override bool keepWaiting =>
|
||||
player != null && player.ShouldWaitForAsyncCompletion(PlaybackHandle);
|
||||
}
|
||||
|
||||
public sealed class FrameAnimationPlaybackHandle : CustomYieldInstruction
|
||||
{
|
||||
private readonly TaskCompletionSource<FrameAnimationPlaybackResult> completionSource =
|
||||
|
||||
@@ -97,6 +97,31 @@ namespace AibisDream.FrameAnimation
|
||||
public int CurrentFrameIndex => hasStarted && !hasCompleted ? frameIndex : -1;
|
||||
public FrameClip CurrentClip => CurrentStep?.Clip;
|
||||
internal bool HasCompletedFirstTerminalLoopCycle => hasCompletedFirstTerminalLoopCycle;
|
||||
internal bool HasReachedAsyncCompletionPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!hasStarted || plan.Steps == null || plan.Steps.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var terminalStep = plan.Steps[plan.Steps.Count - 1];
|
||||
if (terminalStep.TerminalEndBehavior != FrameClipEndBehavior.Loop)
|
||||
{
|
||||
return hasCompleted;
|
||||
}
|
||||
|
||||
if (!plan.IsFlow ||
|
||||
plan.AsyncCompletionMode ==
|
||||
AnimationFlowAsyncCompletionMode.WaitForTerminalLoopFirstCycle)
|
||||
{
|
||||
return hasCompletedFirstTerminalLoopCycle;
|
||||
}
|
||||
|
||||
return stepIndex == plan.Steps.Count - 1;
|
||||
}
|
||||
}
|
||||
internal FrameAnimationPlaybackSnapshot Snapshot =>
|
||||
new FrameAnimationPlaybackSnapshot(
|
||||
hasStarted,
|
||||
|
||||
@@ -138,18 +138,22 @@ namespace AibisDream.FrameAnimation
|
||||
return PlayInternal(playableId, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待当前播放请求完成首轮。非循环请求等待自然结束;终点为 Loop 的请求在
|
||||
/// 第一次回绕后结束等待,但播放请求本身保持活动并继续循环。
|
||||
/// </summary>
|
||||
public CustomYieldInstruction WaitForFirstPass(FrameAnimationPlaybackHandle playbackHandle)
|
||||
public FrameAnimationPlaybackAwaitable PlayAsync()
|
||||
{
|
||||
if (playbackHandle == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(playbackHandle));
|
||||
}
|
||||
var playableId = graph?.Settings?.DefaultPlayableId ?? string.Empty;
|
||||
return PlayAsyncInternal(playableId, default);
|
||||
}
|
||||
|
||||
return new FirstPassYieldInstruction(this, playbackHandle);
|
||||
public FrameAnimationPlaybackAwaitable PlayAsync(string playableId)
|
||||
{
|
||||
return PlayAsyncInternal(playableId, default);
|
||||
}
|
||||
|
||||
public FrameAnimationPlaybackAwaitable PlayAsync(
|
||||
string playableId,
|
||||
FrameAnimationPlayOptions options)
|
||||
{
|
||||
return PlayAsyncInternal(playableId, options);
|
||||
}
|
||||
|
||||
public bool TryBindGraph(
|
||||
@@ -404,6 +408,15 @@ namespace AibisDream.FrameAnimation
|
||||
return handle;
|
||||
}
|
||||
|
||||
private FrameAnimationPlaybackAwaitable PlayAsyncInternal(
|
||||
string playableId,
|
||||
FrameAnimationPlayOptions options)
|
||||
{
|
||||
return new FrameAnimationPlaybackAwaitable(
|
||||
this,
|
||||
PlayInternal(playableId, options));
|
||||
}
|
||||
|
||||
private bool TryCreateTarget(
|
||||
out IFrameAnimationTarget resolvedTarget,
|
||||
out FrameAnimationPlaybackError error)
|
||||
@@ -527,31 +540,15 @@ namespace AibisDream.FrameAnimation
|
||||
private Action<Sprite> ApplySpriteCallback =>
|
||||
applySpriteCallback ??= ApplySprite;
|
||||
|
||||
private bool ShouldWaitForFirstPass(FrameAnimationPlaybackHandle playbackHandle)
|
||||
internal bool ShouldWaitForAsyncCompletion(
|
||||
FrameAnimationPlaybackHandle playbackHandle)
|
||||
{
|
||||
if (playbackHandle.IsCompleted || activeHandle != playbackHandle || session == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !session.HasCompletedFirstTerminalLoopCycle;
|
||||
}
|
||||
|
||||
private sealed class FirstPassYieldInstruction : CustomYieldInstruction
|
||||
{
|
||||
private readonly FrameAnimationPlayer player;
|
||||
private readonly FrameAnimationPlaybackHandle playbackHandle;
|
||||
|
||||
public FirstPassYieldInstruction(
|
||||
FrameAnimationPlayer player,
|
||||
FrameAnimationPlaybackHandle playbackHandle)
|
||||
{
|
||||
this.player = player;
|
||||
this.playbackHandle = playbackHandle;
|
||||
}
|
||||
|
||||
public override bool keepWaiting =>
|
||||
player != null && player.ShouldWaitForFirstPass(playbackHandle);
|
||||
return !session.HasReachedAsyncCompletionPoint;
|
||||
}
|
||||
|
||||
private void ApplyEndBehavior(FrameClipEndBehavior endBehavior)
|
||||
|
||||
@@ -28,12 +28,19 @@ namespace AibisDream.FrameAnimation
|
||||
public string PlayableId { get; }
|
||||
public bool IsFlow { get; }
|
||||
public IReadOnlyList<ResolvedPlaybackStep> Steps { get; }
|
||||
public AnimationFlowAsyncCompletionMode AsyncCompletionMode { get; }
|
||||
|
||||
public ResolvedPlaybackPlan(string playableId, bool isFlow, IReadOnlyList<ResolvedPlaybackStep> steps)
|
||||
public ResolvedPlaybackPlan(
|
||||
string playableId,
|
||||
bool isFlow,
|
||||
IReadOnlyList<ResolvedPlaybackStep> steps,
|
||||
AnimationFlowAsyncCompletionMode asyncCompletionMode =
|
||||
AnimationFlowAsyncCompletionMode.WaitForTerminalLoopFirstCycle)
|
||||
{
|
||||
PlayableId = playableId;
|
||||
IsFlow = isFlow;
|
||||
Steps = steps;
|
||||
AsyncCompletionMode = asyncCompletionMode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +285,11 @@ namespace AibisDream.FrameAnimation
|
||||
currentNodeId = edge.ToNodeId;
|
||||
}
|
||||
|
||||
plan = new ResolvedPlaybackPlan(flow.Id, true, steps);
|
||||
plan = new ResolvedPlaybackPlan(
|
||||
flow.Id,
|
||||
true,
|
||||
steps,
|
||||
flow.AsyncCompletionMode);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,12 @@ namespace AibisDream.FrameAnimation
|
||||
Always
|
||||
}
|
||||
|
||||
public enum AnimationFlowAsyncCompletionMode
|
||||
{
|
||||
CompleteOnTerminalLoopStart = 0,
|
||||
WaitForTerminalLoopFirstCycle = 1
|
||||
}
|
||||
|
||||
public enum FrameAnimationPlaybackState
|
||||
{
|
||||
Stopped,
|
||||
|
||||
@@ -95,12 +95,13 @@ namespace AibisDream
|
||||
|
||||
public override IEnumerator ChangeStateAsync(string stateName)
|
||||
{
|
||||
if (!TryPlay(stateName, false, out var handle))
|
||||
if (!TryPlayAsync(stateName, out var awaitable))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return _player.WaitForFirstPass(handle);
|
||||
yield return awaitable;
|
||||
var handle = awaitable.PlaybackHandle;
|
||||
if (handle.IsCompleted)
|
||||
{
|
||||
LogFailedResult(stateName, handle.Result);
|
||||
@@ -141,6 +142,50 @@ namespace AibisDream
|
||||
out FrameAnimationPlaybackHandle handle)
|
||||
{
|
||||
handle = null;
|
||||
if (!CanPlay(stateName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
handle = restoreTerminalState
|
||||
? _player.RestoreTerminalState(stateName)
|
||||
: _player.Play(stateName);
|
||||
if (handle.IsCompleted &&
|
||||
handle.Result.Reason == FrameAnimationCompletionReason.Failed)
|
||||
{
|
||||
LogFailedResult(stateName, handle.Result);
|
||||
return false;
|
||||
}
|
||||
|
||||
_currentStateName = stateName;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryPlayAsync(
|
||||
string stateName,
|
||||
out FrameAnimationPlaybackAwaitable awaitable)
|
||||
{
|
||||
awaitable = null;
|
||||
if (!CanPlay(stateName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
awaitable = _player.PlayAsync(stateName);
|
||||
var handle = awaitable.PlaybackHandle;
|
||||
if (handle.IsCompleted &&
|
||||
handle.Result.Reason == FrameAnimationCompletionReason.Failed)
|
||||
{
|
||||
LogFailedResult(stateName, handle.Result);
|
||||
return false;
|
||||
}
|
||||
|
||||
_currentStateName = stateName;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CanPlay(string stateName)
|
||||
{
|
||||
if (!_isReady || _player == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
@@ -157,17 +202,6 @@ namespace AibisDream
|
||||
return false;
|
||||
}
|
||||
|
||||
handle = restoreTerminalState
|
||||
? _player.RestoreTerminalState(stateName)
|
||||
: _player.Play(stateName);
|
||||
if (handle.IsCompleted &&
|
||||
handle.Result.Reason == FrameAnimationCompletionReason.Failed)
|
||||
{
|
||||
LogFailedResult(stateName, handle.Result);
|
||||
return false;
|
||||
}
|
||||
|
||||
_currentStateName = stateName;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user