using System; using FairyGUI; using System.IO; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; using System.Collections.Generic; using System.Linq; namespace BallKingdomCrush { public class UISequenceInfo { public BaseUI ui; public object args; public bool isLaunch; } public class UIParentInfo { public GComponent parent; public int index; } public static class UIMgrConst { public static bool IsEnableOpenUIAnim = true; public static bool IsEnableCloseUIAnim = true; public static Vector2 OpenUIAnimEffectScale = new Vector2(0.8f, 0.8f); public static float OpenUIAnimEffectTime = 0.3f; public static float ClickDownAnimEffectScale = 0.9f; } public sealed class UIManager : BaseUnityManager { private const bool IsUseSafeAreaAdaptive = false; private const bool IsSetButtonPivotCenter = false; private GameObject eventSystemGo; private EventSystem eventSystem; private StandaloneInputModule inputModule; private string uiDefaultFontName; private List commonPackageList = new List(); private Queue uiMaskCacheQueue = new Queue(); private Dictionary tempGObjectParentDict = new Dictionary(); private Dictionary uiLayerWindowDict = new Dictionary(); private List existDynamicUIs = new List(); private List tickUpdateUIs = new List(); private List normalUIRecord = new List(); private List uiSequenceQueue = new List(); private ObjectPool uiSequencePool = new ObjectPool(); private Vector2 uiCenterPos; private uint currUIOpenCumsumId; private int closeWorldRaycastRefCount; public void RegisterCommonPackage(string commonPackage) { if (!commonPackageList.Contains(commonPackage)) { commonPackageList.Add(commonPackage); } } public void RegisterCommonPackages(List commonPackages) { #if UNITY_EDITOR if (!UnityEngine.Application.isPlaying) { for (int i = 0; i < commonPackages.Count; i++) { string pakName = commonPackages[i]; if (UIPackage.GetByName(pakName) == null) { string packagePath = GetPackageUIPath(pakName); UIPackage.AddPackage(packagePath); } } return; } #endif for (int i = 0; i < commonPackages.Count; i++) { RegisterCommonPackage(commonPackages[i]); } } public void RegisterDefaultFont(string defaultFontName) { uiDefaultFontName = defaultFontName; LoadKit.Instance.LoadAsset("Font", defaultFontName, (font) => { if (font != null) { FontManager.RegisterFont(new DynamicFont(defaultFontName, font), defaultFontName); InitFguiConfig(); } }); } public void RegisterFont(params string[] otherFontNames) { for (int i = 0; i < otherFontNames.Length; i++) { string fontName = otherFontNames[i]; LoadKit.Instance.LoadAsset("Font", fontName, (font) => { if (font != null) { FontManager.RegisterFont(new DynamicFont(fontName, font), fontName); } }); } } public void DisposeAllUI() { while (existDynamicUIs != null && existDynamicUIs.Count != 0) { BaseUI ui = existDynamicUIs[existDynamicUIs.Count - 1]; Internal_CloseUI(ui, true); } UIPackage.RemoveAllPackages(); } public void SwitchSceneCloseAllUI() { for (int i = existDynamicUIs.Count - 1; i >= 0; i--) { if (existDynamicUIs.Count == 0) return; if (i >= existDynamicUIs.Count) { i = existDynamicUIs.Count; } BaseUI ui = existDynamicUIs[i]; if (ui.uiInfo.isSwitchSceneCloseUI) { Internal_CloseUI(ui); } } } public void Internal_OpenUI(BaseUI ui, object args = null) { if (!IsStartUp) return; LoadUI(ui, args, OpenUIProcess); } private void OpenUIProcess(BaseUI ui, object args) { existDynamicUIs.Add(ui); ui.Process_Bind(); ui.Process_OpenBefore(args); ui.Process_Open(args); AddNormalBaseUI(ui); NotificationEvent(AppMsg.UIEvent_UIOpen, ui); if (ui.uiInfo.isTickUpdate) { tickUpdateUIs.Add(ui); } if (ui.uiInfo.isClosetWorldRaycast) { SetWorldRaycasterEnabled(false); } if (UIMgrConst.IsEnableOpenUIAnim && ui.uiInfo.isNeedOpenAnim) { ui.KillOpenUIAnim(); OpenUIAnim(ui); } } public void Internal_CloseUI(BaseUI ui, bool isImmediatelyDispose = false) { if (existDynamicUIs.Contains(ui)) { existDynamicUIs.Remove(ui); RemoveNormalBaseUI(ui); if (ui.uiInfo.isTickUpdate) { tickUpdateUIs.Remove(ui); } ui.Process_Close(); if (UIMgrConst.IsEnableOpenUIAnim && ui.uiInfo.isNeedOpenAnim) { ui.KillOpenUIAnim(); } if (UIMgrConst.IsEnableCloseUIAnim && ui.uiInfo.isNeedCloseAnim) { ui.KillCloseUIAnim(); CloseUIAnim(ui, () => DestroyUI(ui, isImmediatelyDispose)); } else { DestroyUI(ui, isImmediatelyDispose); } } } public void Internal_HideUI(BaseUI ui) { ui.Process_Hide(); NotificationEvent(AppMsg.UIEvent_UIHide, ui); } public void Internal_DisplayUI(BaseUI ui, object args = null) { ui.Process_Display(args); NotificationEvent(AppMsg.UIEvent_UIDisplay, ui); } private void Update() { if (tickUpdateUIs.Count <= 0) return; for (int i = tickUpdateUIs.Count - 1; i >= 0; i--) { BaseUI ui = tickUpdateUIs[i]; if (ui == null) continue; if (ui.isClose) continue; if (!ui.uiInfo.isTickUpdate) continue; ui.OnUpdate(); } } private void LoadUI(BaseUI ui, object args, Action completeFunc) { AddUIPackage(ui.uiInfo.packageName, () => { CreateUI(ui, args, completeFunc); }); } private void CreateUI(BaseUI ui, object args, Action completeFunc) { if (string.IsNullOrEmpty(ui.uiName)) { return; } GObject gObject = UIPackage.CreateObject(ui.uiInfo.packageName, ui.uiInfo.assetName); string rawGoName = gObject.gameObjectName; string uiGoName = string.Format("({0}){1}", ui.uiName, rawGoName); gObject.gameObjectName = uiGoName; gObject.displayObject.name = uiGoName; ui.baseGObj = gObject; ui.baseUI = ui.baseGObj.asCom; ui.baseUI.fairyBatching = true; ui.baseUI.name = rawGoName; ui.rawGameObjectName = rawGoName; ui.gameObjectName = uiGoName; ui.baseUI.MakeFullScreen(); ui.baseUI.SetSize(GRoot.inst.width, GRoot.inst.height, false); if (IsUseSafeAreaAdaptive) { Rect safeArea = Screen.safeArea; ui.baseUI.SetPivot(0.5f, 0.5f, false); ui.baseUI.SetSize(GRoot.inst.width, safeArea.height); ui.baseUI.y = GRoot.inst.height - safeArea.height; } SetButtonClickDownEffect(ui.baseUI); if (ui.uiInfo.gComType == UIGComType.Window) { FairyGUI.Window gWindowUI = new() { contentPane = ui.baseUI }; ui.windowUI = gWindowUI; ui.windowUI.fairyBatching = true; ui.windowUI.Show(); } if (ui.uiInfo.isNeedUIMask) { GGraph uiMask = CreateUIMask(ui.uiInfo.uiMaskCustomColor); ui.uiMask = uiMask; ui.baseUI.AddChildAt(ui.uiMask, 0); if (ui.uiInfo.isNeedUIMaskCloseEvent) { ui.uiMask.onClick.Add(ui.CtrlCloseUI); } } ui.uiOpenCumsumId = ++currUIOpenCumsumId; ui.currLayer = (int)ui.uiInfo.layerType; uiLayerWindowDict[ui.currLayer].AddChild(ui.baseUI); completeFunc(ui, args); } private void AddUIPackage(string packageName, UnityAction onCompleted = null) { if (UIPackage.GetByName(packageName) == null) { if (packageName.Contains("PLoading") || packageName.Contains("A001_bigImg")) { var resUIPath = Path.Combine("FGUI", packageName); UIPackage.AddPackage(resUIPath); onCompleted?.Invoke(); } else { LoadKit.Instance.LoadAsset("FGUI", $"{packageName}_fui.bytes", textAsset => { UIPackage.AddPackage(textAsset.bytes, packageName, delegate (string s, string extension, Type type, out DestroyMethod destroyMethod) { destroyMethod = DestroyMethod.Unload; return LoadKit.Instance.LoadAsset("FGUI", $"{s}{extension}"); }); onCompleted?.Invoke(); }); } } else { onCompleted?.Invoke(); } } private string GetPackageUIPath(string packageName) { return string.Format("FGUI/{0}", packageName); } private void SetButtonClickDownEffect(GComponent gComponent) { GObject[] gObjects = gComponent.GetChildren(); for (int i = 0; i < gObjects.Length; i++) { GObject gObject = gObjects[i]; GButton gButton = gObject.asButton; if (gButton != null && gButton.mode == ButtonMode.Common) { if (!gButton.name.StartsWith("obtn_")) { if (IsSetButtonPivotCenter) { gButton.SetPivot(0.5f, 0.5f, false); } gButton.SetClickDownEffect(UIMgrConst.ClickDownAnimEffectScale); } continue; } GComponent otherGComponent = gObject.asCom; if (otherGComponent != null) { SetButtonClickDownEffect(otherGComponent); continue; } } } private GGraph CreateUIMask(Color color) { GGraph uiMask = null; if (uiMaskCacheQueue.Count > 0) { uiMask = GetUIMaskFormPool(); uiMask.color = color; uiMask.visible = true; } else { uiMask = new GGraph(); uiMask.gameObjectName = "UIMask"; uiMask.name = uiMask.gameObjectName; uiMask.SetPivot(0.5f, 0.5f, true); uiMask.SetXY(uiCenterPos.x, uiCenterPos.y); uiMask.DrawRect(5000, 5000, 0, Color.black, color); } return uiMask; } private GGraph GetUIMaskFormPool() { if (uiMaskCacheQueue.Count == 0) return null; return uiMaskCacheQueue.Dequeue(); } private void ReleaseUIMaskToPool(BaseUI ui) { if (ui.uiMask == null) return; GGraph uiMask = ui.uiMask; ui.baseUI.RemoveChild(uiMask); ui.uiMask = null; uiMask.onClick.Clear(); uiMask.visible = false; uiMaskCacheQueue.Enqueue(uiMask); } private void DestroyUI(BaseUI ui, bool isImmediatelyDispose) { if (ui.uiInfo.isNeedUIMask) { ReleaseUIMaskToPool(ui); } if (ui.uiInfo.isClosetWorldRaycast) { SetWorldRaycasterEnabled(true); } CloseAllSubUI(ui); DisposeUI(ui); ui.Process_Destroy(); NotificationEvent(AppMsg.UIEvent_UIClose, ui); QuitUISequence(ui); UnloadAsset(ui.uiInfo.packageName, ui.uiInfo.assetName); } private void UnloadAsset(string packageName, string assetName) { if (UIPackage.GetByName(packageName) != null) { LoadKit.Instance.RecycleAsset(packageName); } } private void DisposeUI(BaseUI ui) { uiLayerWindowDict[ui.currLayer].RemoveChild(ui.baseUI); if (ui.uiInfo.gComType == UIGComType.Window) { if (ui.windowUI != null) { ui.windowUI.Dispose(); ui.windowUI = null; } } ui.baseUI.Dispose(); ui.baseUI = null; } private void NotificationEvent(uint msgId, BaseUI ui) { AppDispatcher.Instance.Dispatch(msgId, ui); } private void OpenUIAnim(BaseUI ui) { GObject gObj = ui.baseUI; gObj.pivot = VectorConst.Half; gObj.SetScale(UIMgrConst.OpenUIAnimEffectScale.x, UIMgrConst.OpenUIAnimEffectScale.y); ui.openUiGTweener = gObj.TweenScale(Vector3.one, UIMgrConst.OpenUIAnimEffectTime).OnComplete(() => { ui.openUiGTweener = null; ui.Process_OpenUIAnimEnd(); }).SetIgnoreEngineTimeScale(true).SetEase(EaseType.BackOut); } private void CloseUIAnim(BaseUI ui, Action animEndCB) { GObject gObj = ui.baseUI; ui.closeUiGTweener = gObj.TweenScale(UIMgrConst.OpenUIAnimEffectScale, UIMgrConst.OpenUIAnimEffectTime) .OnComplete(() => { ui.Process_CloseUIAnimEnd(); animEndCB(); }).SetIgnoreEngineTimeScale(true).SetEase(EaseType.BackIn); } private void SetWorldRaycasterEnabled(bool enabled) { if (enabled) { closeWorldRaycastRefCount--; } else { closeWorldRaycastRefCount++; } if (closeWorldRaycastRefCount > 0) { CameraManager.Instance.SetWorldRaycasterEnabled(false); } else { CameraManager.Instance.SetWorldRaycasterEnabled(true); } } public BaseUI GetDynamicUI(string uiName) { foreach (BaseUI ui in existDynamicUIs) { if (ui.uiName == uiName) { return ui; } } return null; } public bool IsExistUI(string uiName) { foreach (BaseUI ui in existDynamicUIs) { if (ui.uiName == uiName) { return true; } } return false; } public void SetGObjectUILayer(UILayerType toLayer, params GObject[] objs) { foreach (GObject item in objs) { if (item.parent != null) { GComponent defaultParent = item.parent; int idx = defaultParent.GetChildIndex(item); if (!tempGObjectParentDict.ContainsKey(item)) { tempGObjectParentDict.Add(item, new UIParentInfo() { parent = defaultParent, index = idx, }); } } uiLayerWindowDict[(int)toLayer].AddChild(item); Vector2 pos = item.LocalToRoot(Vector2.zero, GRoot.inst); item.position = pos; } } public void ResetGObjectUILayer(params GObject[] objs) { foreach (GObject item in objs) { if (tempGObjectParentDict.ContainsKey(item)) { UIParentInfo parentInfo = tempGObjectParentDict[item]; int itemIndex = parentInfo.index; int parentCount = parentInfo.parent.GetChildrenCount(); if (parentCount != 0 && parentCount >= itemIndex) { parentInfo.parent.AddChildAt(item, itemIndex); } else { parentInfo.parent.AddChild(item); } Vector2 pos = parentInfo.parent.RootToLocal(item.position, GRoot.inst); item.position = pos; tempGObjectParentDict.Remove(item); } } } public bool AddNormalBaseUI(BaseUI ui) { if (ui.uiInfo.layerType != UILayerType.Normal) return false; BaseUI nowUI = GetNowBaseUI(); if (nowUI == null) { normalUIRecord.Add(ui); return true; } if (ui.uiInfo.packageName == nowUI.uiInfo.packageName && ui.uiInfo.assetName == nowUI.uiInfo.assetName) return false; normalUIRecord.Add(ui); return true; } public bool RemoveNormalBaseUI(BaseUI ui) { if (ui.uiInfo.layerType != UILayerType.Normal) return false; { normalUIRecord.Remove(ui); return true; } } public BaseUI GetNowBaseUI() { if (normalUIRecord.Count > 0) { return normalUIRecord[normalUIRecord.Count - 1]; } return null; } private void ExecuteOpenUISequence() { if (uiSequenceQueue.Count == 0) return; UISequenceInfo sequenceInfo = uiSequenceQueue[0]; if (sequenceInfo.isLaunch) return; sequenceInfo.isLaunch = true; BaseUI ui = sequenceInfo.ui; ui.Open(sequenceInfo.args); } private void QuitUISequence(BaseUI ui) { if (uiSequenceQueue.Count == 0) return; UISequenceInfo sequenceInfo = uiSequenceQueue[0]; if (sequenceInfo.ui != ui) return; uiSequenceQueue.Remove(sequenceInfo); uiSequencePool.Release(sequenceInfo); ExecuteOpenUISequence(); } public void CloseSubUI(BaseUI mainUI, SubUI subUI) { mainUI.subUIs.Remove(subUI); mainUI.baseUI.RemoveChild(subUI.baseGObj); subUI.baseUI.Dispose(); subUI.baseUI = null; } public void CloseAllSubUI(BaseUI mainUI) { List subUIs = mainUI.subUIs; if (subUIs == null) return; for (int i = subUIs.Count - 1; i >= 0; i--) { CloseSubUI(mainUI, subUIs[i]); } mainUI.subUIs.Clear(); mainUI.subUIs = null; } private void InitUIMgr() { GameObject uiRootParent = new GameObject(AppObjConst.UIGoName); uiRootParent.layer = LayerMaskConst.UI; AppObjConst.UIGo = uiRootParent; AppObjConst.UIGo.SetParent(AppObjConst.FrameGo); AppObjConst.UIGo.transform.position = new Vector3(CameraConst.UICameraPos.x, CameraConst.UICameraPos.y, 0); InitFguiConfig(); InitFguiSettings(); InitFguiLayers(); } private void InitEventSystem() { GameObject engineEventSystemGo = new GameObject("[EngineEventSystem]"); engineEventSystemGo.AddComponent(); engineEventSystemGo.transform.SetParent(LoveLegendCore.Instance.transform, false); } private void InitFguiConfig() { UIPackage.branch = null; AppObjConst.UIGo.AddComponent(); UIConfig.defaultFont = uiDefaultFontName; UIConfig.enhancedTextOutlineEffect = true; UIConfig.bringWindowToFrontOnClick = false; UIConfig.modalLayerColor = new Color(0f, 0f, 0f, (255f / 2f) / 255f); UIConfig.buttonSoundVolumeScale = 1; } private void InitFguiSettings() { AppObjConst.UICacheGo = new GameObject(AppObjConst.UICacheGoName); AppObjConst.UICacheGo.SetParent(AppObjConst.FrameGo); DisplayObject.CreateUICacheRoot(AppObjConst.UICacheGo.transform); Stage.Instantiate(); Stage.inst.gameObject.transform.SetParent(AppObjConst.UIGo.transform, false); Vector2Int uiResolution = AppConst.UIResolution; GRoot.inst.SetContentScaleFactor(uiResolution.x, uiResolution.y, UIContentScaler.ScreenMatchMode.MatchWidthOrHeight); uiCenterPos = new Vector2(GRoot.inst.width / 2f, GRoot.inst.height / 2f); } private void InitFguiLayers() { GRoot.inst.fairyBatching = false; for (int i = 0; i < UILayerConst.AllUILayer.Length; i++) { Window uiLayerWindow = new Window(); string name = UILayerConst.AllUILayer[i]; uiLayerWindow.fairyBatching = false; uiLayerWindow.name = name; uiLayerWindow.displayObject.name = name; uiLayerWindow.gameObjectName = uiLayerWindow.name; uiLayerWindow.sortingOrder = i * 100; uiLayerWindow.Show(); uiLayerWindow.fairyBatching = false; uiLayerWindowDict.Add(i, uiLayerWindow); } } private void InitFguiCommonPackages() { foreach (string commonPackage in commonPackageList) { Debug.Log($"Load Common Package: {commonPackage}"); LoadKit.Instance.LoadAsset("FGUI", $"{commonPackage}_fui.bytes", textAsset => { UIPackage.AddPackage(textAsset.bytes, commonPackage, delegate (string s, string extension, Type type, out DestroyMethod destroyMethod) { destroyMethod = DestroyMethod.Unload; return LoadKit.Instance.LoadAsset("FGUI", $"{s}{extension}"); }); }); } } private void InitFguiMultiLanguage() { if (AppConst.IsMultiLangue) { Stage.inst.currLang = Language.getCurrentLanguage(); } } public override void Init() { base.Init(); AppDispatcher.Instance.AddListener(AppMsg.InitUIMgr, (obj) => { // InitReporterGo(); InitEventSystem(); InitFguiCommonPackages(); InitFguiMultiLanguage(); }); InitUIMgr(); } public override void DisposeBefore() { base.DisposeBefore(); DisposeAllUI(); } protected override void OnDestroy() { base.OnDestroy(); GeneralKit.Destroy(AppObjConst.UIGo); commonPackageList.Clear(); existDynamicUIs.Clear(); tickUpdateUIs.Clear(); commonPackageList = null; existDynamicUIs = null; tickUpdateUIs = null; uiSequencePool.Dispose(); uiSequenceQueue.Clear(); uiSequencePool = null; uiSequenceQueue = null; } public void SetEventSystemGo(GameObject go) { eventSystemGo = go; eventSystem = eventSystemGo.GetComponent(); inputModule = eventSystemGo.GetComponent(); } public void SetSwitchLanguage(string switchLang) { if (AppConst.IsMultiLangue) { var multiLangueConfig = LoadKit.Instance.LoadAsset("TextAsset.I18N", "#JarvisI18N"); if (!multiLangueConfig) return; if (string.IsNullOrWhiteSpace(multiLangueConfig.text)) return; var multiLangueConfigList = SerializeUtil.ToObject>(multiLangueConfig.text); if (multiLangueConfigList == null || multiLangueConfigList.Count == 0) return; if (string.IsNullOrWhiteSpace(switchLang)) return; if (switchLang == Stage.inst.currLang) return; if (!multiLangueConfigList.Contains(switchLang)) return; var switchLangXML = LoadKit.Instance.LoadAsset("TextAsset.I18N", switchLang); if (!switchLangXML) return; if (string.IsNullOrWhiteSpace(switchLangXML.text)) return; var xml = new FairyGUI.Utils.XML(switchLangXML.text); UIPackage.SetStringsSource(xml); Stage.inst.currLang = switchLang; AppConst.CurrMultiLangue = switchLang; PlayerPrefsKit.WriteObject(PrefsKeyConst.UIMgr_switchLanguage, AppConst.CurrMultiLangue); AppDispatcher.Instance.Dispatch(AppMsg.App_SwitchLanguage); var uiPackageList = UIPackage.GetPackages(); foreach (var packageItem in uiPackageList.Select(uiPackage => uiPackage.GetItems()).SelectMany( packageItemList => packageItemList.Where(packageItem => packageItem.translated))) { packageItem.translated = false; } for (var i = existDynamicUIs.Count - 1; i >= 0; i--) { var ui = existDynamicUIs[i]; if (ui == null) continue; if (ui.isClose) continue; ui.ProcessFunc_SwitchLanguage(); } } } } }