Files
aibis-dream/Assets/Scripts/SceneManagement/SceneCenter/SubwayCenter.cs
T
2026-07-22 00:07:54 +08:00

229 lines
7.0 KiB
C#

using System;
using System.Collections;
using AibisDream.Framework;
using AibisDream.Kit;
using AibisDream.SaveSystem;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.ResourceManagement.AsyncOperations;
using Yarn.Unity;
namespace AibisDream
{
public class SubwayCenter : Singleton<SubwayCenter>, ISceneDialogueGate
{
#region 部分索引
public SpriteRenderer newsSpriteRenderer;
public PlayableDirector arriveStation;
[Tooltip("车厢加速度驱动;到站时触发把手制动摆动")]
public SubwayCarMotion carMotion;
public static StateMachine<OutSideState> StateMachine { get; private set; }
public bool IsDialogueReady { get; private set; }
private string _currentNewsSpriteId;
#endregion
public override void OnSingletonInit()
{
StateMachine = new StateMachine<OutSideState>(CreateState);
IsDialogueReady = false;
StartCoroutine(InitStateMachineRoutine());
}
private IEnumerator InitStateMachineRoutine()
{
IsDialogueReady = false;
yield return StateMachine.Init(OutSideState.Subway);
IsDialogueReady = true;
}
public override void OnSingletonDestroy()
{
IsDialogueReady = false;
}
private static IState<OutSideState> CreateState(OutSideState nextState, string arg = "")
{
IState<OutSideState> res = nextState switch
{
OutSideState.Subway => new SubwayState(),
OutSideState.SubwayTV => new SubwayTVState(),
_ => throw new ArgumentOutOfRangeException(nameof(nextState), nextState, null)
};
res.SetArgs(arg);
return res;
}
public IEnumerator ArriveAtStation()
{
float duration = arriveStation != null && arriveStation.playableAsset != null
? (float)arriveStation.playableAsset.duration
: 8.5f;
if (carMotion == null)
{
carMotion = FindFirstObjectByType<SubwayCarMotion>();
}
if (carMotion != null)
{
carMotion.BeginBraking(duration);
}
if (arriveStation != null)
{
arriveStation.Play();
}
yield return new WaitForSeconds(duration);
}
public IEnumerator SwitchNews(string spriteId)
{
if (string.IsNullOrWhiteSpace(spriteId))
{
Debug.LogWarning("[SubwayCenter] spriteId 为空,无法切换新闻图片。");
yield break;
}
string addressableKey = $"Sprite/News[{spriteId}]";
var handle = ResourceSystem.LoadAsync<Sprite>(addressableKey);
yield return handle;
if (handle.Status != AsyncOperationStatus.Succeeded || handle.Result == null)
{
Debug.LogError($"[SubwayCenter] 加载新闻图片失败: {addressableKey}");
yield break;
}
if (newsSpriteRenderer == null)
{
Debug.LogWarning("[SubwayCenter] newsSpriteRenderer 未赋值,无法切换新闻图片。");
yield break;
}
newsSpriteRenderer.sprite = handle.Result;
_currentNewsSpriteId = spriteId;
}
/// <summary>Captures the macro subway scene snapshot.</summary>
public SubwaySnapshotDto CaptureSnapshot()
{
if (StateMachine == null || !StateMachine.HasState)
{
return null;
}
return new SubwaySnapshotDto
{
state = StateMachine.CurState.ToString(),
args = "",
newsSpriteId = _currentNewsSpriteId
};
}
/// <summary>Restores the macro subway scene snapshot by jumping directly to the target state.</summary>
public IEnumerator RestoreSnapshot(SubwaySnapshotDto dto)
{
if (dto == null || string.IsNullOrEmpty(dto.state))
{
yield break;
}
if (!Enum.TryParse<OutSideState>(dto.state, out var state))
{
Debug.LogWarning($"[SubwayCenter] Could not parse OutSideState: {dto.state}");
yield break;
}
yield return StateMachine.SwitchStateImmediate(state, dto.args ?? "");
if (!string.IsNullOrEmpty(dto.newsSpriteId))
{
yield return SwitchNews(dto.newsSpriteId);
}
}
}
public struct SubwayState : IState<OutSideState>
{
public OutSideState GetState => OutSideState.Subway;
public IEnumerator Enter()
{
yield return CameraKit.Instance.SwitchCamera(CameraEnum.Subway);
DialogUIManager.Instance.LoadBubbles(BubbleSlotKit.Instance.CollectData(BubbleSlotEnum.Subway));
}
public IEnumerator EnterImmediate()
{
yield return CameraKit.Instance.SwitchCamera(CameraEnum.Subway, true);
DialogUIManager.Instance.LoadBubbles(BubbleSlotKit.Instance.CollectData(BubbleSlotEnum.Subway));
}
}
public struct SubwayTVState : IState<OutSideState>
{
public OutSideState GetState => OutSideState.SubwayTV;
public IEnumerator Enter()
{
yield return CameraKit.Instance.SwitchCamera(CameraEnum.SubwayTV);
// 要加载气泡吗?
DialogUIManager.Instance.LoadBubbles(BubbleSlotKit.Instance.CollectData(BubbleSlotEnum.SubwayTV));
}
public IEnumerator EnterImmediate()
{
yield return CameraKit.Instance.SwitchCamera(CameraEnum.SubwayTV, true);
DialogUIManager.Instance.LoadBubbles(BubbleSlotKit.Instance.CollectData(BubbleSlotEnum.SubwayTV));
}
}
public enum OutSideState
{
Subway,
SubwayTV
}
public static class SubwayYarnCommand
{
[YarnCommand("switch_outside_to")]
public static IEnumerator SwitchOutsideTo(string targetStateStr)
{
if (!Enum.TryParse(targetStateStr, out OutSideState targetState))
{
Debug.Log($"{targetStateStr}不在OutSideState枚举中");
yield break;
}
yield return SubwayCenter.StateMachine.SwitchState(targetState);
yield return null;
}
[YarnCommand("arrive_at_station")]
public static IEnumerator ArriveAtStation()
{
return SubwayCenter.Instance.ArriveAtStation();
}
[YarnCommand("switch_news")]
public static IEnumerator SwitchNews(string spriteId)
{
if (SubwayCenter.Instance == null)
{
Debug.LogError("[SubwayYarnCommand] SubwayCenter 实例不存在。");
yield break;
}
yield return SubwayCenter.Instance.SwitchNews(spriteId);
}
}
}