1079 lines
40 KiB
C#
1079 lines
40 KiB
C#
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
|
||
}
|
||
} |