319 lines
9.8 KiB
C#
319 lines
9.8 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using AibisDream.Kit;
|
|
using AibisDream.SaveSystem;
|
|
using UnityEngine;
|
|
using Yarn.Unity;
|
|
|
|
namespace AibisDream
|
|
{
|
|
public class ActorManager : Singleton<ActorManager>
|
|
{
|
|
#region 索引
|
|
|
|
private Transform _actorRoot;
|
|
private Dictionary<string, ActorSlot> _slotDict;
|
|
private Dictionary<string, AbsActor> _actorDict;
|
|
|
|
private ActorFactory _actorFactory;
|
|
|
|
#endregion
|
|
|
|
public override void OnSingletonInit()
|
|
{
|
|
InitRefs();
|
|
DestroyEditorTemp();
|
|
InitDefaultActor();
|
|
}
|
|
|
|
public void DestroyEditorTemp()
|
|
{
|
|
foreach (Transform child in _actorRoot)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
DestroyImmediate(child.gameObject);
|
|
continue;
|
|
}
|
|
#endif
|
|
Destroy(child.gameObject);
|
|
}
|
|
|
|
_actorDict.Clear();
|
|
}
|
|
|
|
private void InitDefaultActor()
|
|
{
|
|
foreach (var slot in _slotDict.Values.Where(slot => slot.hasDefaultActor))
|
|
{
|
|
if (slot.defaultActorType == ActorType.FrameAnimation)
|
|
{
|
|
StartCoroutine(InitActorAsync(
|
|
slot.defaultActorName,
|
|
slot.slotName,
|
|
slot.defaultActorType));
|
|
}
|
|
else
|
|
{
|
|
InitActor(slot.defaultActorName, slot.slotName, slot.defaultActorType);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void InitRefs()
|
|
{
|
|
_actorRoot = transform.Find("ActorRoot");
|
|
_slotDict = new Dictionary<string, ActorSlot>();
|
|
foreach (var child in transform.Find("Slots").GetComponentsInChildren<ActorSlot>())
|
|
{
|
|
_slotDict[child.slotName] = child;
|
|
}
|
|
|
|
_actorDict = new Dictionary<string, AbsActor>();
|
|
_actorFactory = new ActorFactory(_actorRoot);
|
|
}
|
|
|
|
public ActorSnapshotDto CaptureSnapshot()
|
|
{
|
|
var dto = new ActorSnapshotDto();
|
|
foreach (var actor in _actorDict.Values)
|
|
{
|
|
dto.actors.Add(actor.CaptureEntry());
|
|
}
|
|
|
|
return dto;
|
|
}
|
|
|
|
public IEnumerator RestoreSnapshotAsync(ActorSnapshotDto dto)
|
|
{
|
|
DestroyEditorTemp();
|
|
|
|
if (dto?.actors == null)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
foreach (var entry in dto.actors)
|
|
{
|
|
if (!_slotDict.TryGetValue(entry.slotName, out var slot))
|
|
{
|
|
Debug.LogWarning($"slot {entry.slotName} 不存在");
|
|
continue;
|
|
}
|
|
|
|
AbsActor actor = null;
|
|
yield return _actorFactory.CreateFromEntryAsync(
|
|
entry,
|
|
slot,
|
|
created => actor = created);
|
|
if (actor == null) continue;
|
|
|
|
UpsertActor(entry.actorName, actor);
|
|
yield return actor.ApplyEntryAsync(entry);
|
|
}
|
|
}
|
|
|
|
private void UpsertActor(string actorName, AbsActor actor)
|
|
{
|
|
if (_actorDict.TryGetValue(actorName, out var existing) && existing != null)
|
|
{
|
|
Destroy(existing.gameObject);
|
|
}
|
|
|
|
_actorDict[actorName] = actor;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
public void InitAllDefault()
|
|
{
|
|
InitRefs();
|
|
DestroyEditorTemp();
|
|
foreach (var slot in _slotDict.Values)
|
|
{
|
|
if (!string.IsNullOrEmpty(slot.defaultActorName))
|
|
{
|
|
if (slot.defaultActorType == ActorType.FrameAnimation)
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
StartCoroutine(InitActorAsync(
|
|
slot.defaultActorName,
|
|
slot.slotName,
|
|
slot.defaultActorType,
|
|
actor => actor?.Show()));
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("FrameAnimation 默认角色只能在 Play Mode 中异步加载。");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var actor = InitActor(slot.defaultActorName, slot.slotName, slot.defaultActorType);
|
|
actor?.Show();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.Log($"slot {slot.slotName} 没有默认角色");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
public AbsActor InitActor(string actorName, string slotName, ActorType actorType)
|
|
{
|
|
if (actorType == ActorType.FrameAnimation)
|
|
{
|
|
Debug.LogError(
|
|
$"FrameAnimation 角色 '{actorName}' 必须通过 InitActorAsync 初始化。");
|
|
return null;
|
|
}
|
|
|
|
var slot = _slotDict[slotName];
|
|
var actor = _actorFactory.CreateActor(actorName, slot, actorType);
|
|
UpsertActor(actorName, actor);
|
|
|
|
return actor;
|
|
}
|
|
|
|
public IEnumerator InitActorAsync(
|
|
string actorName,
|
|
string slotName,
|
|
ActorType actorType,
|
|
Action<AbsActor> onCompleted = null)
|
|
{
|
|
if (!_slotDict.TryGetValue(slotName, out var slot))
|
|
{
|
|
Debug.LogError(
|
|
$"[ActorManager] 初始化角色 '{actorName}' 失败:slot '{slotName}' 不存在。");
|
|
onCompleted?.Invoke(null);
|
|
yield break;
|
|
}
|
|
|
|
AbsActor actor = null;
|
|
yield return _actorFactory.CreateActorAsync(
|
|
actorName,
|
|
slot,
|
|
actorType,
|
|
created => actor = created);
|
|
if (actor == null)
|
|
{
|
|
onCompleted?.Invoke(null);
|
|
yield break;
|
|
}
|
|
|
|
UpsertActor(actorName, actor);
|
|
onCompleted?.Invoke(actor);
|
|
}
|
|
|
|
public bool TryFindActor(string actorOrSlot, out AbsActor actor)
|
|
{
|
|
if (_actorDict.TryGetValue(actorOrSlot, out actor))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
actor = _actorDict.Values.FirstOrDefault(item => item.SlotName == actorOrSlot);
|
|
return actor != null;
|
|
}
|
|
|
|
public void ChangeSlot(string actorOrSlot, string targetSlot)
|
|
{
|
|
if (!_slotDict.TryGetValue(targetSlot, out var targetSlotObj)) return;
|
|
if (!TryFindActor(actorOrSlot, out var actor)) return;
|
|
actor.SetSlot(targetSlotObj);
|
|
}
|
|
}
|
|
|
|
public static class ActorYarnCommand
|
|
{
|
|
private const string ClinicSlotName = "clinic";
|
|
|
|
[YarnCommand("init_actor")]
|
|
public static IEnumerator InitActor(string actorName, string slotName = ClinicSlotName, string actorType = "Anima")
|
|
{
|
|
if (Enum.TryParse<ActorType>(actorType, out var actorTypeEnum))
|
|
{
|
|
yield return ActorManager.Instance.InitActorAsync(actorName, slotName, actorTypeEnum);
|
|
}
|
|
else
|
|
{
|
|
Debug.Log($"Actor类型{actorType}有问题");
|
|
}
|
|
}
|
|
|
|
[YarnCommand("change_actor_state")]
|
|
public static void PlayAnima(string animaName, string slotOrActorName = ClinicSlotName)
|
|
{
|
|
if (ActorManager.Instance.TryFindActor(slotOrActorName, out var actor))
|
|
{
|
|
actor.ChangeState(animaName);
|
|
}
|
|
}
|
|
|
|
[YarnCommand("change_actor_state_async")]
|
|
public static IEnumerator PlayAnimaAsync(string animaName, string slotOrActorName = ClinicSlotName)
|
|
{
|
|
if (ActorManager.Instance.TryFindActor(slotOrActorName, out var actor))
|
|
{
|
|
yield return actor.ChangeStateAsync(animaName);
|
|
}
|
|
}
|
|
|
|
[YarnCommand("fade_in_actor")]
|
|
public static IEnumerator ActorFadeIn(string actorName = ClinicSlotName, float duration = 1)
|
|
{
|
|
if (ActorManager.Instance.TryFindActor(actorName, out var actor))
|
|
{
|
|
yield return actor.FadeInAsync(duration);
|
|
}
|
|
}
|
|
|
|
[YarnCommand("fade_out_actor")]
|
|
public static IEnumerator ActorFadeOut(string actorName = ClinicSlotName, float duration = 1)
|
|
{
|
|
if (ActorManager.Instance.TryFindActor(actorName, out var actor))
|
|
{
|
|
yield return actor.FadeOutAsync(duration);
|
|
}
|
|
}
|
|
|
|
[YarnCommand("change_actor_slot")]
|
|
public static void ChangeSlot(string actorOrSlot, string targetSlot)
|
|
{
|
|
ActorManager.Instance.ChangeSlot(actorOrSlot, targetSlot);
|
|
}
|
|
|
|
[YarnCommand("show_actor")]
|
|
public static void ShowActor(string actorName = ClinicSlotName)
|
|
{
|
|
if (ActorManager.Instance.TryFindActor(actorName, out var actor))
|
|
{
|
|
actor.Show();
|
|
}
|
|
}
|
|
|
|
[YarnCommand("hide_actor")]
|
|
public static void HideActor(string actorName = ClinicSlotName)
|
|
{
|
|
if (ActorManager.Instance.TryFindActor(actorName, out var actor))
|
|
{
|
|
actor.Hide();
|
|
}
|
|
}
|
|
|
|
[YarnCommand("set_actor_face")]
|
|
public static void SetFaceState(string faceName, string actorName = ClinicSlotName)
|
|
{
|
|
if (ActorManager.Instance.TryFindActor(actorName, out var actor))
|
|
{
|
|
actor.SetFaceState(faceName);
|
|
}
|
|
}
|
|
}
|
|
}
|