using UnityEngine; using UnityEngine.Events; /// /// 实验流程状态机——整个项目的核心控制器。 /// 负责步骤推进、模式切换、UI 更新、加权考核判分调度。 /// 挂在一个持久化的 GameObject 上。 /// public class StepManager : MonoBehaviour { [Header("配置")] public ExperimentConfig Config; [Header("事件(拖入场景对象或通过代码绑定)")] public UnityEvent OnStepChanged; // 步骤切换时触发,传新步骤索引 public UnityEvent OnTipChanged; // Tip 文案更新 public UnityEvent OnDialogueChanged; // 角色对话更新 public UnityEvent OnExamPopup; // 考核弹窗触发,传当前 StepData public UnityEvent OnDetailedContent; // 详细操作说明更新(甲方补充) public UnityEvent OnExperimentComplete; // 实验全部完成 public UnityEvent OnVideoToggle; // 画中画视频开关(文件名, 是否激活) public UnityEvent OnModeUIUpdate; // 通知 UI 刷新模式按钮状态 // 私有状态 private StepData[] _activeSteps; private int _currentStepIndex = -1; private ExperimentMode _mode; private FixationMethod _fixation; public int CurrentStepIndex => _currentStepIndex; public int TotalSteps => _activeSteps != null ? _activeSteps.Length : 0; public ExperimentMode Mode => _mode; public FixationMethod Fixation => _fixation; /// 用 GameManager 的当前设置初始化实验流程 public void InitializeFromGameManager() { var gm = GameManager.Instance; if (gm == null) { Debug.LogError("StepManager: GameManager.Instance is null!"); return; } Initialize(gm.CurrentExperimentType, gm.CurrentMode, gm.CurrentFixationMethod); } /// 显式指定参数初始化 public void Initialize(ExperimentType type, ExperimentMode mode, FixationMethod fixation) { _mode = mode; _fixation = fixation; Config = Resources.Load($"Configs/{type}Config"); if (Config == null) { Debug.LogError($"StepManager: 找不到 ExperimentConfig for {type},请在 Resources/Configs/ 下创建"); return; } _activeSteps = Config.GetStepsFor(mode, fixation); if (_activeSteps == null || _activeSteps.Length == 0) { Debug.LogError($"StepManager: 实验 {type} 模式 {mode} 固定方式 {fixation} 无步骤数据!"); return; } _currentStepIndex = -1; if (mode == ExperimentMode.Exam) GameManager.Instance.InitExamRecord(_activeSteps.Length); OnModeUIUpdate?.Invoke(mode == ExperimentMode.Exam); GoToStep(0); } /// 跳转到指定步骤(含边界检查) public void GoToStep(int index) { if (_activeSteps == null) return; index = Mathf.Clamp(index, 0, _activeSteps.Length - 1); if (index == _currentStepIndex) return; _currentStepIndex = index; var step = _activeSteps[index]; if (_mode == ExperimentMode.Learn) { OnTipChanged?.Invoke(step.TipText); OnDialogueChanged?.Invoke(step.DialogueText); OnDetailedContent?.Invoke(step.DetailedContent); if (step.ShowPipVideo) OnVideoToggle?.Invoke(step.VideoFileName, true); else OnVideoToggle?.Invoke(null, false); } else { OnTipChanged?.Invoke(""); OnDialogueChanged?.Invoke(""); OnDetailedContent?.Invoke(""); OnVideoToggle?.Invoke(null, false); if (!string.IsNullOrEmpty(step.ExamQuestionText)) OnExamPopup?.Invoke(step); } OnStepChanged?.Invoke(_currentStepIndex); } /// 下一步 public void NextStep() { if (_activeSteps == null) return; if (_currentStepIndex >= _activeSteps.Length - 1) { CompleteExperiment(); return; } GoToStep(_currentStepIndex + 1); } /// 上一步 public void PreviousStep() { if (_activeSteps == null || _currentStepIndex <= 0) return; GoToStep(_currentStepIndex - 1); } /// 获取当前步骤数据(只读) public StepData GetCurrentStep() { if (_activeSteps == null || _currentStepIndex < 0 || _currentStepIndex >= _activeSteps.Length) return null; return _activeSteps[_currentStepIndex]; } /// /// 当前步骤互动完成后的回调。 /// proportion: 得分比例 0~1(SequenceClick 按子项命中比例,其余全对=1,全错=0)。 /// public void OnStepInteractionComplete(float proportion = 1f, string feedback = "") { var step = GetCurrentStep(); if (step == null) return; if (_mode == ExperimentMode.Exam) { string fb = string.IsNullOrEmpty(feedback) ? step.ErrorFeedback : feedback; GameManager.Instance.RecordExamResult(_currentStepIndex, step.ScoreWeight, proportion, fb); } if (_mode == ExperimentMode.Learn) NextStep(); } /// 实验完成 private void CompleteExperiment() { Debug.Log("StepManager: 实验流程结束"); OnExperimentComplete?.Invoke(); } /// 切换模式(学习 ↔ 考核),从头开始 public void SwitchMode(ExperimentMode newMode) { GameManager.Instance.CurrentMode = newMode; Initialize(GameManager.Instance.CurrentExperimentType, newMode, GameManager.Instance.CurrentFixationMethod); } /// 切换固定方式,从头开始 public void SwitchFixation(FixationMethod newFixation) { GameManager.Instance.CurrentFixationMethod = newFixation; Initialize(GameManager.Instance.CurrentExperimentType, GameManager.Instance.CurrentMode, newFixation); } }