1666 lines
62 KiB
C#
1666 lines
62 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Threading.Tasks;
|
||
using UnityEngine;
|
||
using UnityEditor;
|
||
using UnityEngine.UI;
|
||
using Cysharp.Threading.Tasks;
|
||
using DefaultNamespace.ProcessMode;
|
||
using MotionFramework;
|
||
using TMPro;
|
||
|
||
namespace Framework.Manager
|
||
{
|
||
public class TutorialGuideManager : MonoBehaviour
|
||
{
|
||
public static TutorialGuideManager Instance;
|
||
|
||
|
||
public GameObject UIButton;
|
||
|
||
[Header("引导设置")]
|
||
[Tooltip("引导配置表")]
|
||
[SerializeField]
|
||
private TutorialGuideConfig guideConfig;
|
||
|
||
[Space(5)]
|
||
[Tooltip("是否启用引导跳转功能")]
|
||
[SerializeField]
|
||
private bool enableSmartGuideJump = true;
|
||
|
||
[Space(10)]
|
||
[Header("状态信息")]
|
||
[SerializeField, ReadOnly]
|
||
private int currentIndex = -1;
|
||
|
||
[SerializeField, ReadOnly] private bool isGuiding = false;
|
||
|
||
// [SerializeField, ReadOnly] private bool isGuideDisabled = false;
|
||
|
||
[Space(5)]
|
||
[Tooltip("是否启用UI显示状态检测")]
|
||
[SerializeField]
|
||
private bool enableUIStateCheck = true;
|
||
|
||
// 运行时存储UI对象的字典
|
||
private Dictionary<string, GameObject> uiObjects = new Dictionary<string, GameObject>();
|
||
|
||
// 待处理的UI注册队列
|
||
private HashSet<string> pendingUIObjects = new HashSet<string>();
|
||
|
||
// ProcessManager引用,用于获取当前流程步骤信息
|
||
// private ProcessManager processManager;
|
||
|
||
// 记录已匹配引导的步骤名称,避免同一步骤内重复匹配
|
||
private string lastMatchedStepName = string.Empty;
|
||
|
||
// 记录被隐藏但需要重新显示的步骤名称
|
||
private HashSet<string> hiddenStepsToRedisplay = new HashSet<string>();
|
||
|
||
// 记录当前步骤名称下已执行的引导数量,用于支持同一步骤下的多个引导按顺序执行
|
||
private Dictionary<string, int> stepExecutionCount = new Dictionary<string, int>();
|
||
|
||
// 安全帽点击特殊处理状态
|
||
private bool hasHelmetClicked = false; // 是否已点击安全帽
|
||
private string helmetClickedObjectName = string.Empty; // 点击的安全帽对象名称
|
||
private int helmetNextGuideIndex = -1; // 安全帽点击后的下一个引导索引
|
||
|
||
// 添加自定义特性类
|
||
public class ReadOnlyAttribute : PropertyAttribute
|
||
{
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
// 自定义属性绘制器
|
||
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
|
||
public class ReadOnlyDrawer : PropertyDrawer
|
||
{
|
||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||
{
|
||
GUI.enabled = false;
|
||
EditorGUI.PropertyField(position, property, label);
|
||
GUI.enabled = true;
|
||
}
|
||
}
|
||
|
||
|
||
#endif
|
||
[ContextMenu("开始引导")]
|
||
private void StartGuideIn()
|
||
{
|
||
StartGuide();
|
||
}
|
||
|
||
[ContextMenu("下一步")]
|
||
public async void NextGuideIn()
|
||
{
|
||
await TriggerNextGuide();
|
||
}
|
||
|
||
[ContextMenu("上一步")]
|
||
public async void LastStepIn()
|
||
{
|
||
await TriggerPrevGuide();
|
||
}
|
||
|
||
[ContextMenu("隐藏引导")]
|
||
private void HideGuideInEditor()
|
||
{
|
||
HideGuide();
|
||
}
|
||
|
||
[ContextMenu("打印引导配置")]
|
||
private void PrintGuideConfig()
|
||
{
|
||
if (guideConfig == null)
|
||
{
|
||
Debug.Log("TutorialGuideManager: 引导配置为空");
|
||
return;
|
||
}
|
||
|
||
var orderedSteps = guideConfig.GetOrderedSteps();
|
||
Debug.Log($"TutorialGuideManager: 引导配置总步骤数: {orderedSteps.Count}");
|
||
|
||
for (int i = 0; i < orderedSteps.Count; i++)
|
||
{
|
||
var step = orderedSteps[i];
|
||
Debug.Log($"TutorialGuideManager: 步骤 {i}: stepName='{step.stepName}', uiObjectName='{step.uiObjectName}', order={step.order}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停用引导系统
|
||
/// 调用此方法后,TriggerNextGuide将不再执行引导逻辑
|
||
/// </summary>
|
||
public void DisableGuide()
|
||
{
|
||
Destroy(this.gameObject);
|
||
//isGuideDisabled = true;
|
||
Debug.Log("TutorialGuideManager: 引导系统已停用");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启用引导系统
|
||
/// 重新启用引导系统,可以继续执行引导
|
||
/// </summary>
|
||
public void EnableGuide()
|
||
{
|
||
//isGuideDisabled = false;
|
||
Debug.Log("TutorialGuideManager: 引导系统已启用");
|
||
}
|
||
|
||
|
||
private void Awake()
|
||
{
|
||
Instance = this;
|
||
// 初始化ProcessManager引用
|
||
// processManager = MotionEngine.GetModule<ProcessManager>();
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
// InitializeGuideObjects();
|
||
// StartGuide();
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
if (Input.GetKeyDown(KeyCode.F1))
|
||
{
|
||
LastStepIn();
|
||
}
|
||
|
||
if (Input.GetKeyDown(KeyCode.F2))
|
||
{
|
||
NextGuideIn();
|
||
}
|
||
|
||
if (Input.GetKeyDown(KeyCode.F3))
|
||
{
|
||
MotionEngine.GetModule<ProcessManager>().JumpToProcessAsync(4, 2);
|
||
}
|
||
}
|
||
|
||
|
||
// 初始化引导对象
|
||
public void InitializeGuideObjects(string tipc)
|
||
{
|
||
Debug.Log(tipc);
|
||
guideConfig = Resources.Load<TutorialGuideConfig>("Tipsconfig/" + tipc);
|
||
|
||
|
||
if (guideConfig == null)
|
||
{
|
||
Debug.LogError("TutorialGuideManager: 引导配置为空!");
|
||
return;
|
||
}
|
||
|
||
var orderedSteps = guideConfig.GetOrderedSteps();
|
||
foreach (var step in orderedSteps)
|
||
{
|
||
// 在场景中查找UI对象
|
||
GameObject uiObject = GameObject.Find(step.uiObjectName);
|
||
if (uiObject != null)
|
||
{
|
||
RegisterUIObject(step.uiObjectName, uiObject);
|
||
}
|
||
else
|
||
{
|
||
// 将未找到的UI添加到待处理队列
|
||
pendingUIObjects.Add(step.uiObjectName);
|
||
Debug.LogWarning($"TutorialGuideManager: 未找到UI对象 {step.uiObjectName},已添加到待处理队列");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 注册UI对象
|
||
public void RegisterUIObject(string uiName, GameObject uiObject)
|
||
{
|
||
if (string.IsNullOrEmpty(uiName) || uiObject == null)
|
||
{
|
||
Debug.LogError("TutorialGuideManager: 注册UI对象失败,参数无效!");
|
||
return;
|
||
}
|
||
|
||
if (uiObjects.ContainsKey(uiName))
|
||
{
|
||
uiObjects[uiName] = uiObject;
|
||
}
|
||
else
|
||
{
|
||
uiObjects.Add(uiName, uiObject);
|
||
}
|
||
|
||
Debug.Log($"TutorialGuideManager: 注册UI对象 {uiName}");
|
||
|
||
// 从待处理队列中移除
|
||
pendingUIObjects.Remove(uiName);
|
||
|
||
// 如果当前正在引导,检查是否需要更新引导
|
||
if (isGuiding)
|
||
{
|
||
UpdateCurrentGuideIfNeeded(uiName);
|
||
}
|
||
}
|
||
|
||
// 取消注册UI对象
|
||
public void UnregisterUIObject(string uiName)
|
||
{
|
||
if (uiObjects.ContainsKey(uiName))
|
||
{
|
||
uiObjects.Remove(uiName);
|
||
Debug.Log($"TutorialGuideManager: 取消注册UI对象 {uiName}");
|
||
}
|
||
}
|
||
|
||
// 更新当前引导状态(如果需要)
|
||
private void UpdateCurrentGuideIfNeeded(string uiName)
|
||
{
|
||
var currentStep = GetCurrentStep();
|
||
if (currentStep != null && currentStep.uiObjectName == uiName)
|
||
{
|
||
// 当前引导步骤正好是刚注册的UI对象
|
||
if (uiObjects.TryGetValue(uiName, out GameObject uiObject))
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 更新当前引导 - {currentStep.stepName}");
|
||
GuideMask.Instance.CreateRectangleMask(uiObject);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查UI对象是否已注册
|
||
public bool IsUIObjectRegistered(string uiName)
|
||
{
|
||
return uiObjects.ContainsKey(uiName);
|
||
}
|
||
|
||
// 获取待注册的UI对象列表
|
||
public HashSet<string> GetPendingUIObjects()
|
||
{
|
||
return new HashSet<string>(pendingUIObjects);
|
||
}
|
||
|
||
// 开始引导流程
|
||
public async void StartGuide()
|
||
{
|
||
if (guideConfig == null)
|
||
{
|
||
Debug.LogError("TutorialGuideManager: 引导配置为空!");
|
||
return;
|
||
}
|
||
|
||
var orderedSteps = guideConfig.GetOrderedSteps();
|
||
if (orderedSteps.Count == 0)
|
||
{
|
||
Debug.LogError("TutorialGuideManager: 引导步骤为空!");
|
||
return;
|
||
}
|
||
|
||
if (isGuiding)
|
||
{
|
||
Debug.LogWarning("TutorialGuideManager: 引导流程已经在进行中!");
|
||
return;
|
||
}
|
||
|
||
Debug.Log($"TutorialGuideManager: 开始引导流程,总步骤数: {orderedSteps.Count}");
|
||
isGuiding = true;
|
||
currentIndex = -1;
|
||
await TriggerNextGuide();
|
||
}
|
||
|
||
public void TriggerNextGuideBtton()
|
||
{
|
||
TriggerNextGuide("下一步");
|
||
}
|
||
|
||
|
||
// 触发下一个引导步骤
|
||
public async UniTask<bool> TriggerNextGuide(string uiName = null)
|
||
{
|
||
if (!isGuiding)
|
||
{
|
||
Debug.LogWarning("引导流程未开始!");
|
||
return false;
|
||
}
|
||
|
||
// if (uiName == "关闭")
|
||
// {
|
||
// HideGuide();
|
||
// return;
|
||
// }
|
||
// 参数匹配逻辑:根据传入的uiName参数决定是否继续引导
|
||
if (!string.IsNullOrEmpty(uiName))
|
||
{
|
||
// 获取当前引导步骤
|
||
var currentStep = GetCurrentStep();
|
||
if (currentStep != null)
|
||
{
|
||
// 检查传入的uiName是否与当前步骤的uiObjectName匹配
|
||
if (uiName == "下一步")
|
||
{
|
||
}
|
||
else if (uiName == "大厅位置" || uiName == "地磅位置" || uiName == "电脑房" || uiName == "关闭" || uiName == "对话")
|
||
{
|
||
if (uiName != currentStep.uiObjectName)
|
||
{
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if (uiName != "对话")
|
||
currentIndex++;
|
||
}
|
||
|
||
HideGuide();
|
||
return true;
|
||
}
|
||
else if (uiName != currentStep.uiObjectName)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 参数匹配失败 - 传入的uiName: '{uiName}' 与当前步骤的uiObjectName: '{currentStep.uiObjectName}' 不匹配,停止引导");
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 参数匹配成功 - 传入的uiName: '{uiName}' 与当前步骤的uiObjectName: '{currentStep.uiObjectName}' 匹配,继续引导");
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("TutorialGuideManager: 无法获取当前引导步骤,跳过参数匹配检查");
|
||
return false;
|
||
}
|
||
}
|
||
else if (uiName == "")
|
||
{
|
||
// 传入空字符串,自动跳到下一步引导
|
||
Debug.Log("TutorialGuideManager: 传入空字符串,自动跳到下一步引导");
|
||
}
|
||
else
|
||
{
|
||
// uiName为null,使用原有逻辑
|
||
Debug.Log("TutorialGuideManager: 未传入uiName参数,使用原有引导逻辑");
|
||
}
|
||
|
||
if (GuideMask.Instance.guide != null)
|
||
GuideMask.Instance.guide.gameObject.SetActive(true);
|
||
|
||
bool hasJumpedToGuide = false;
|
||
|
||
|
||
var orderedSteps = guideConfig.GetOrderedSteps();
|
||
|
||
// 只有在没有跳转到引导步骤的情况下才执行currentIndex++
|
||
if (!hasJumpedToGuide)
|
||
{
|
||
currentIndex++;
|
||
}
|
||
|
||
if (currentIndex < orderedSteps.Count)
|
||
{
|
||
var currentStep = orderedSteps[currentIndex];
|
||
if (currentStep.isOpenUI)
|
||
{
|
||
// UIButton.SetActive(true);
|
||
}
|
||
else
|
||
{
|
||
UIButton.SetActive(false);
|
||
}
|
||
|
||
if (uiObjects.TryGetValue(currentStep.uiObjectName, out GameObject uiObject))
|
||
{
|
||
// 如果启用了UI状态检测,需要检查UI是否完全显示
|
||
if (enableUIStateCheck)
|
||
{
|
||
if (IsUIObjectFullyDisplayed(uiObject))
|
||
{
|
||
Debug.Log($"执行引导步骤: {currentStep.stepName} 查询的对象:{uiObject.name}");
|
||
GuideMask.Instance.CreateRectangleMask(uiObject);
|
||
// 如果UI对象是InputField,自动聚焦到该输入框
|
||
FocusInputFieldIfApplicable(uiObject);
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"UI对象 {uiObject.name} 存在但未完全显示,使用延迟查找");
|
||
// 使用UniTask来延迟查找UI
|
||
bool delayedResult = await DelayedFindUIObjectAsync(currentStep);
|
||
return delayedResult;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"执行引导步骤: {currentStep.stepName} 查询的对象:{uiObject.name}");
|
||
GuideMask.Instance.CreateRectangleMask(uiObject);
|
||
// 如果UI对象是InputField,自动聚焦到该输入框
|
||
FocusInputFieldIfApplicable(uiObject);
|
||
}
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
// 使用UniTask来延迟查找UI
|
||
bool delayedResult = await DelayedFindUIObjectAsync(currentStep);
|
||
return delayedResult;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 引导流程结束
|
||
isGuiding = false;
|
||
|
||
HideGuide();
|
||
Debug.Log("引导流程完成!");
|
||
DisableGuide();
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 只显示遮罩不执行下一步
|
||
/// </summary>
|
||
public void ShowMaskdon_tNext()
|
||
{
|
||
if (MotionFramework.MotionEngine.GetModule<ProcessManager>()._currentMode == DefaultNamespace.ProcessMode.ProcessMode.教学模式 ||
|
||
MotionFramework.MotionEngine.GetModule<ProcessManager>()._currentMode == DefaultNamespace.ProcessMode.ProcessMode.课程预览)
|
||
{
|
||
var currentStep = GetCurrentStep();
|
||
if (uiObjects.TryGetValue(currentStep.uiObjectName, out GameObject uiObject))
|
||
{
|
||
Debug.Log($"执行引导步骤: {currentStep.stepName} ");
|
||
GuideMask.Instance.CreateRectangleMask(uiObject);
|
||
}
|
||
}
|
||
}
|
||
|
||
//触发上一个引导步骤
|
||
public async UniTask<bool> TriggerPrevGuide()
|
||
{
|
||
if (!isGuiding)
|
||
{
|
||
Debug.LogWarning("引导流程未开始!");
|
||
return false;
|
||
}
|
||
|
||
var orderedSteps = guideConfig.GetOrderedSteps();
|
||
currentIndex--;
|
||
//Debug.Log($"------------------------>{currentIndex}");
|
||
if (currentIndex < orderedSteps.Count)
|
||
{
|
||
var currentStep = orderedSteps[currentIndex];
|
||
if (uiObjects.TryGetValue(currentStep.uiObjectName, out GameObject uiObject))
|
||
{
|
||
Debug.Log($"执行引导步骤: {currentStep.stepName} ");
|
||
GuideMask.Instance.CreateRectangleMask(uiObject);
|
||
}
|
||
else
|
||
{
|
||
// 使用UniTask来延迟查找UI
|
||
bool delayedResult = await DelayedFindUIObjectAsync(currentStep);
|
||
if (!delayedResult)
|
||
{
|
||
Debug.LogWarning($"延迟查找UI对象失败: {currentStep.uiObjectName}");
|
||
}
|
||
}
|
||
|
||
if (currentStep.isOpenUI)
|
||
{
|
||
// UIButton.SetActive(true);
|
||
}
|
||
else
|
||
{
|
||
UIButton.SetActive(false);
|
||
}
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
// 引导流程结束
|
||
isGuiding = false;
|
||
|
||
HideGuide();
|
||
Debug.Log("引导流程完成!");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private async UniTask<bool> DelayedFindUIObjectAsync(GuideStepConfig step)
|
||
{
|
||
// 记录尝试次数
|
||
int attempts = 0;
|
||
int maxAttempts = 10; // 最大尝试次数
|
||
int delayBetweenAttempts = 300; // 每次尝试间隔时间(毫秒)
|
||
|
||
Debug.Log($"开始延迟查找UI对象: {step.uiObjectName}");
|
||
|
||
// 将当前步骤的UI对象添加到待处理队列
|
||
pendingUIObjects.Add(step.uiObjectName);
|
||
|
||
GameObject foundObject = null;
|
||
|
||
// 初始延迟
|
||
await UniTask.Delay(100);
|
||
|
||
// 多次尝试查找UI
|
||
while (attempts < maxAttempts)
|
||
{
|
||
// 尝试查找UI对象
|
||
foundObject = FindAndRegisterUI(step.uiObjectName);
|
||
|
||
if (foundObject != null)
|
||
{
|
||
// 如果启用了UI状态检测,需要等待UI完全显示
|
||
if (enableUIStateCheck)
|
||
{
|
||
if (IsUIObjectFullyDisplayed(foundObject))
|
||
{
|
||
Debug.Log($"延迟查找成功且UI完全显示,第{attempts + 1}次尝试找到UI对象: {step.uiObjectName}");
|
||
GuideMask.Instance.CreateRectangleMask(foundObject);
|
||
// 如果UI对象是InputField,自动聚焦到该输入框
|
||
FocusInputFieldIfApplicable(foundObject);
|
||
return true; // 成功找到并显示UI对象
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"第{attempts + 1}次尝试找到UI对象但未完全显示: {step.uiObjectName},继续等待...");
|
||
foundObject = null; // 重置foundObject,继续等待
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"延迟查找成功,第{attempts + 1}次尝试找到UI对象: {step.uiObjectName}");
|
||
GuideMask.Instance.CreateRectangleMask(foundObject);
|
||
// 如果UI对象是InputField,自动聚焦到该输入框
|
||
FocusInputFieldIfApplicable(foundObject);
|
||
return true; // 成功找到并显示UI对象
|
||
}
|
||
}
|
||
|
||
// 等待一段时间再尝试
|
||
attempts++;
|
||
Debug.Log($"第{attempts}次尝试未找到UI对象: {step.uiObjectName},等待下一次尝试...");
|
||
await UniTask.Delay(delayBetweenAttempts);
|
||
}
|
||
|
||
// 如果尝试多次后仍未找到
|
||
if (foundObject == null)
|
||
{
|
||
Debug.LogWarning($"多次尝试({maxAttempts}次)后仍未找到UI对象: {step.uiObjectName}");
|
||
|
||
// 判断是否自动跳过
|
||
bool autoSkipMissingUI = false; // 可以添加配置选项控制此行为
|
||
if (autoSkipMissingUI)
|
||
{
|
||
Debug.Log($"自动跳过未找到的UI对象: {step.uiObjectName}");
|
||
TriggerNextGuide(); // 跳过当前步骤,继续下一个
|
||
return true; // 虽然UI对象未找到,但已自动跳过,视为成功
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"等待UI对象: {step.uiObjectName} 注册后继续引导");
|
||
return false; // 延迟查找失败
|
||
}
|
||
}
|
||
|
||
return false; // 默认返回失败
|
||
}
|
||
|
||
// 尝试查找并注册UI对象
|
||
private GameObject FindAndRegisterUI(string uiName)
|
||
{
|
||
// 尝试在场景中查找对象
|
||
GameObject uiObject = GameObject.Find(uiName);
|
||
|
||
// 尝试通过名称查找激活/非激活的UI对象
|
||
if (uiObject == null)
|
||
{
|
||
Canvas[] canvases = FindObjectsOfType<Canvas>(true);
|
||
foreach (Canvas canvas in canvases)
|
||
{
|
||
Transform foundTransform = FindChildRecursively(canvas.transform, uiName);
|
||
if (foundTransform != null)
|
||
{
|
||
uiObject = foundTransform.gameObject;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果找到了对象,注册它
|
||
if (uiObject != null)
|
||
{
|
||
RegisterUIObject(uiName, uiObject);
|
||
return uiObject;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
// 递归查找子对象
|
||
private Transform FindChildRecursively(Transform parent, string childName)
|
||
{
|
||
if (parent.name == childName)
|
||
return parent;
|
||
|
||
for (int i = 0; i < parent.childCount; i++)
|
||
{
|
||
Transform child = parent.GetChild(i);
|
||
Transform found = FindChildRecursively(child, childName);
|
||
if (found != null)
|
||
return found;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测UI对象是否完全显示
|
||
/// </summary>
|
||
/// <param name="uiObject">要检测的UI对象</param>
|
||
/// <returns>如果UI完全显示返回true,否则返回false</returns>
|
||
private bool IsUIObjectFullyDisplayed(GameObject uiObject)
|
||
{
|
||
if (uiObject == null)
|
||
{
|
||
Debug.LogWarning("TutorialGuideManager: UI对象为空,无法检测显示状态");
|
||
return false;
|
||
}
|
||
|
||
// 检查对象是否在层级中激活
|
||
if (!uiObject.activeInHierarchy)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: UI对象 {uiObject.name} 未在层级中激活");
|
||
return false;
|
||
}
|
||
|
||
// 检查CanvasGroup的透明度
|
||
CanvasGroup canvasGroup = uiObject.GetComponent<CanvasGroup>();
|
||
if (canvasGroup != null)
|
||
{
|
||
if (canvasGroup.alpha < 0.9f)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: UI对象 {uiObject.name} 透明度不足: {canvasGroup.alpha}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 检查RectTransform的尺寸
|
||
RectTransform rectTransform = uiObject.GetComponent<RectTransform>();
|
||
if (rectTransform != null)
|
||
{
|
||
if (rectTransform.sizeDelta.x <= 0 || rectTransform.sizeDelta.y <= 0)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: UI对象 {uiObject.name} 尺寸异常: {rectTransform.sizeDelta}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
Debug.Log($"TutorialGuideManager: UI对象 {uiObject.name} 显示状态检测通过");
|
||
return true;
|
||
}
|
||
|
||
// 获取当前引导步骤
|
||
public GuideStepConfig GetCurrentStep()
|
||
{
|
||
if (!isGuiding || currentIndex < 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var orderedSteps = guideConfig.GetOrderedSteps();
|
||
if (currentIndex < orderedSteps.Count)
|
||
{
|
||
return orderedSteps[currentIndex];
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
// 重置引导流程
|
||
public void ResetGuide()
|
||
{
|
||
isGuiding = false;
|
||
currentIndex = -1;
|
||
lastMatchedStepName = string.Empty; // 清除已匹配步骤记录
|
||
hiddenStepsToRedisplay.Clear(); // 清除待重新显示步骤记录
|
||
stepExecutionCount.Clear(); // 清除步骤执行计数记录
|
||
|
||
// 重置安全帽点击状态
|
||
hasHelmetClicked = false;
|
||
helmetClickedObjectName = string.Empty;
|
||
helmetNextGuideIndex = -1;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 标记步骤为已完成,从待重新显示列表中移除
|
||
/// </summary>
|
||
/// <param name="stepName">步骤名称</param>
|
||
public void MarkStepAsCompleted(string stepName)
|
||
{
|
||
if (hiddenStepsToRedisplay.Contains(stepName))
|
||
{
|
||
hiddenStepsToRedisplay.Remove(stepName);
|
||
Debug.Log($"TutorialGuideManager: 步骤 '{stepName}' 已标记为完成,从待重新显示列表中移除");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除所有待重新显示的步骤
|
||
/// </summary>
|
||
public void ClearHiddenStepsToRedisplay()
|
||
{
|
||
hiddenStepsToRedisplay.Clear();
|
||
Debug.Log("TutorialGuideManager: 已清除所有待重新显示的步骤");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取待重新显示的步骤列表
|
||
/// </summary>
|
||
/// <returns>待重新显示的步骤名称集合</returns>
|
||
public HashSet<string> GetHiddenStepsToRedisplay()
|
||
{
|
||
return new HashSet<string>(hiddenStepsToRedisplay);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 记录安全帽点击状态
|
||
/// </summary>
|
||
/// <param name="clickedObjectName">点击的对象名称</param>
|
||
public void RecordHelmetClick(string clickedObjectName)
|
||
{
|
||
if (string.IsNullOrEmpty(clickedObjectName))
|
||
{
|
||
Debug.LogWarning("TutorialGuideManager: 安全帽点击对象名称为空");
|
||
return;
|
||
}
|
||
|
||
hasHelmetClicked = true;
|
||
helmetClickedObjectName = clickedObjectName;
|
||
|
||
// 查找安全帽点击后的下一个引导索引
|
||
if (guideConfig != null)
|
||
{
|
||
var orderedSteps = guideConfig.GetOrderedSteps();
|
||
for (int i = 0; i < orderedSteps.Count; i++)
|
||
{
|
||
var step = orderedSteps[i];
|
||
if (!string.IsNullOrEmpty(step.stepName) && step.stepName.Contains("安全帽"))
|
||
{
|
||
// 找到安全帽引导步骤,记录下一个引导索引
|
||
if (i + 1 < orderedSteps.Count)
|
||
{
|
||
helmetNextGuideIndex = i + 1;
|
||
Debug.Log($"TutorialGuideManager: 记录安全帽点击状态 - 对象: '{clickedObjectName}', 下一个引导索引: {helmetNextGuideIndex}");
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取安全帽点击状态信息
|
||
/// </summary>
|
||
/// <returns>安全帽点击状态信息</returns>
|
||
public string GetHelmetClickStatus()
|
||
{
|
||
if (hasHelmetClicked)
|
||
{
|
||
return $"安全帽已点击 - 对象: '{helmetClickedObjectName}', 下一个引导索引: {helmetNextGuideIndex}";
|
||
}
|
||
else
|
||
{
|
||
return "安全帽未点击";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前引导步骤的UI对象
|
||
/// </summary>
|
||
/// <returns>当前引导步骤的UI对象,如果没有则返回null</returns>
|
||
public GameObject GetCurrentGuideUIObject()
|
||
{
|
||
if (!isGuiding || guideConfig == null)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var orderedSteps = guideConfig.GetOrderedSteps();
|
||
if (currentIndex < 0 || currentIndex >= orderedSteps.Count)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var currentStep = orderedSteps[currentIndex];
|
||
if (uiObjects.TryGetValue(currentStep.uiObjectName, out GameObject uiObject))
|
||
{
|
||
return uiObject;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
// 隐藏引导
|
||
public void HideGuide()
|
||
{
|
||
// GuideMask.Instance.InitializeMaterial();
|
||
|
||
// GuideMask.Instance.guide.gameObject.SetActive(false);
|
||
// UIButton.SetActive(false);
|
||
try
|
||
{
|
||
GuideMask.Instance.rectTransform.gameObject.SetActive(false);
|
||
GuideMask.Instance.rectTransform.gameObject.SetActive(false);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.Log(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public void ShowGuide()
|
||
{
|
||
// 检查流程是否全部完成
|
||
var processManager = MotionEngine.GetModule<ProcessManager>();
|
||
// if (processManager != null && processManager.IsProcessComplete())
|
||
// {
|
||
// Debug.Log("TutorialGuideManager: 流程已全部完成,不显示引导");
|
||
// return;
|
||
// }
|
||
|
||
Debug.Log("TutorialGuideManager: 流程未完成,显示引导");
|
||
// UIButton.SetActive(true);
|
||
GuideMask.Instance.rectTransform.gameObject.SetActive(true);
|
||
}
|
||
|
||
#region UI事件触发系统
|
||
|
||
/// <summary>
|
||
/// 触发当前UI对象的事件
|
||
/// 自动检测UI组件类型并触发相应的事件
|
||
/// </summary>
|
||
/// <returns>是否成功触发了事件</returns>
|
||
public void TriggerCurrentUIEvent()
|
||
{
|
||
var currentStep = GetCurrentStep();
|
||
if (currentStep == null)
|
||
{
|
||
Debug.LogWarning("TutorialGuideManager: 无法获取当前引导步骤,无法触发UI事件");
|
||
}
|
||
|
||
if (!uiObjects.TryGetValue(currentStep.uiObjectName, out GameObject uiObject))
|
||
{
|
||
Debug.LogWarning($"TutorialGuideManager: 当前UI对象 {currentStep.uiObjectName} 未注册,无法触发事件");
|
||
}
|
||
|
||
Debug.Log($"TutorialGuideManager: 开始触发UI对象 {currentStep.uiObjectName} 的事件");
|
||
TriggerUIEvent(uiObject);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发指定UI对象的事件
|
||
/// </summary>
|
||
/// <param name="uiObject">要触发事件的UI对象</param>
|
||
/// <returns>是否成功触发了事件</returns>
|
||
public bool TriggerUIEvent(GameObject uiObject)
|
||
{
|
||
if (uiObject == null)
|
||
{
|
||
Debug.LogError("TutorialGuideManager: UI对象为空,无法触发事件");
|
||
return false;
|
||
}
|
||
|
||
try
|
||
{
|
||
// // 尝试触发Button事件
|
||
// if (TryTriggerButtonEvent(uiObject))
|
||
// {
|
||
// Debug.Log($"TutorialGuideManager: 成功触发Button事件 - {uiObject.name}");
|
||
// return true;
|
||
// }
|
||
//
|
||
// 尝试触发Toggle事件
|
||
if (TryTriggerToggleEvent(uiObject))
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 成功触发Toggle事件 - {uiObject.name}");
|
||
return true;
|
||
}
|
||
//
|
||
// //// 尝试触发Slider事件
|
||
// //if (TryTriggerSliderEvent(uiObject))
|
||
// //{
|
||
// // Debug.Log($"TutorialGuideManager: 成功触发Slider事件 - {uiObject.name}");
|
||
// // return true;
|
||
// //}
|
||
//
|
||
// // 尝试触发InputField事件
|
||
// if (TryTriggerInputFieldEvent(uiObject))
|
||
// {
|
||
// Debug.Log($"TutorialGuideManager: 成功触发InputField事件 - {uiObject.name}");
|
||
// return true;
|
||
// }
|
||
//
|
||
// // 尝试触发Dropdown事件
|
||
// if (TryTriggerDropdownEvent(uiObject))
|
||
// {
|
||
// Debug.Log($"TutorialGuideManager: 成功触发Dropdown事件 - {uiObject.name}");
|
||
// return true;
|
||
// }
|
||
//
|
||
// //// 尝试触发Scrollbar事件
|
||
// //if (TryTriggerScrollbarEvent(uiObject))
|
||
// //{
|
||
// // Debug.Log($"TutorialGuideManager: 成功触发Scrollbar事件 - {uiObject.name}");
|
||
// // return true;
|
||
// //}
|
||
//
|
||
// //// 尝试触发ScrollRect事件
|
||
// //if (TryTriggerScrollRectEvent(uiObject))
|
||
// //{
|
||
// // Debug.Log($"TutorialGuideManager: 成功触发ScrollRect事件 - {uiObject.name}");
|
||
// // return true;
|
||
// //}
|
||
//
|
||
// // 尝试触发RawImage事件
|
||
// if (TryTriggerRawImageEvent(uiObject))
|
||
// {
|
||
// Debug.Log($"TutorialGuideManager: 成功触发RawImage事件 - {uiObject.name}");
|
||
// return true;
|
||
// }
|
||
//
|
||
// // 尝试触发Panel事件
|
||
// if (TryTriggerPanelEvent(uiObject))
|
||
// {
|
||
// Debug.Log($"TutorialGuideManager: 成功触发Panel事件 - {uiObject.name}");
|
||
// return true;
|
||
// }
|
||
//
|
||
// // 尝试触发Canvas事件
|
||
// if (TryTriggerCanvasEvent(uiObject))
|
||
// {
|
||
// Debug.Log($"TutorialGuideManager: 成功触发Canvas事件 - {uiObject.name}");
|
||
// return true;
|
||
// }
|
||
//
|
||
// // 尝试触发LayoutGroup事件
|
||
// if (TryTriggerLayoutGroupEvent(uiObject))
|
||
// {
|
||
// Debug.Log($"TutorialGuideManager: 成功触发LayoutGroup事件 - {uiObject.name}");
|
||
// return true;
|
||
// }
|
||
|
||
// 如果没有找到支持的UI组件,记录警告
|
||
Debug.LogWarning($"TutorialGuideManager: UI对象 {uiObject.name} 不包含支持的UI组件类型");
|
||
return false;
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.LogError($"TutorialGuideManager: 触发UI事件时发生错误: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试触发Button事件
|
||
/// </summary>
|
||
private bool TryTriggerButtonEvent(GameObject uiObject)
|
||
{
|
||
Button button = uiObject.GetComponent<Button>();
|
||
if (button != null && button.interactable)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到Button组件,正在触发点击事件 - {uiObject.name}");
|
||
button.onClick?.Invoke();
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试触发Toggle事件
|
||
/// </summary>
|
||
private bool TryTriggerToggleEvent(GameObject uiObject)
|
||
{
|
||
Toggle toggle = uiObject.GetComponent<Toggle>();
|
||
if (toggle != null && toggle.interactable)
|
||
{
|
||
// 处理Toggle状态
|
||
if (!toggle.isOn)
|
||
{
|
||
// 如果Toggle未勾选,则自动勾选
|
||
Debug.Log($"TutorialGuideManager: 检测到Toggle组件,当前状态为未勾选,正在自动勾选 - {uiObject.name}");
|
||
toggle.isOn = true;
|
||
toggle.onValueChanged?.Invoke(true);
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
// 如果Toggle已经勾选,则不做任何操作
|
||
Debug.Log($"TutorialGuideManager: 检测到Toggle组件,当前状态为已勾选,无需操作 - {uiObject.name}");
|
||
return true; // 返回true表示处理成功,即使没有实际改变状态
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试触发Slider事件
|
||
/// </summary>
|
||
private bool TryTriggerSliderEvent(GameObject uiObject)
|
||
{
|
||
Slider slider = uiObject.GetComponent<Slider>();
|
||
if (slider != null && slider.interactable)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到Slider组件,正在设置值 - {uiObject.name}");
|
||
// 将滑块值设置为最大值,触发onValueChanged事件
|
||
float newValue = slider.maxValue;
|
||
slider.value = newValue;
|
||
slider.onValueChanged?.Invoke(newValue);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试触发InputField事件
|
||
/// </summary>
|
||
private bool TryTriggerInputFieldEvent(GameObject uiObject)
|
||
{
|
||
InputField inputField = uiObject.GetComponent<InputField>();
|
||
if (inputField != null && inputField.interactable)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到InputField组件,正在设置焦点 - {uiObject.name}");
|
||
inputField.Select();
|
||
inputField.ActivateInputField();
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 如果UI对象是InputField,自动聚焦到该输入框
|
||
/// </summary>
|
||
/// <param name="uiObject">要检查的UI对象</param>
|
||
private void FocusInputFieldIfApplicable(GameObject uiObject)
|
||
{
|
||
if (uiObject == null)
|
||
{
|
||
Debug.LogWarning("TutorialGuideManager: UI对象为空,无法检查InputField");
|
||
return;
|
||
}
|
||
|
||
InputField inputField = uiObject.GetComponent<InputField>();
|
||
if (inputField != null && inputField.interactable)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到InputField组件,自动聚焦到输入框 - {uiObject.name}");
|
||
inputField.Select();
|
||
inputField.ActivateInputField();
|
||
}
|
||
|
||
TMP_InputField tmpInputField = uiObject.GetComponent<TMP_InputField>();
|
||
if (tmpInputField != null && inputField.interactable)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到TMP_InputField组件,自动聚焦到输入框 - {uiObject.name}");
|
||
tmpInputField.Select();
|
||
tmpInputField.ActivateInputField();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试触发Dropdown事件
|
||
/// </summary>
|
||
private bool TryTriggerDropdownEvent(GameObject uiObject)
|
||
{
|
||
Dropdown dropdown = uiObject.GetComponent<Dropdown>();
|
||
if (dropdown != null && dropdown.interactable)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到Dropdown组件,正在打开下拉列表 - {uiObject.name}");
|
||
dropdown.Show();
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试触发Scrollbar事件
|
||
/// </summary>
|
||
private bool TryTriggerScrollbarEvent(GameObject uiObject)
|
||
{
|
||
Scrollbar scrollbar = uiObject.GetComponent<Scrollbar>();
|
||
if (scrollbar != null && scrollbar.interactable)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到Scrollbar组件,正在设置值 - {uiObject.name}");
|
||
// 将滚动条值设置为1.0,触发onValueChanged事件
|
||
float newValue = 1.0f;
|
||
scrollbar.value = newValue;
|
||
scrollbar.onValueChanged?.Invoke(newValue);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试触发ScrollRect事件
|
||
/// </summary>
|
||
private bool TryTriggerScrollRectEvent(GameObject uiObject)
|
||
{
|
||
ScrollRect scrollRect = uiObject.GetComponent<ScrollRect>();
|
||
if (scrollRect != null && scrollRect.enabled)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到ScrollRect组件,正在滚动到底部 - {uiObject.name}");
|
||
// 滚动到底部,触发onValueChanged事件
|
||
scrollRect.normalizedPosition = Vector2.zero;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试触发RawImage事件
|
||
/// </summary>
|
||
private bool TryTriggerRawImageEvent(GameObject uiObject)
|
||
{
|
||
RawImage rawImage = uiObject.GetComponent<RawImage>();
|
||
if (rawImage != null)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到RawImage组件,正在高亮显示 - {uiObject.name}");
|
||
// 为RawImage添加高亮效果或触发相关事件
|
||
StartCoroutine(HighlightRawImage(rawImage));
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试触发Panel事件
|
||
/// </summary>
|
||
private bool TryTriggerPanelEvent(GameObject uiObject)
|
||
{
|
||
// 检查是否为Panel(通常包含CanvasGroup或Image组件)
|
||
CanvasGroup canvasGroup = uiObject.GetComponent<CanvasGroup>();
|
||
Image panelImage = uiObject.GetComponent<Image>();
|
||
|
||
if ((canvasGroup != null || panelImage != null) && uiObject.name.ToLower().Contains("panel"))
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到Panel组件,正在激活 - {uiObject.name}");
|
||
// 激活Panel并设置透明度
|
||
if (canvasGroup != null)
|
||
{
|
||
canvasGroup.alpha = 1.0f;
|
||
canvasGroup.interactable = true;
|
||
canvasGroup.blocksRaycasts = true;
|
||
}
|
||
|
||
uiObject.SetActive(true);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试触发Canvas事件
|
||
/// </summary>
|
||
private bool TryTriggerCanvasEvent(GameObject uiObject)
|
||
{
|
||
Canvas canvas = uiObject.GetComponent<Canvas>();
|
||
if (canvas != null)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到Canvas组件,正在激活 - {uiObject.name}");
|
||
// 激活Canvas并设置排序顺序
|
||
canvas.enabled = true;
|
||
canvas.sortingOrder = 100; // 设置较高的排序顺序确保显示在最前面
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试触发LayoutGroup事件
|
||
/// </summary>
|
||
private bool TryTriggerLayoutGroupEvent(GameObject uiObject)
|
||
{
|
||
LayoutGroup layoutGroup = uiObject.GetComponent<LayoutGroup>();
|
||
if (layoutGroup != null)
|
||
{
|
||
Debug.Log($"TutorialGuideManager: 检测到LayoutGroup组件,正在刷新布局 - {uiObject.name}");
|
||
// 强制刷新布局
|
||
LayoutRebuilder.ForceRebuildLayoutImmediate(uiObject.GetComponent<RectTransform>());
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 高亮RawImage的协程方法
|
||
/// </summary>
|
||
private IEnumerator HighlightRawImage(RawImage rawImage)
|
||
{
|
||
if (rawImage == null) yield break;
|
||
|
||
Color originalColor = rawImage.color;
|
||
float duration = 0.5f;
|
||
float elapsed = 0f;
|
||
|
||
// 创建高亮效果
|
||
while (elapsed < duration)
|
||
{
|
||
elapsed += Time.deltaTime;
|
||
float t = elapsed / duration;
|
||
|
||
// 在原始颜色和白色之间插值
|
||
rawImage.color = Color.Lerp(originalColor, Color.white, Mathf.Sin(t * Mathf.PI) * 0.5f + 0.5f);
|
||
|
||
yield return null;
|
||
}
|
||
|
||
// 恢复原始颜色
|
||
rawImage.color = originalColor;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前UI对象的组件类型信息
|
||
/// </summary>
|
||
/// <returns>当前UI对象的组件类型描述</returns>
|
||
public string GetCurrentUIComponentInfo()
|
||
{
|
||
var currentStep = GetCurrentStep();
|
||
if (currentStep == null)
|
||
{
|
||
return "无当前引导步骤";
|
||
}
|
||
|
||
if (!uiObjects.TryGetValue(currentStep.uiObjectName, out GameObject uiObject))
|
||
{
|
||
return $"UI对象 {currentStep.uiObjectName} 未注册";
|
||
}
|
||
|
||
return GetUIComponentInfo(uiObject);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定UI对象的组件类型信息
|
||
/// </summary>
|
||
/// <param name="uiObject">要检查的UI对象</param>
|
||
/// <returns>UI对象的组件类型描述</returns>
|
||
public string GetUIComponentInfo(GameObject uiObject)
|
||
{
|
||
if (uiObject == null)
|
||
{
|
||
return "UI对象为空";
|
||
}
|
||
|
||
var componentInfo = new System.Text.StringBuilder();
|
||
componentInfo.AppendLine($"UI对象: {uiObject.name}");
|
||
componentInfo.AppendLine($"激活状态: {uiObject.activeInHierarchy}");
|
||
componentInfo.AppendLine($"组件类型:");
|
||
|
||
// 检查各种UI组件
|
||
if (uiObject.GetComponent<Button>() != null)
|
||
componentInfo.AppendLine(" - Button");
|
||
if (uiObject.GetComponent<Toggle>() != null)
|
||
componentInfo.AppendLine(" - Toggle");
|
||
if (uiObject.GetComponent<Slider>() != null)
|
||
componentInfo.AppendLine(" - Slider");
|
||
if (uiObject.GetComponent<InputField>() != null)
|
||
componentInfo.AppendLine(" - InputField");
|
||
if (uiObject.GetComponent<Dropdown>() != null)
|
||
componentInfo.AppendLine(" - Dropdown");
|
||
if (uiObject.GetComponent<Scrollbar>() != null)
|
||
componentInfo.AppendLine(" - Scrollbar");
|
||
if (uiObject.GetComponent<ScrollRect>() != null)
|
||
componentInfo.AppendLine(" - ScrollRect");
|
||
if (uiObject.GetComponent<RawImage>() != null)
|
||
componentInfo.AppendLine(" - RawImage");
|
||
|
||
if (uiObject.GetComponent<Canvas>() != null)
|
||
componentInfo.AppendLine(" - Canvas");
|
||
if (uiObject.GetComponent<LayoutGroup>() != null)
|
||
componentInfo.AppendLine(" - LayoutGroup");
|
||
if (uiObject.GetComponent<Image>() != null)
|
||
componentInfo.AppendLine(" - Image");
|
||
if (uiObject.GetComponent<Text>() != null)
|
||
componentInfo.AppendLine(" - Text");
|
||
|
||
return componentInfo.ToString();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 删除步骤,跳转引导
|
||
|
||
/// <summary>
|
||
/// 判断当前动作是否为步骤的第一个动作
|
||
/// </summary>
|
||
/// <returns>如果是第一个动作返回true,否则返回false</returns>
|
||
private bool IsCurrentActionFirstInStep()
|
||
{
|
||
try
|
||
{
|
||
if (MotionEngine.GetModule<ProcessManager>() == null)
|
||
{
|
||
Debug.LogWarning("无法判断当前动作位置");
|
||
return false;
|
||
}
|
||
|
||
// 获取当前动作索引
|
||
var currentActionIndexField = typeof(ProcessManager).GetField("_currentActionIndex",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
|
||
if (currentActionIndexField != null)
|
||
{
|
||
int currentActionIndex = (int)currentActionIndexField.GetValue(MotionEngine.GetModule<ProcessManager>());
|
||
bool isFirstAction = currentActionIndex == 0;
|
||
|
||
Debug.Log($"TutorialGuideManager: 当前动作索引: {currentActionIndex}, 是否为第一个动作: {isFirstAction}");
|
||
return isFirstAction;
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("TutorialGuideManager: 无法获取当前动作索引");
|
||
return false;
|
||
}
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.LogError($"TutorialGuideManager: 判断当前动作位置时发生异常: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前动作的步骤描述
|
||
/// </summary>
|
||
/// <returns>当前动作的步骤描述,如果获取失败返回空字符串</returns>
|
||
private string GetCurrentActionStepDescription()
|
||
{
|
||
try
|
||
{
|
||
if (MotionEngine.GetModule<ProcessManager>() == null)
|
||
{
|
||
Debug.LogWarning("无法获取当前动作步骤描述");
|
||
return string.Empty;
|
||
}
|
||
|
||
// 通过反射获取当前动作的StepDescription
|
||
var processManager = MotionEngine.GetModule<ProcessManager>();
|
||
var currentStepIndexField = typeof(ProcessManager).GetField("_currentStepIndex",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
var currentActionIndexField = typeof(ProcessManager).GetField("_currentActionIndex",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
|
||
if (currentStepIndexField != null && currentActionIndexField != null)
|
||
{
|
||
int currentStepIndex = (int)currentStepIndexField.GetValue(processManager);
|
||
int currentActionIndex = (int)currentActionIndexField.GetValue(processManager);
|
||
|
||
// 获取当前动作
|
||
var currentProcessCollection = processManager.CurrentProcessCollection;
|
||
if (currentStepIndex >= 0 && currentStepIndex < currentProcessCollection.Steps.Count)
|
||
{
|
||
var currentStep = currentProcessCollection.Steps[currentStepIndex];
|
||
if (currentActionIndex >= 0 && currentActionIndex < currentStep.Actions.Count)
|
||
{
|
||
var currentAction = currentStep.Actions[currentActionIndex];
|
||
string stepDescription = currentAction.StepDescription;
|
||
|
||
// 如果动作的StepDescription为空,使用步骤的StepDescription
|
||
if (string.IsNullOrEmpty(stepDescription))
|
||
{
|
||
stepDescription = currentStep.StepDescription;
|
||
Debug.Log($"TutorialGuideManager: 动作StepDescription为空,使用步骤StepDescription: {stepDescription}");
|
||
}
|
||
|
||
Debug.Log($"TutorialGuideManager: 获取当前动作步骤描述: {stepDescription}");
|
||
return stepDescription;
|
||
}
|
||
}
|
||
}
|
||
|
||
Debug.LogWarning("TutorialGuideManager: 无法获取当前动作步骤描述");
|
||
return string.Empty;
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.LogError($"TutorialGuideManager: 获取当前动作步骤描述时发生异常: {ex.Message}");
|
||
return string.Empty;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定步骤名称下的引导步骤总数(包括顺序延续逻辑)
|
||
/// </summary>
|
||
/// <param name="stepName">步骤名称</param>
|
||
/// <returns>该步骤名称下的引导步骤总数</returns>
|
||
private int GetTotalGuideCountForStep(string stepName)
|
||
{
|
||
if (string.IsNullOrEmpty(stepName) || guideConfig == null)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
var orderedSteps = guideConfig.GetOrderedSteps();
|
||
int count = 0;
|
||
string lastNonEmptyStepName = string.Empty;
|
||
bool foundTargetStep = false;
|
||
|
||
// 遍历所有步骤,计算指定步骤名称下的引导数量
|
||
for (int i = 0; i < orderedSteps.Count; i++)
|
||
{
|
||
var step = orderedSteps[i];
|
||
|
||
// 如果当前步骤的stepName不为空
|
||
if (!string.IsNullOrEmpty(step.stepName))
|
||
{
|
||
// 如果找到了目标步骤,开始计数
|
||
if (step.stepName == stepName)
|
||
{
|
||
foundTargetStep = true;
|
||
count++;
|
||
lastNonEmptyStepName = step.stepName;
|
||
}
|
||
// 如果已经找到目标步骤,但遇到了新的步骤名称,停止计数
|
||
else if (foundTargetStep)
|
||
{
|
||
break;
|
||
}
|
||
// 更新lastNonEmptyStepName
|
||
else
|
||
{
|
||
lastNonEmptyStepName = step.stepName;
|
||
}
|
||
}
|
||
// 如果当前步骤的stepName为空
|
||
else
|
||
{
|
||
// 如果已经找到目标步骤,继续计数(顺序延续)
|
||
if (foundTargetStep)
|
||
{
|
||
count++;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果精确匹配没有结果,尝试包含匹配
|
||
if (count == 0)
|
||
{
|
||
foundTargetStep = false;
|
||
lastNonEmptyStepName = string.Empty;
|
||
|
||
for (int i = 0; i < orderedSteps.Count; i++)
|
||
{
|
||
var step = orderedSteps[i];
|
||
|
||
if (!string.IsNullOrEmpty(step.stepName))
|
||
{
|
||
if (step.stepName.Contains(stepName))
|
||
{
|
||
foundTargetStep = true;
|
||
count++;
|
||
lastNonEmptyStepName = step.stepName;
|
||
}
|
||
else if (foundTargetStep)
|
||
{
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
lastNonEmptyStepName = step.stepName;
|
||
}
|
||
}
|
||
else if (foundTargetStep)
|
||
{
|
||
count++;
|
||
}
|
||
}
|
||
}
|
||
|
||
return count;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据步骤名称查找引导步骤索引(包括顺序延续逻辑)
|
||
/// </summary>
|
||
/// <param name="stepName">步骤名称</param>
|
||
/// <param name="executionIndex">执行索引,用于查找同一步骤名称下的第几个引导(从0开始)</param>
|
||
/// <returns>找到的引导步骤索引,未找到返回-1</returns>
|
||
private int FindGuideStepByName(string stepName, int executionIndex = 0)
|
||
{
|
||
try
|
||
{
|
||
if (string.IsNullOrEmpty(stepName))
|
||
{
|
||
Debug.LogWarning("TutorialGuideManager: 步骤名称为空,无法查找引导步骤");
|
||
return -1;
|
||
}
|
||
|
||
if (guideConfig == null)
|
||
{
|
||
Debug.LogWarning("TutorialGuideManager: 引导配置为空,无法查找引导步骤");
|
||
return -1;
|
||
}
|
||
|
||
var orderedSteps = guideConfig.GetOrderedSteps();
|
||
|
||
// 收集所有匹配的引导步骤索引
|
||
List<int> matchedIndices = new List<int>();
|
||
string lastNonEmptyStepName = string.Empty;
|
||
bool foundTargetStep = false;
|
||
|
||
// 精确匹配(包括顺序延续逻辑)
|
||
for (int i = 0; i < orderedSteps.Count; i++)
|
||
{
|
||
var step = orderedSteps[i];
|
||
|
||
// 如果当前步骤的stepName不为空
|
||
if (!string.IsNullOrEmpty(step.stepName))
|
||
{
|
||
// 如果找到了目标步骤,开始收集
|
||
if (step.stepName == stepName)
|
||
{
|
||
foundTargetStep = true;
|
||
matchedIndices.Add(i);
|
||
lastNonEmptyStepName = step.stepName;
|
||
Debug.Log($"TutorialGuideManager: 精确匹配找到引导步骤: 索引={i}, stepName='{step.stepName}', uiObjectName='{step.uiObjectName}'");
|
||
}
|
||
// 如果已经找到目标步骤,但遇到了新的步骤名称,停止收集
|
||
else if (foundTargetStep)
|
||
{
|
||
break;
|
||
}
|
||
// 更新lastNonEmptyStepName
|
||
else
|
||
{
|
||
lastNonEmptyStepName = step.stepName;
|
||
}
|
||
}
|
||
// 如果当前步骤的stepName为空
|
||
else
|
||
{
|
||
// 如果已经找到目标步骤,继续收集(顺序延续)
|
||
if (foundTargetStep)
|
||
{
|
||
matchedIndices.Add(i);
|
||
Debug.Log($"TutorialGuideManager: 顺序延续找到引导步骤: 索引={i}, stepName='{step.stepName}', uiObjectName='{step.uiObjectName}', lastNonEmptyStepName='{lastNonEmptyStepName}'");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果精确匹配没有结果,尝试包含匹配
|
||
if (matchedIndices.Count == 0)
|
||
{
|
||
foundTargetStep = false;
|
||
lastNonEmptyStepName = string.Empty;
|
||
|
||
for (int i = 0; i < orderedSteps.Count; i++)
|
||
{
|
||
var step = orderedSteps[i];
|
||
|
||
if (!string.IsNullOrEmpty(step.stepName))
|
||
{
|
||
if (step.stepName.Contains(stepName))
|
||
{
|
||
foundTargetStep = true;
|
||
matchedIndices.Add(i);
|
||
lastNonEmptyStepName = step.stepName;
|
||
Debug.Log($"TutorialGuideManager: 包含匹配找到引导步骤: 索引={i}, stepName='{step.stepName}', uiObjectName='{step.uiObjectName}'");
|
||
}
|
||
else if (foundTargetStep)
|
||
{
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
lastNonEmptyStepName = step.stepName;
|
||
}
|
||
}
|
||
else if (foundTargetStep)
|
||
{
|
||
matchedIndices.Add(i);
|
||
Debug.Log($"TutorialGuideManager: 包含匹配顺序延续找到引导步骤: 索引={i}, stepName='{step.stepName}', uiObjectName='{step.uiObjectName}', lastNonEmptyStepName='{lastNonEmptyStepName}'");
|
||
}
|
||
}
|
||
}
|
||
|
||
Debug.Log($"TutorialGuideManager: 步骤名称 '{stepName}' 总共找到 {matchedIndices.Count} 个匹配的引导步骤");
|
||
|
||
// 根据执行索引返回对应的引导步骤
|
||
if (executionIndex >= 0 && executionIndex < matchedIndices.Count)
|
||
{
|
||
int guideIndex = matchedIndices[executionIndex];
|
||
Debug.Log($"TutorialGuideManager: 找到匹配的引导步骤: {stepName} -> 执行索引: {executionIndex}, 引导索引: {guideIndex}, UI对象: {orderedSteps[guideIndex].uiObjectName}");
|
||
return guideIndex;
|
||
}
|
||
else if (matchedIndices.Count > 0)
|
||
{
|
||
// 如果执行索引超出范围,说明该步骤的所有引导都已执行完毕
|
||
// 返回-1让系统按顺序继续执行下一个引导,而不是返回第一个匹配的
|
||
Debug.Log($"TutorialGuideManager: 执行索引超出范围,步骤 '{stepName}' 的所有引导步骤已执行完毕,将按顺序继续");
|
||
return -1;
|
||
}
|
||
|
||
Debug.Log($"TutorialGuideManager: 未找到匹配的引导步骤: {stepName}");
|
||
return -1;
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.LogError($"TutorialGuideManager: 查找引导步骤时发生异常: {ex.Message}");
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |