using UnityEngine; using UnityEngine.Events; /// /// 实验流程状态机——整个项目的核心控制器。 /// 负责步骤推进、模式切换、UI 更新、加权考核判分调度。 /// 挂在一个持久化的 GameObject 上。 /// public class StepManager : MonoBehaviour { [Header("配置(优先级:Inspector直接赋值 > ExperimentFlowCoordinator兜底 > Resources加载)")] public ExperimentConfig Config; [Header("事件(拖入场景对象或通过代码绑定)")] public UnityEvent OnStepChanged; public UnityEvent OnTipChanged; public UnityEvent OnDialogueChanged; public UnityEvent OnExamPopup; public UnityEvent OnDetailedContent; public UnityEvent OnExperimentComplete; public UnityEvent OnVideoToggle; public UnityEvent OnModeUIUpdate; // 私有状态 private StepData[] _activeSteps; private int _currentStepIndex = -1; private ExperimentMode _mode; private FixationMethod _fixation; private bool _initialized = false; public int CurrentStepIndex => _currentStepIndex; public int TotalSteps => _activeSteps != null ? _activeSteps.Length : 0; public ExperimentMode Mode => _mode; public FixationMethod Fixation => _fixation; public bool IsInitialized => _initialized; /// 用 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) { // 如果 Inspector 里没有直接拖 Config,尝试从 ExperimentFlowCoordinator 获取兜底配置 if (Config == null) { var coordinator = FindObjectOfType(); if (coordinator != null && coordinator.DefaultConfig != null) { Config = coordinator.DefaultConfig; } } // 最后尝试从 Resources 加载 if (Config == null) { Config = Resources.Load($"Configs/{type}Config"); } if (Config == null) { Debug.LogError($"StepManager: 找不到 ExperimentConfig for {type}。" + "请在 Inspector 中直接拖入 Config,或在 ExperimentFlowCoordinator 上设置 Default Config," + "或在 Resources/Configs/ 下创建 ScriptableObject。"); return; } _mode = mode; _fixation = fixation; _activeSteps = Config.GetStepsFor(mode, fixation); if (_activeSteps == null || _activeSteps.Length == 0) { Debug.LogError($"StepManager: 实验 {type} 模式 {mode} 固定方式 {fixation} 无步骤数据!"); return; } _currentStepIndex = -1; _initialized = true; 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() { // Woman 自由步进优先 if (_womanObject != null && _womanPositions != null) { WomanStepNext(); return; } if (_activeSteps == null) return; if (_currentStepIndex >= _activeSteps.Length - 1) { CompleteExperiment(); return; } GoToStep(_currentStepIndex + 1); } public void PreviousStep() { // Woman 自由步进优先 if (_womanObject != null && _womanPositions != null) { WomanStepPrevious(); return; } if (_activeSteps == null) return; if (_currentStepIndex <= 0) return; GoToStep(_currentStepIndex - 1); } public StepData GetCurrentStep() { if (_activeSteps == null || _currentStepIndex < 0 || _currentStepIndex >= _activeSteps.Length) return null; return _activeSteps[_currentStepIndex]; } 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); } public void InitWomanSteps() { _womanObject = GameObject.Find("woman"); _touchArrayObject = GameObject.Find("touchArray"); var pts = GameObject.Find("womanPointArray"); if (pts != null) { _womanMaxStep = pts.transform.childCount; _womanPositions = new Transform[_womanMaxStep]; for (int i = 0; i < _womanMaxStep; i++) _womanPositions[i] = pts.transform.GetChild(i); } if (_touchArrayObject != null) { _touchPoints = new GameObject[_touchArrayObject.transform.childCount]; for (int i = 0; i < _touchPoints.Length; i++) _touchPoints[i] = _touchArrayObject.transform.GetChild(i).gameObject; } _womanStepIndex = 0; Debug.Log($"StepManager: Woman步进已初始化, 共{_womanMaxStep}个位置"); } public void RecordWomanStep(int newStepIndex) { _womanStepIndex = Mathf.Clamp(newStepIndex, 0, _womanMaxStep); Debug.Log($"StepManager: Woman步进 -> {_womanStepIndex}"); OnStepChanged?.Invoke(_womanStepIndex); } private void WomanStepPrevious() { if (_womanStepIndex <= 0) return; _womanStepIndex--; MoveWomanToStep(_womanStepIndex); UpdateTouchVisibility(_womanStepIndex); Debug.Log($"StepManager: Woman撤销 -> {_womanStepIndex}"); OnStepChanged?.Invoke(_womanStepIndex); } private void WomanStepNext() { if (_womanStepIndex >= _womanMaxStep) return; _womanStepIndex++; MoveWomanToStep(_womanStepIndex); UpdateTouchVisibility(_womanStepIndex); Debug.Log($"StepManager: Woman前进 -> {_womanStepIndex}"); OnStepChanged?.Invoke(_womanStepIndex); } private void MoveWomanToStep(int step) { if (_womanObject == null || _womanPositions == null || step <= 0 || step > _womanPositions.Length) return; var t = _womanPositions[step - 1]; _womanObject.transform.position = t.position; _womanObject.transform.rotation = t.rotation; } private void UpdateTouchVisibility(int step) { if (_touchPoints == null) return; for (int i = 0; i < _touchPoints.Length; i++) { bool show = (i == step); _touchPoints[i].SetActive(show); if (show) { var mr = _touchPoints[i].GetComponent(); if (mr != null) mr.enabled = true; } } } [Header("=== Woman 步进追踪(不依赖 Config) ===")] [SerializeField] private GameObject _womanObject; [SerializeField] private GameObject _touchArrayObject; private int _womanStepIndex = 0; private int _womanMaxStep = 3; private Transform[] _womanPositions; private GameObject[] _touchPoints; }