Files
VFSUnity/Assets/Scripts/Core/StepManager.cs
T
yangbear 2df79866d0 feat: 上一步/下一步支持woman步进撤销重做
- StepManager新增自由步进模式(无Config时):
  InitWomanSteps/RecordWomanStep/WomanStepPrevious/WomanStepNext
- PreviousStep/NextStep在_activeSteps为null时走woman步进
- TouchPositionHandler点击后通知StepManager.RecordWomanStep
- PageController在实验启动时调用InitWomanSteps初始化
- 上一步→woman回到上个位置+显示对应touchPoint
- 下一步→woman移到下个位置+显示对应touchPoint
2026-06-30 14:32:30 +08:00

237 lines
8.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 实验流程状态机——整个项目的核心控制器。
/// 负责步骤推进、模式切换、UI 更新、加权考核判分调度。
/// 挂在一个持久化的 GameObject 上。
/// </summary>
public class StepManager : MonoBehaviour
{
[Header("配置(优先级:Inspector直接赋值 > ExperimentFlowCoordinator兜底 > Resources加载)")]
public ExperimentConfig Config;
[Header("事件(拖入场景对象或通过代码绑定)")]
public UnityEvent<int> OnStepChanged;
public UnityEvent<string> OnTipChanged;
public UnityEvent<string> OnDialogueChanged;
public UnityEvent<StepData> OnExamPopup;
public UnityEvent<string> OnDetailedContent;
public UnityEvent OnExperimentComplete;
public UnityEvent<string, bool> OnVideoToggle;
public UnityEvent<bool> 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;
/// <summary>用 GameManager 的当前设置初始化</summary>
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);
}
/// <summary>显式指定参数初始化</summary>
public void Initialize(ExperimentType type, ExperimentMode mode, FixationMethod fixation)
{
// 如果 Inspector 里没有直接拖 Config,尝试从 ExperimentFlowCoordinator 获取兜底配置
if (Config == null)
{
var coordinator = FindObjectOfType<ExperimentFlowCoordinator>();
if (coordinator != null && coordinator.DefaultConfig != null)
{
Config = coordinator.DefaultConfig;
}
}
// 最后尝试从 Resources 加载
if (Config == null)
{
Config = Resources.Load<ExperimentConfig>($"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()
{
if (_activeSteps == null) { WomanStepNext(); return; }
if (_currentStepIndex >= _activeSteps.Length - 1) { CompleteExperiment(); return; }
GoToStep(_currentStepIndex + 1);
}
public void PreviousStep()
{
if (_activeSteps == null) { WomanStepPrevious(); 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++) _touchPoints[i].SetActive(i == step - 1);
}
[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;
}