using cfg.GuideCfg; using Framework; using Framework.Condition; using Gameplay.Level; using Gameplay.Net; using Gameplay.SubSystems; using NLDPB; using PhxhSDK; using System; using System.Collections.Generic; using UnityEngine; namespace Gameplay.Guide { /// /// 新手引导管理器 /// public sealed class GuideManager : Singlenton, IInitable, IUpdatable { #region 兜底强制跳过引导机制 /// /// 2秒内点击n次,弹出强制跳过引导 /// private readonly Queue queueTime = new(); /// /// 强制跳过引导检测时间(秒) /// private const double SKIP_GUIDE_CHECK_TIME = 2d; #endregion /// /// 未完成的引导步骤,注册的开始条件记录在这,key:reactionId,value:引导步骤配置 /// private Dictionary dicReaction; /// /// 完成的引导步骤id /// private HashSet setCompleteId; /// /// 当前的引导步骤 /// private GuideStepBase curStep; /// /// 完成的引导组id /// private int completeGroupId; /// /// 是否初始化引导 /// private bool isInitGuide; /// /// 是否任意点击 /// private bool isAnyClick; /// /// 点击引导按钮 /// private bool isClickButton; /// /// 战斗按钮是否显示 /// public bool IsFightButtonShow; /// /// 是否引导中 /// public bool IsGuiding { get { return null != curStep && curStep.IsGuiding; } } #region 引导特殊控制参数 /// /// 控制地板点击 /// public bool IsControlFloor = false; /// /// 指定地板索引 /// public int FloorIndex = -1; /// /// 是否响应任意点击 /// public bool IsEnableAnyClick = true; #endregion public void Init() { isInitGuide = false; InputManager.Instance.OnAnyClick += OnAnyClick; } public void Update(float deltaTime) { if (!IsGuiding) return; isAnyClick = false; } public void Release() { if (null != InputManager.Instance) InputManager.Instance.OnAnyClick -= OnAnyClick; isInitGuide = false; } /// /// 初始化引导 /// public void InitGuide() { #if GUIDE if (isInitGuide) return; dicReaction = new(); setCompleteId = new(); completeGroupId = (int)Actor.Instance.Logic.GetCount((uint)LogicID.NewGuideProgress); // 服务器所在新手引导组id Debug(PhxhSDK.Utils.Format("初始新手引导,当前服务器保存id:{0}", SeverGuideIdToString(completeGroupId))); foreach (DataGuideCfg cfg in TableManager.Instance.Tables.GuideConfig.DataList) { if (IsCompleted(cfg.GroupId)) // 已完成跳过 continue; dicReaction[ConditionReactor.Instance.AddReaction(cfg.BeginCondition, OnBeginGuideByCondition)] = cfg; } isInitGuide = true; #endif } /// /// 完成指定引导步骤 /// /// public void CompleteStep() { if (null == curStep) { EndGuide(true); return; } setCompleteId.Add(curStep.Cfg.Id); if (curStep.Cfg.IsFinalStep) { setCompleteId.Clear(); completeGroupId |= GetServerSaveId(curStep.Cfg.GroupId); // 按位存 NetHelper.SaveGuideProgress(LogicID.NewGuideProgress, completeGroupId); // 完成引导步骤,保存服务器 Debug(PhxhSDK.Utils.Format("服务器保存引导组id:{0},服务器实际保存id:{1}", curStep.Cfg.GroupId.ToString(), SeverGuideIdToString(completeGroupId))); } queueTime.Clear(); curStep = null; EventManager.Instance.Send(EventManager.EventName.Guide_Progress_Change); } /// /// 结束引导 /// public void EndGuide(bool isReEnterMain) { if (IsGuiding) curStep.EndGuide(); curStep = null; UIManager.Instance.CloseWindow(UINameConst.UI_Guide); UIManager.Instance.CloseWindow(UINameConst.UI_GuideTip); } /// /// 获取任意点击 /// /// public bool GetAnyClick() { if (isAnyClick) { isAnyClick = false; return true; } return false; } /// /// 点击指定引导按钮 /// /// public bool GetClickButton() { if (isClickButton) { isClickButton = false; return true; } return false; } /// /// 设置点击引导按钮状态 /// public void SetClickButtonState() { isClickButton = true; } /// /// 判断引导步骤是否完成 /// /// public bool IsStepComplete(int guideId) { DataGuideCfg cfg = TableManager.Instance.Tables.GuideConfig.Get(guideId); if (null == cfg) return false; if (IsCompleted(cfg.GroupId)) return true; return setCompleteId.Contains(guideId); } /// /// 引导Debug /// /// public static void Debug(string content) { DebugUtil.Log($"(新手引导){content}"); } /// /// 服务器保存的引导id转字符串 /// /// /// public string SeverGuideIdToString(int guideId) { int id = 1; int value = 1; while (value > 0 && value <= guideId) { PhxhSDK.Utils.Append(id.ToString()); PhxhSDK.Utils.Append(","); value <<= 1; ++id; } return PhxhSDK.Utils.GetStringBuilderToString(); } #region 私有方法 /// /// 创建引导步骤 /// private GuideStepBase CreateGuideStep(DataGuideCfg cfg) { switch (cfg.GuideType) { case E_GuideType.ClickFloor: // 点击地板 return ClickFloorGuide.Create(cfg); case E_GuideType.ClickCharacter: // 点击角色(根据角色点击地板) return ClickCharacterGuide.Create(cfg); case E_GuideType.ClickEnemy: // 点击敌人(根据敌人点击地板) return ClickEnemyGuide.Create(cfg); case E_GuideType.ArrowTip: // 指示UI提示 return ArrowUITipGuide.Create(cfg); case E_GuideType.ClickButton: // 点击引导按钮 return ClickButtonGuide.Create(cfg); case E_GuideType.LevelCancelSelectUnit: // 关卡取消单位选中 return LevelCancelSelectUnitGuide.Create(cfg); default: { throw new Exception($"新手引导,未处理类型:{cfg.GuideType}"); } } } /// /// 引导组是否完成 /// /// /// private bool IsCompleted(int groupId) { int id = GetServerSaveId(groupId); return id == (completeGroupId & id); } /// /// 获取服务器存储的id /// /// private int GetServerSaveId(int groupId) { int res = 1; for (int i = 0; i < groupId - 1; ++i) { res <<= 1; } return res; } /// /// 引导过程中的任意点击事件 /// private void OnAnyClick() { if (!IsGuiding) return; if (!IsEnableAnyClick) return; if (LoadingManager.Instance.IsRuning) return; isAnyClick = true; EventManager.Instance.Send(EventManager.EventName.Guide_Any_Click); /* DateTime curTime = CommonUtils.GetCurTime(CommonUtils.E_TimeType.Local); queueTime.Enqueue(curTime); while (queueTime.TryPeek(out DateTime firstTime)) { TimeSpan timeSpan = curTime - firstTime; if (timeSpan.TotalSeconds < SKIP_GUIDE_CHECK_TIME) break; queueTime.Dequeue(); } if (TableManager.Instance.Tables.GlobalConfig.SkipGuideClickCount <= queueTime.Count) { DebugUtil.Log($"{SKIP_GUIDE_CHECK_TIME}秒内连续点击{TableManager.Instance.Tables.GlobalConfig.SkipGuideClickCount}次,弹出强制跳过新手引导提示"); queueTime.Clear(); ShowSkipAllGuide(); }*/ } /// /// 通过条件触发开始引导 /// private void OnBeginGuideByCondition(bool conditionResult, object callBackParam, int reactionID) { if (!conditionResult) return; if (!dicReaction.TryGetValue(reactionID, out DataGuideCfg guideCfg)) return; ConditionReactor.Instance.RemoveReaction(reactionID); dicReaction.Remove(reactionID); try { curStep = CreateGuideStep(guideCfg); curStep.StartGuide(); } catch (Exception e) { DebugUtil.LogError($"跳过引导,错误信息:{e}"); } } #endregion } }