多轮审查和修复
This commit is contained in:
141
Assets/Scripts/Dialogue/DialogueManager.cs
Normal file
141
Assets/Scripts/Dialogue/DialogueManager.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System.Collections;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Events;
|
||||
using BaseGames.Input;
|
||||
using BaseGames.World;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Dialogue
|
||||
{
|
||||
/// <summary>
|
||||
/// 对话管理器(架构 14_NarrativeModule §4)。
|
||||
/// 驱动 DialogueUI 打字机效果;管理 Action Map 切换;向 QuestManager 广播对话完成事件。
|
||||
/// 在 Awake 中注册到 ServiceLocator。
|
||||
/// </summary>
|
||||
public class DialogueManager : MonoBehaviour, IDialogueService
|
||||
{
|
||||
[SerializeField] private DialogueUI _dialogueBox;
|
||||
[SerializeField] private InputReaderSO _inputReader;
|
||||
[SerializeField] private WorldStateRegistry _worldState;
|
||||
|
||||
[Header("事件频道")]
|
||||
[SerializeField] private VoidEventChannelSO _onDialogueStarted;
|
||||
[SerializeField] private VoidEventChannelSO _onDialogueEnded;
|
||||
[SerializeField] private StringEventChannelSO _onNpcDialogueCompleted; // → EVT_NpcDialogueCompleted (npcId)
|
||||
|
||||
private bool _skipRequested;
|
||||
|
||||
/// <summary>当前是否有对话正在播放。</summary>
|
||||
public bool IsDialogueActive { get; private set; }
|
||||
|
||||
// ── 生命周期 ──────────────────────────────────────────────────────
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (ServiceLocator.GetOrDefault<IDialogueService>() != null) { Destroy(gameObject); return; }
|
||||
ServiceLocator.Register<IDialogueService>(this);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ServiceLocator.Unregister<IDialogueService>(this);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_inputReader != null) _inputReader.SubmitEvent += OnSubmit;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_inputReader != null) _inputReader.SubmitEvent -= OnSubmit;
|
||||
}
|
||||
|
||||
// ── 公开 API ──────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 启动对话序列。若已有对话在播放则忽略新请求。
|
||||
/// 由 InteractableNPC.Interact() 调用。
|
||||
/// </summary>
|
||||
/// <param name="sequence">要播放的对话序列 SO。</param>
|
||||
/// <param name="npcId">NPC 标识符,对话结束时随 EVT_NpcDialogueCompleted 广播。</param>
|
||||
public void StartDialogue(DialogueSequenceSO sequence, string npcId = "")
|
||||
{
|
||||
if (IsDialogueActive || sequence == null) return;
|
||||
IsDialogueActive = true;
|
||||
_skipRequested = false;
|
||||
|
||||
// 切换到 UI Action Map(禁用玩家移动输入)
|
||||
_inputReader.EnableUIInput();
|
||||
|
||||
_onDialogueStarted?.Raise();
|
||||
StartCoroutine(PlaySequence(sequence, npcId));
|
||||
}
|
||||
|
||||
// ── 输入回调 ──────────────────────────────────────────────────────
|
||||
|
||||
private void OnSubmit() => _skipRequested = true;
|
||||
|
||||
// ── 内部协程 ──────────────────────────────────────────────────────
|
||||
|
||||
private IEnumerator PlaySequence(DialogueSequenceSO sequence, string npcId)
|
||||
{
|
||||
// 选择条件变体(查询 ConditionalVariant.conditionFlag — 未实现 WorldStateRegistry 查询时直接使用默认序列)
|
||||
var resolved = ResolveVariant(sequence);
|
||||
|
||||
foreach (var line in resolved.lines)
|
||||
{
|
||||
_skipRequested = false;
|
||||
_dialogueBox.ShowLine(line);
|
||||
|
||||
// 等待打字完成,期间允许跳过
|
||||
yield return new WaitUntil(() => !_dialogueBox.IsTyping || _skipRequested);
|
||||
if (_dialogueBox.IsTyping) _dialogueBox.SkipTyping();
|
||||
|
||||
// 等待玩家按 Submit 推进下一行
|
||||
_skipRequested = false;
|
||||
yield return new WaitUntil(() => _skipRequested);
|
||||
}
|
||||
|
||||
EndDialogue(npcId);
|
||||
}
|
||||
|
||||
private void EndDialogue(string npcId)
|
||||
{
|
||||
_dialogueBox.Hide();
|
||||
IsDialogueActive = false;
|
||||
|
||||
// 恢复 Gameplay Action Map
|
||||
_inputReader.EnableGameplayInput();
|
||||
|
||||
_onDialogueEnded?.Raise();
|
||||
|
||||
if (!string.IsNullOrEmpty(npcId))
|
||||
_onNpcDialogueCompleted?.Raise(npcId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ConditionalVariant 选择正确的序列版本。
|
||||
/// 按顺序检查 variants:第一个满足 WorldStateRegistry 标志的变体胜出;
|
||||
/// 未注入 WorldStateRegistry 或无满足条件的变体时,返回原序列(默认)。
|
||||
/// </summary>
|
||||
private DialogueSequenceSO ResolveVariant(DialogueSequenceSO sequence)
|
||||
{
|
||||
if (sequence.variants == null || sequence.variants.Length == 0)
|
||||
return sequence;
|
||||
|
||||
if (_worldState != null)
|
||||
{
|
||||
foreach (var variant in sequence.variants)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(variant.conditionFlag)
|
||||
&& variant.sequence != null
|
||||
&& _worldState.HasFlag(variant.conditionFlag))
|
||||
return variant.sequence;
|
||||
}
|
||||
}
|
||||
|
||||
return sequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user