Framework/Assets/Framework/Manager/ProcessManager.cs

1079 lines
40 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 System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using DefaultNamespace.Dto;
using Framework.ProcessMode;
// using HighlightPlus;
using MotionFramework;
using Newtonsoft.Json;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using SceneManager = Framework.Scripts.Runtime.Engine.Scene.SceneManager;
using Type = System.Type;
namespace DefaultNamespace.ProcessMode
{
/// <summary>
/// 流程模式管理器 - 负责管理不同模式下的流程控制、步骤执行和评分
/// </summary>
public class ProcessManager : ModuleSingleton<ProcessManager>, IModule
{
#region
private Dictionary<string, ProcessCollection> _processes = new Dictionary<string, ProcessCollection>();
public ProcessMode _currentMode; // 当前模式
private int _currentStepIndex; // 当前步骤索引
private int _currentActionIndex; // 当前动作索引
private int _currentActionGameIndex; // 当前游戏动作索引
public Dictionary<int, List<string>> _incorrectClicksPerStep; // 每个步骤的错误点击记录
private List<SubmitScoreStep> _submitScoreSteps = new List<SubmitScoreStep>(); // 存储分数集合
private List<Record> records; // 分数集合
#endregion
#region
public ProcessCollection CurrentProcessCollection => _processes[_currentMode.ToString()];
#endregion
#region
public delegate void CompleteEventHandler(int score);
public delegate void UIEventHandler();
public delegate void StepProcessDescriptionMessage(string message);
public delegate void StepProcessMessage(string message);
public delegate void TeachingPromptsObjects(GameObject gameObj);
public delegate void TeachingMessagePrompt(string message);
/// <summary>
/// 全部流程结束调用方法
/// </summary>
public event CompleteEventHandler OnCompleteEvent;
public event UIEventHandler OnUIEvent;
/// <summary>
/// 右上角消息通知
/// </summary>
public event StepProcessMessage OnStepProcessMessage;
/// <summary>
/// 教学模式箭头指向
/// </summary>
public event TeachingPromptsObjects OnTeachingPromptsObjects;
/// <summary>
/// 步骤描述
/// </summary>
public event StepProcessDescriptionMessage OnStepProcessDescriptionMessage;
#endregion
#region
/// <summary>
/// 初始化第一个步骤
/// </summary>
public void InitializeFirstStep(string json, ProcessMode mode)
{
_currentMode = ProcessMode.Teaching;
_processes[_currentMode.ToString()] = new ProcessCollection(_currentMode.ToString());
List<ProcessStep> steps = JsonConvert.DeserializeObject<List<ProcessStep>>(json);
foreach (var processStep in steps)
{
AddStepToProcess(_currentMode.ToString(), processStep);
}
InitializeModeFeedback();
}
/// <summary>
/// 初始化模式反馈
/// </summary>
private void InitializeModeFeedback()
{
if (_currentMode == ProcessMode.Teaching || _currentMode == ProcessMode.Training)
{
if (CurrentProcessCollection.Steps.Count > 0)
{
ProcessStep firstStep = CurrentProcessCollection.Steps[0];
if (firstStep.Actions.Count > 0)
{
HandleModeFeedback(_currentMode, firstStep.Actions[0]);
}
}
}
}
/// <summary>
/// 重置流程并加载新的配置
/// </summary>
/// <param name="newConfigPath">新的配置文件路径,如果为空则使用当前配置文件</param>
/// <returns>重置是否成功</returns>
public bool ResetProcess(string newJson, ProcessMode mode)
{
try
{
// 重置状态
_currentStepIndex = 0;
_currentActionIndex = 0;
_currentActionGameIndex = 0;
_incorrectClicksPerStep.Clear();
_submitScoreSteps.Clear();
// 清除当前流程集合
_processes.Clear();
Debug.Log($"流程已重置新json--->{newJson}");
// 重新初始化第一个步骤
InitializeFirstStep(newJson, mode);
return true;
}
catch (System.Exception e)
{
Debug.LogError($"重置流程失败:{e.Message}");
return false;
}
}
// /// <summary>
// /// 添加流程类型
// /// </summary>
// public void AddProcessType(string type)
// {
// _processes[type] = new ProcessCollection(type);
// Enum.TryParse(type, true, out _currentMode);
// }
/// <summary>
/// 添加步骤到流程
/// </summary>
private void AddStepToProcess(string type, ProcessStep step)
{
if (_processes.ContainsKey(type))
{
_processes[type].AddStep(step);
}
}
#endregion
#region
/// <summary>
/// 处理点击事件 - 多选题答案集合版本
/// </summary>
public bool HandleClick(List<string> selectedAnswers)
{
string type = _currentMode.ToString();
if (!_processes.ContainsKey(type)) return false;
ProcessCollection processCollection = _processes[type];
if (_currentStepIndex >= processCollection.Steps.Count) return false;
ProcessStep step = processCollection.Steps[_currentStepIndex];
ProcessStepDescription currentAction = step.Actions[_currentActionIndex];
// 只处理多选题类型
if (currentAction.ActionType != ProcessActionType.)
{
Debug.LogWarning("当前动作不是多选题类型!");
return false;
}
// 确保有多选题配置
if (currentAction.MultipleChoiceQuestions == null || currentAction.MultipleChoiceQuestions.Count == 0)
{
Debug.LogWarning("当前动作没有配置多选题!");
return false;
}
// 获取当前要回答的多选题
if (currentAction.CurrentObjectIndex >= currentAction.MultipleChoiceQuestions.Count)
{
Debug.LogWarning("已经回答完所有多选题!");
return false;
}
var currentQuestion = currentAction.MultipleChoiceQuestions[currentAction.CurrentObjectIndex];
// 检查答案正确性
var correctAnswers = currentQuestion.Options; // 直接使用选项列表作为正确答案
var selectedAnswersSet = new HashSet<string>(selectedAnswers);
var correctAnswersSet = new HashSet<string>(correctAnswers);
// 找出未选择的正确答案(算作错误)
var unselectedCorrectAnswers = correctAnswersSet.Where(correct => !selectedAnswersSet.Contains(correct)).ToList();
// 找出多选的错误答案
var extraWrongAnswers = selectedAnswersSet.Where(selected => !correctAnswersSet.Contains(selected)).ToList();
// 计算错误答案总数
int wrongAnswersCount = unselectedCorrectAnswers.Count + extraWrongAnswers.Count;
// 只有当没有错误答案时才算正确
bool isCorrect = wrongAnswersCount == 0;
// 记录用户的选择
currentAction.ClickedObjects.Clear();
currentAction.ClickedObjects.AddRange(selectedAnswers);
if (isCorrect)
{
Debug.Log($"回答正确!问题:{currentQuestion.Question}");
Debug.Log($"选择的答案:{string.Join(", ", selectedAnswers)}");
Debug.Log($"正确答案:{string.Join(", ", correctAnswers)}");
currentAction.CurrentObjectIndex++; // 移动到下一题
currentAction.ClickedObjects.Clear(); // 清空选择,准备下一题
// 如果所有题目都回答完了
if (currentAction.CurrentObjectIndex >= currentAction.MultipleChoiceQuestions.Count)
{
CompleteAction(step, currentAction);
}
return true;
}
else
{
// 记录错误信息
Debug.Log($"回答错误!问题:{currentQuestion.Question}");
Debug.Log($"选择的答案:{string.Join(", ", selectedAnswers)}");
Debug.Log($"正确答案:{string.Join(", ", correctAnswers)}");
// 记录未选择的正确答案
foreach (var unselectedAnswer in unselectedCorrectAnswers)
{
Debug.Log($"未选择正确答案:{unselectedAnswer}");
OnStepProcessDescriptionMessage?.Invoke($"<color=red>未选择正确答案:{unselectedAnswer}</color>");
AddIncorrectClick(_currentStepIndex, unselectedAnswer);
}
// 记录多选的错误答案
foreach (var extraAnswer in extraWrongAnswers)
{
Debug.Log($"多选错误答案:{extraAnswer}");
OnStepProcessDescriptionMessage?.Invoke($"<color=red>多选错误答案:{extraAnswer}</color>");
AddIncorrectClick(_currentStepIndex, extraAnswer);
}
OnStepProcessDescriptionMessage?.Invoke($"<color=red>回答错误</color>,正确答案:{string.Join(", ", correctAnswers)}");
// 如果不要求正确完成,且在练习或考核模式下
if (!currentAction.RequireCorrectCompletion && IsInPracticeOrAssessment())
{
currentAction.CurrentObjectIndex++;
currentAction.ClickedObjects.Clear();
if (currentAction.CurrentObjectIndex >= currentAction.MultipleChoiceQuestions.Count)
{
CompleteAction(step, currentAction);
}
return true;
}
else if (currentAction.RequireCorrectCompletion)
{
// 如果要求正确完成,清空当前作答记录,重新开始当前题目
currentAction.ClickedObjects.Clear();
OnStepProcessDescriptionMessage?.Invoke($"<color=red>需要正确完成此题</color>,请重新作答");
return false;
}
}
return true;
}
/// <summary>
/// 处理点击事件
/// </summary>
public bool HandleClick(string clickedObject)
{
Debug.Log("点击得物体--->" + clickedObject);
string type = _currentMode.ToString();
if (!_processes.ContainsKey(type)) return false;
ProcessCollection processCollection = _processes[type];
if (_currentStepIndex >= processCollection.Steps.Count) return false;
ProcessStep step = processCollection.Steps[_currentStepIndex];
ProcessStepDescription currentAction = step.Actions[_currentActionIndex];
// 根据动作类型进行不同的处理
if (currentAction.ActionType == ProcessActionType.)
{
return HandleOtherModeClick(step, currentAction, clickedObject);
}
else if (currentAction.ActionType == ProcessActionType.)
{
return HandleJudgmentQuestionClick(step, currentAction, clickedObject);
}
else if (currentAction.ActionType == ProcessActionType.)
{
// 对于多选题,如果是单个点击,添加或移除选项
if (!currentAction.ClickedObjects.Contains(clickedObject))
{
currentAction.ClickedObjects.Add(clickedObject);
}
else
{
currentAction.ClickedObjects.Remove(clickedObject);
}
return true;
}
return false;
}
/// <summary>
/// 处理判断题类型的点击
/// </summary>
private bool HandleJudgmentQuestionClick(ProcessStep step, ProcessStepDescription currentAction, string userAnswer)
{
// 确保有判断题配置
if (currentAction.JudgmentQuestions == null || currentAction.JudgmentQuestions.Count == 0)
{
Debug.LogWarning("当前动作没有配置判断题!");
return false;
}
// 获取当前要回答的判断题
if (currentAction.CurrentObjectIndex >= currentAction.JudgmentQuestions.Count)
{
Debug.LogWarning("已经回答完所有判断题!");
return false;
}
var currentQuestion = currentAction.JudgmentQuestions[currentAction.CurrentObjectIndex];
bool isCorrect = currentQuestion.CorrectAnswer == userAnswer;
if (isCorrect)
{
Debug.Log($"回答正确!问题:{currentQuestion.Question},答案:{userAnswer}");
currentAction.ClickedObjects.Add(userAnswer); // 记录已回答
currentAction.CurrentObjectIndex++; // 移动到下一题
// 如果所有题目都回答完了
if (currentAction.CurrentObjectIndex >= currentAction.JudgmentQuestions.Count)
{
CompleteAction(step, currentAction);
}
return true;
}
else
{
Debug.Log($"回答错误!问题:{currentQuestion.Question},你的答案:{userAnswer},正确答案:{currentQuestion.CorrectAnswer}");
OnStepProcessDescriptionMessage?.Invoke($"<color=red>回答错误</color>,正确答案:{currentQuestion.CorrectAnswer}");
AddIncorrectClick(_currentStepIndex, userAnswer);
// 如果不要求正确完成,且在练习或考核模式下
if (!currentAction.RequireCorrectCompletion && IsInPracticeOrAssessment())
{
currentAction.ClickedObjects.Add(userAnswer);
currentAction.CurrentObjectIndex++;
if (currentAction.CurrentObjectIndex >= currentAction.JudgmentQuestions.Count)
{
CompleteAction(step, currentAction);
}
return true;
}
else if (currentAction.RequireCorrectCompletion)
{
// 如果要求正确完成,清空当前作答记录,重新开始当前题目
currentAction.ClickedObjects.Clear();
OnStepProcessDescriptionMessage?.Invoke($"<color=red>需要正确完成此题</color>,请重新作答");
}
return false;
}
}
/// <summary>
/// 处理教学模式下的点击
/// </summary>
private bool HandleTeachingModeClick(ProcessStep step, ProcessStepDescription currentAction, string clickedObject)
{
if (currentAction.TargetObjects.Count == 0) return false;
if (currentAction.TargetObjects[currentAction.CurrentObjectIndex].ObjectName == clickedObject)
{
ProcessCorrectClick(step, currentAction, clickedObject);
return true;
}
string correctObjectName = currentAction.TargetObjects[currentAction.CurrentObjectIndex].ObjectName;
Debug.Log($"错误点击:{clickedObject} --- 正确的物体是:{correctObjectName}");
OnStepProcessDescriptionMessage?.Invoke($"<color=red>错误点击</color>,正确对象:{correctObjectName}");
if (!currentAction.RequireCorrectCompletion && IsInPracticeOrAssessment())
{
ProcessCorrectClick(step, currentAction, clickedObject);
return true;
}
return false;
}
/// <summary>
/// 处理其他模式下的点击
/// </summary>
private bool HandleOtherModeClick(ProcessStep step, ProcessStepDescription currentAction, string clickedObject)
{
if (currentAction.IsSequential)
{
return HandleSequentialClick(step, currentAction, clickedObject);
}
else
{
return HandleNonSequentialClick(step, currentAction, clickedObject);
}
}
/// <summary>
/// 处理顺序点击
/// </summary>
private bool HandleSequentialClick(ProcessStep step, ProcessStepDescription currentAction, string clickedObject)
{
if (currentAction.CurrentObjectIndex < currentAction.TargetObjects.Count &&
currentAction.TargetObjects[currentAction.CurrentObjectIndex].ObjectName == clickedObject)
{
ProcessCorrectClick(step, currentAction, clickedObject);
return true;
}
string correctObjectName = currentAction.TargetObjects[currentAction.CurrentObjectIndex].ObjectName;
Debug.Log($"错误点击或顺序错误:{clickedObject}。正确的物体是:{correctObjectName}");
OnStepProcessDescriptionMessage?.Invoke($"<color=red>错误点击</color>,正确:{correctObjectName}");
AddIncorrectClick(_currentStepIndex, clickedObject);
if (!currentAction.RequireCorrectCompletion && IsInPracticeOrAssessment())
{
ProcessCorrectClick(step, currentAction, clickedObject);
return true;
}
return false;
}
/// <summary>
/// 处理非顺序点击
/// </summary>
private bool HandleNonSequentialClick(ProcessStep step, ProcessStepDescription currentAction, string clickedObject)
{
if (currentAction.TargetObjects.Any(obj => obj.ObjectName == clickedObject))
{
if (currentAction.ClickedObjects.Contains(clickedObject))
{
Debug.Log($"错误点击:{clickedObject}。这个物体已经点击过。");
OnStepProcessDescriptionMessage?.Invoke($"<color=red>错误点击:</color>{clickedObject}。这个物体已经点击过。");
return true;
}
ProcessCorrectClick(step, currentAction, clickedObject);
return true;
}
HandleIncorrectClick(currentAction, clickedObject);
if (!currentAction.RequireCorrectCompletion && IsInPracticeOrAssessment())
{
ProcessCorrectClick(step, currentAction, clickedObject);
return true;
}
return false;
}
/// <summary>
/// 处理错误点击
/// </summary>
private void HandleIncorrectClick(ProcessStepDescription currentAction, string clickedObject)
{
List<string> correctObjectNames = currentAction.TargetObjects
.Where(obj => !currentAction.ClickedObjects.Contains(obj.ObjectName))
.Select(obj => obj.ObjectName)
.ToList();
string correctObjects = string.Join(",", correctObjectNames);
Debug.Log($"错误点击:{clickedObject} 正确的物体是:{correctObjects}");
OnStepProcessDescriptionMessage?.Invoke($"<color=red>错误点击</color>,正确:{correctObjects}");
AddIncorrectClick(_currentStepIndex, clickedObject);
}
#endregion
#region
/// <summary>
/// 处理正确点击
/// </summary>
private void ProcessCorrectClick(ProcessStep step, ProcessStepDescription currentAction, string clickedObject)
{
Debug.Log($"正确点击了:{clickedObject}");
currentAction.ClickedObjects.Add(clickedObject);
currentAction.CurrentObjectIndex++;
Debug.Log($"当前动作进度:{currentAction.CurrentObjectIndex}/{currentAction.TargetObjects.Count}");
if (currentAction.CurrentObjectIndex >= currentAction.TargetObjects.Count)
{
Debug.Log($"动作完成!当前索引:{currentAction.CurrentObjectIndex},目标数量:{currentAction.TargetObjects.Count}");
CompleteAction(step, currentAction);
}
else
{
HighlightNextObject(currentAction);
}
}
/// <summary>
/// 完成当前动作
/// </summary>
private void CompleteAction(ProcessStep step, ProcessStepDescription currentAction)
{
Debug.Log($"完成了动作 {_currentActionIndex + 1}");
step.PlayAnimation(_currentActionIndex);
_currentActionIndex++;
currentAction.CurrentObjectIndex = 0;
currentAction.ClickedObjects.Clear();
currentAction.FeedbackDisplayed = false;
if (_currentActionIndex >= step.Actions.Count)
{
HandleStepCompletion(step);
}
else
{
Debug.Log("开始下一个动作!");
HandleModeFeedback(_currentMode, step.Actions[_currentActionIndex]);
}
}
/// <summary>
/// 处理步骤完成
/// </summary>
private void HandleStepCompletion(ProcessStep step)
{
Debug.Log("所有动作完成!=>" + step.StepDescription);
step.IsCompleted = true;
if (_currentMode == ProcessMode.Practice)
{
HandlePracticeModeCompletion();
}
else
{
HandleOtherModeCompletion();
}
}
/// <summary>
/// 处理练习模式完成
/// </summary>
private void HandlePracticeModeCompletion()
{
if (_currentStepIndex < _processes[_currentMode.ToString()].Steps.Count)
{
var nextStep = _processes[_currentMode.ToString()].Steps[_currentStepIndex];
HandleModeFeedback(_currentMode, nextStep.Actions[0]);
_currentActionIndex = 0;
_currentStepIndex++;
if (_currentStepIndex == _processes[_currentMode.ToString()].Steps.Count)
{
CompleteProcess();
}
}
else
{
CompleteProcess();
}
}
/// <summary>
/// 处理其他模式完成
/// </summary>
private void HandleOtherModeCompletion()
{
_currentActionIndex = 0;
_currentStepIndex++;
if (_currentStepIndex < _processes[_currentMode.ToString()].Steps.Count)
{
var nextStep = _processes[_currentMode.ToString()].Steps[_currentStepIndex];
Debug.Log($"开始继续下一个步骤----{_processes[_currentMode.ToString()].Steps[_currentStepIndex].StepDescription}");
HandleModeFeedback(_currentMode, nextStep.Actions[0]);
}
else
{
if (OnTeachingPromptsObjects != null) OnTeachingPromptsObjects(null);
Debug.Log("所有得步骤都已经完成了!");
}
}
/// <summary>
/// 完成整个流程
/// </summary>
private void CompleteProcess()
{
Debug.Log(CalculateTotalScore());
OnCompleteEvent?.Invoke(CalculateTotalScore());
Debug.Log("全部完成了!!!!");
}
#endregion
#region
/// <summary>
/// 高亮显示下一个需要点击的物体
/// </summary>
private void HighlightNextObject(ProcessStepDescription action)
{
if (action == null) return;
bool hasDuplicates = action.TargetObjects.GroupBy(x => x.ObjectName).Any(g => g.Count() > 1);
for (int i = action.CurrentObjectIndex; i < action.TargetObjects.Count; i++)
{
var nextTarget = action.TargetObjects[i];
string nextObjectName = nextTarget.ObjectName;
if (hasDuplicates)
{
if (HandleDuplicateObjectHighlight(action, nextTarget, nextObjectName, i))
break;
}
else
{
if (HandleUniqueObjectHighlight(action, nextTarget, nextObjectName))
break;
}
}
}
/// <summary>
/// 处理重复物体的高亮
/// </summary>
private bool HandleDuplicateObjectHighlight(ProcessStepDescription action, (string ObjectName, ProcessTargetType Type) nextTarget, string nextObjectName, int currentIndex)
{
int count = action.TargetObjects.Take(currentIndex).Count(o => o.ObjectName == nextObjectName);
string uniqueObjectKey = $"{nextObjectName}_{count}";
if (!action.ClickedObjects.Contains(uniqueObjectKey))
{
DisplayHighlightFeedback(action, nextTarget, nextObjectName);
return true;
}
return false;
}
/// <summary>
/// 处理唯一物体的高亮
/// </summary>
private bool HandleUniqueObjectHighlight(ProcessStepDescription action, (string ObjectName, ProcessTargetType Type) nextTarget, string nextObjectName)
{
if (!action.ClickedObjects.Contains(nextObjectName))
{
DisplayHighlightFeedback(action, nextTarget, nextObjectName);
return true;
}
return false;
}
/// <summary>
/// 显示高亮反馈
/// </summary>
private void DisplayHighlightFeedback(ProcessStepDescription action, (string ObjectName, ProcessTargetType Type) nextTarget, string nextObjectName)
{
Debug.Log("提示:" + action.StepDescription);
OnStepProcessMessage?.Invoke("提示:" + action.StepDescription);
OnStepProcessDescriptionMessage?.Invoke($"{action.Description} {nextObjectName}");
if (nextTarget.Type == ProcessTargetType.Event)
{
OnTeachingPromptsObjects?.Invoke(null);
}
else if (nextTarget.Type == ProcessTargetType.Model)
{
HighlightObject(nextObjectName);
}
}
/// <summary>
/// 高亮指定的物体
/// </summary>
private void HighlightObject(string objectName)
{
try
{
var obj = GameObject.Find(objectName);
if (obj != null)
{
OnTeachingPromptsObjects?.Invoke(obj);
Debug.Log($"高亮显示:{objectName}");
}
}
catch (Exception e)
{
Debug.LogError($"异常高亮物体 {objectName}: {e.Message}");
}
}
#endregion
#region
/// <summary>
/// 根据当前模式处理相应的反馈逻辑
/// </summary>
public void HandleModeFeedback(ProcessMode mode, object stepOrAction)
{
switch (mode)
{
case ProcessMode.Teaching:
HandleTeachingModeFeedback(stepOrAction);
break;
case ProcessMode.Training:
HandleTrainingModeFeedback(stepOrAction);
break;
case ProcessMode.Practice:
HandlePracticeModeFeedback(stepOrAction);
break;
case ProcessMode.Assessment:
// 考核模式无提示
break;
}
}
/// <summary>
/// 处理教学模式反馈
/// </summary>
private void HandleTeachingModeFeedback(object stepOrAction)
{
ProcessStepDescription stringStep = (ProcessStepDescription)stepOrAction;
Debug.Log($"{stringStep.Title}---{stringStep.StepDescription}");
HighlightNextObject(stringStep);
}
/// <summary>
/// 处理培训模式反馈
/// </summary>
private void HandleTrainingModeFeedback(object stepOrAction)
{
if (stepOrAction is ProcessStepDescription action)
{
DisplayModeFeedback(action.StepDescription);
}
else if (stepOrAction is ProcessStep step && step.Actions.Count > 0)
{
DisplayModeFeedback(step.Actions[0].StepDescription);
}
}
/// <summary>
/// 处理练习模式反馈
/// </summary>
private void HandlePracticeModeFeedback(object stepOrAction)
{
if (stepOrAction is ProcessStepDescription practiceAction)
{
if (IsLastTargetObject(practiceAction))
{
ShowPracticeStep(practiceAction);
}
}
else if (stepOrAction is ProcessStep practiceStep && practiceStep.Actions.Count > 0)
{
if (IsLastTargetObject(practiceStep.Actions[0]))
{
ShowPracticeStep(practiceStep.Actions[0]);
}
}
}
/// <summary>
/// 显示模式反馈信息
/// </summary>
private void DisplayModeFeedback(string message)
{
Debug.Log($"培训模式:{message}");
OnStepProcessMessage?.Invoke("提示:" + message);
OnStepProcessDescriptionMessage?.Invoke("提示:" + message);
}
#endregion
#region
/// <summary>
/// 添加错误点击记录
/// </summary>
private void AddIncorrectClick(int stepIndex, string clickedObject)
{
if (!_incorrectClicksPerStep.ContainsKey(stepIndex))
{
_incorrectClicksPerStep[stepIndex] = new List<string>();
}
_incorrectClicksPerStep[stepIndex].Add(clickedObject);
Debug.Log($"步骤 {stepIndex + 1} 错误点击的物体: {clickedObject}");
}
/// <summary>
/// 判断是否为targetObjects中的最后一个物体
/// </summary>
private bool IsLastTargetObject(ProcessStepDescription action)
{
return action.CurrentObjectIndex == action.TargetObjects.Count - 1;
}
/// <summary>
/// 判断是否在练习或考核模式
/// </summary>
private bool IsInPracticeOrAssessment()
{
// return (MotionEngine.GetModule<DataConfigManager>().GetProcessMode() == ProcessMode.Assessment ||
// MotionEngine.GetModule<DataConfigManager>().GetProcessMode() == ProcessMode.Practice);
return (MotionEngine.GetModule<DataConfigManager>().GetProcessMode() != ProcessMode.Assessment);
}
/// <summary>
/// 设置当前模式
/// </summary>
public void SetCurrentMode(ProcessMode mode)
{
_currentMode = mode;
_currentStepIndex = 0;
_currentActionIndex = 0;
}
#endregion
#region
public void OnCreate(object createParam)
{
_processes = new Dictionary<string, ProcessCollection>();
_incorrectClicksPerStep = new Dictionary<int, List<string>>();
}
public void OnUpdate()
{
// 更新逻辑
}
public void OnDestroy()
{
// 清理逻辑
}
public void OnGUI()
{
// GUI逻辑
}
#endregion
#region
/// <summary>
/// 练习模式反馈
/// </summary>
/// <param name="nextStepOrAction"></param>
private void ShowPracticeStep(object nextStepOrAction)
{
if (nextStepOrAction is ProcessStepDescription practiceAction)
{
if (OnStepProcessMessage != null) OnStepProcessMessage(practiceAction.StepDescription);
OnStepProcessDescriptionMessage?.Invoke(practiceAction.StepDescription);
Debug.Log($"练习模式:{practiceAction.StepDescription}");
}
}
/// <summary>
/// 计算总分
/// </summary>
public int CalculateTotalScore()
{
float totalScore = 0;
Debug.Log("========== 开始计算总分 ==========");
// 如果当前模式没有在流程中,返回-1
if (!_processes.ContainsKey(_currentMode.ToString()))
{
Debug.LogError("当前模式不存在于流程中");
return -1;
}
// 获取当前流程的步骤
var steps = _processes[_currentMode.ToString()].Steps;
Debug.Log($"当前模式: {_currentMode}, 总步骤数: {steps.Count}");
// 遍历每个步骤
for (int i = 0; i < steps.Count; i++)
{
var step = steps[i];
Debug.Log($"\n===== 步骤 {i + 1}: {step.StepDescription} =====");
// 遍历步骤中的所有动作
for (int j = 0; j < step.Actions.Count; j++)
{
var action = step.Actions[j];
Debug.Log($"\n动作 {j + 1}: {action.Title}");
Debug.Log($"描述: {action.Description}");
Debug.Log($"满分: {action.Score}");
// 计算动作得分
float actionScore = 0;
// 检查当前动作是否有错误点击
var currentActionErrors = _incorrectClicksPerStep
.Where(kvp => kvp.Key == i)
.SelectMany(kvp => kvp.Value)
.ToList();
// 如果动作已完成(所有目标都已点击)
if (action.CurrentObjectIndex >= action.TargetObjects.Count)
{
// 如果没有错误点击,得满分
if (currentActionErrors.Count == 0)
{
actionScore = action.Score;
Debug.Log($"动作完成且无错误,得满分:{actionScore}");
}
else
{
// 有错误点击,计算扣分
float scorePerError = action.Score / action.TargetObjects.Count;
float deductedScore = scorePerError * currentActionErrors.Count;
actionScore = Math.Max(0, action.Score - deductedScore);
Debug.Log($"动作完成但有错误:");
Debug.Log($"错误点击次数: {currentActionErrors.Count}");
Debug.Log($"错误点击的物体: {string.Join(", ", currentActionErrors)}");
Debug.Log($"每个错误扣分: {scorePerError}");
Debug.Log($"总扣分: {deductedScore}");
Debug.Log($"实际得分: {actionScore}");
}
}
else
{
// 动作未完成得分为0
Debug.Log($"动作未完成得分为0");
}
totalScore += actionScore;
}
}
Debug.Log("\n========== 评分总结 ==========");
Debug.Log($"总分: {totalScore:F2}");
Debug.Log($"有错误步骤数: {_incorrectClicksPerStep.Count}");
Debug.Log("============================\n");
return (int)totalScore;
}
/// <summary>
/// 跳转到指定的步骤和动作
/// </summary>
/// <param name="targetStepIndex"></param>
/// <param name="targetActionIndex"></param>
public async UniTask JumpToProcessAsync(int targetStepIndex = 0, int targetActionIndex = 0)
{
if (!_processes.ContainsKey(_currentMode.ToString()))
{
Debug.LogError($"流程 {_currentMode.ToString()} 不存在!");
return;
}
var targetProcessCollection = _processes[_currentMode.ToString()];
// 检查目标步骤索引是否有效
if (targetStepIndex < 0 || targetStepIndex >= targetProcessCollection.Steps.Count)
{
Debug.LogError($"目标步骤索引 {targetStepIndex} 超出范围!");
return;
}
// 获取目标步骤
var targetStep = targetProcessCollection.Steps[targetStepIndex];
// 如果指定了动作索引,则检查其有效性
if (targetActionIndex < 0 || targetActionIndex >= targetStep.Actions.Count)
{
Debug.LogError($"步骤 {targetStepIndex} 下目标动作索引 {targetActionIndex} 超出范围!");
return;
}
// 遍历到目标步骤及动作
for (int stepIndex = 0; stepIndex < targetProcessCollection.Steps.Count; stepIndex++)
{
var step = targetProcessCollection.Steps[stepIndex];
// 检测场景加载状态,异步等待直到场景加载完成
for (int actionIndex = 0; actionIndex < step.Actions.Count; actionIndex++)
{
var action = step.Actions[actionIndex];
string str = action.TargetObjects.Count == 0 ? "没有目标!" : "";
foreach (var target in action.TargetObjects)
{
// 执行目标物体绑定的事件
await ExecuteObjectEventWhenSceneLoadedAsync(action, target.ObjectName); // 使用异步执行
}
// 如果达到目标步骤且达到目标动作,则停止遍历
if (stepIndex == targetStepIndex && actionIndex == targetActionIndex)
{
_currentStepIndex = stepIndex;
_currentActionIndex = actionIndex;
return;
}
}
}
}
/// <summary>
/// 检查场景加载状态并在场景加载完成后执行目标物体事件
/// </summary>
/// <param name="action">动作对象</param>
/// <param name="objectName">目标物体名称</param>
private async UniTask ExecuteObjectEventWhenSceneLoadedAsync(ProcessStepDescription action, string objectName)
{
// 如果场景正在加载,则等待场景加载完成
if (MotionEngine.GetModule<SceneManager>().IsLoading)
{
Debug.Log("场景正在加载,等待...");
// 使用 UniTask.WaitUntil 检查 SceneManagerSingleton 状态
await UniTask.WaitUntil(() => !(MotionEngine.GetModule<SceneManager>().IsLoading)); // 等待场景加载完成
}
action.ExecuteObjectEvent(objectName);
}
#endregion
}
}