225 lines
7.0 KiB
C#
225 lines
7.0 KiB
C#
using System.Collections;
|
||
using System.Linq;
|
||
using AibisDream.FrameAnimation;
|
||
using AibisDream.Framework;
|
||
using AibisDream.SaveSystem;
|
||
using AibisDream.Utility;
|
||
using UnityEngine;
|
||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||
|
||
namespace AibisDream
|
||
{
|
||
/// <summary>
|
||
/// 由 FrameAnimationGraph 驱动的 ActorManager 角色实现。
|
||
/// Graph 使用 FrameAnimation/{ActorName} 的场景级 Addressable key。
|
||
/// </summary>
|
||
public sealed class FrameAnimationActor : AbsActor
|
||
{
|
||
private FrameAnimationPlayer _player;
|
||
private string _currentStateName = string.Empty;
|
||
private bool _isReady;
|
||
|
||
public override bool IsReady => _isReady;
|
||
|
||
protected override void InitInternal()
|
||
{
|
||
_player = GetComponent<FrameAnimationPlayer>();
|
||
base.InitInternal();
|
||
_actorType = ActorType.FrameAnimation;
|
||
_isReady = false;
|
||
}
|
||
|
||
public override IEnumerator InitializeAsync()
|
||
{
|
||
if (_player == null)
|
||
{
|
||
Debug.LogError(
|
||
$"[FrameAnimationActor] 角色 '{ActorName}' 缺少 FrameAnimationPlayer。",
|
||
this);
|
||
yield break;
|
||
}
|
||
|
||
var graphKey = ConstRef.FrameAnimationGraphPrefix + ActorName;
|
||
var handle = ResourceSystem.LoadSceneAsync<FrameAnimationGraph>(graphKey);
|
||
if (!handle.IsValid())
|
||
{
|
||
Debug.LogError(
|
||
$"[FrameAnimationActor] 角色 '{ActorName}' 无法开始加载 Graph,key='{graphKey}'。",
|
||
this);
|
||
yield break;
|
||
}
|
||
|
||
yield return handle;
|
||
if (handle.Status != AsyncOperationStatus.Succeeded || handle.Result == null)
|
||
{
|
||
Debug.LogError(
|
||
$"[FrameAnimationActor] 角色 '{ActorName}' 的 Graph 加载失败,key='{graphKey}'。",
|
||
this);
|
||
yield break;
|
||
}
|
||
|
||
var validation = FrameAnimationGraphValidator.Validate(handle.Result);
|
||
if (validation.HasErrors)
|
||
{
|
||
var errors = string.Join(
|
||
" | ",
|
||
validation.Issues
|
||
.Where(issue => issue.Severity == FrameAnimationValidationSeverity.Error)
|
||
.Select(issue => $"{issue.Code}: {issue.Message}"));
|
||
Debug.LogError(
|
||
$"[FrameAnimationActor] 角色 '{ActorName}' 的 Graph 校验失败,key='{graphKey}':{errors}",
|
||
this);
|
||
yield break;
|
||
}
|
||
|
||
if (!_player.TryBindGraph(handle.Result, out var bindError))
|
||
{
|
||
Debug.LogError(
|
||
$"[FrameAnimationActor] 角色 '{ActorName}' 绑定 Graph 失败,key='{graphKey}', error={bindError}",
|
||
this);
|
||
yield break;
|
||
}
|
||
|
||
_isReady = true;
|
||
}
|
||
|
||
public override void ChangeState(string stateName)
|
||
{
|
||
if (!TryPlay(stateName, false, out var handle))
|
||
{
|
||
return;
|
||
}
|
||
|
||
handle.RegisterCompleted(result => LogFailedResult(stateName, result));
|
||
}
|
||
|
||
public override IEnumerator ChangeStateAsync(string stateName)
|
||
{
|
||
if (!TryPlayAsync(stateName, out var awaitable))
|
||
{
|
||
yield break;
|
||
}
|
||
|
||
yield return awaitable;
|
||
var handle = awaitable.PlaybackHandle;
|
||
if (handle.IsCompleted)
|
||
{
|
||
LogFailedResult(stateName, handle.Result);
|
||
}
|
||
}
|
||
|
||
public override ActorEntrySnapshotDto CaptureEntry()
|
||
{
|
||
var entry = new ActorEntrySnapshotDto();
|
||
FillBaseEntry(entry);
|
||
entry.stateName = _currentStateName;
|
||
return entry;
|
||
}
|
||
|
||
public override IEnumerator ApplyEntryAsync(ActorEntrySnapshotDto entry)
|
||
{
|
||
ApplyAlphaFromEntry(entry);
|
||
if (string.IsNullOrWhiteSpace(entry.stateName))
|
||
{
|
||
yield break;
|
||
}
|
||
|
||
if (!TryPlay(entry.stateName, true, out var handle))
|
||
{
|
||
yield break;
|
||
}
|
||
|
||
// 非循环终态会同步完成;Loop 会继续播放,读档 Barrier 不等待它。
|
||
if (handle.IsCompleted)
|
||
{
|
||
LogFailedResult(entry.stateName, handle.Result);
|
||
}
|
||
}
|
||
|
||
private bool TryPlay(
|
||
string stateName,
|
||
bool restoreTerminalState,
|
||
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(
|
||
$"[FrameAnimationActor] 角色 '{ActorName}' 尚未完成初始化,playable='{stateName}'。",
|
||
this);
|
||
return false;
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(stateName))
|
||
{
|
||
Debug.LogWarning(
|
||
$"[FrameAnimationActor] 角色 '{ActorName}' 收到空 playable id。",
|
||
this);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private void LogFailedResult(
|
||
string stateName,
|
||
FrameAnimationPlaybackResult result)
|
||
{
|
||
if (result.Reason != FrameAnimationCompletionReason.Failed)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var graphKey = ConstRef.FrameAnimationGraphPrefix + ActorName;
|
||
Debug.LogError(
|
||
$"[FrameAnimationActor] 角色 '{ActorName}' 播放失败,Graph='{graphKey}', " +
|
||
$"playable='{stateName}', error={result.Error}",
|
||
this);
|
||
}
|
||
}
|
||
}
|