Tz2/Assets/Framework/Manager/TutorialGuideManager.cs

1666 lines
62 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;
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
}
}