From 78d29bc8cf5ddc435adc5cf243ca50e5f9f7181d Mon Sep 17 00:00:00 2001 From: NguyenVanDat Date: Thu, 5 Mar 2026 10:26:21 +0700 Subject: [PATCH] update stack logic and document --- .../Scripts/UI/Dialogs/DlgInstall.cs | 2 +- .../PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs | 2 +- Assets/Scripts/CECUIManager.cs | 99 +++++++++++-------- UIManagerV1.md | 66 +++++++++++++ 4 files changed, 125 insertions(+), 44 deletions(-) create mode 100644 UIManagerV1.md diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs index c5169a8b3e..10457be6b0 100644 --- a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs @@ -657,7 +657,7 @@ namespace BrewMonster public void ShowTest(bool isStack) { - CECUIManager.Instance.ShowUI("Win_Disenchase",isStack); + CECUIManager.Instance.ShowUI("Win_Disenchase"); } } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs index 0ca4a7456b..27567d166d 100644 --- a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs @@ -3413,7 +3413,7 @@ namespace BrewMonster.UI // var dlgInstall =GetGameUIMan().GetDialog("Win_Enchase"); // dlgInstall.Show(true); - CECUIManager.Instance.ShowUI("Win_Enchase",true); + CECUIManager.Instance.ShowUI("Win_Enchase"); } //pShow1 = m_pAUIManager.GetDialog("Win_Enchase"); //pShow2 = m_pAUIManager.GetDialog("Win_Inventory"); diff --git a/Assets/Scripts/CECUIManager.cs b/Assets/Scripts/CECUIManager.cs index 6ce2c282b8..71319d3ccd 100644 --- a/Assets/Scripts/CECUIManager.cs +++ b/Assets/Scripts/CECUIManager.cs @@ -17,6 +17,9 @@ public class CECUIManager : MonoSingleton /// Stack of dialog names (front = index 0 = top / currently on top). private readonly List _uiStack = new(); + /// Max number of dialogs in stack; when exceeded on Push, the top is popped once. 0 = unlimited. + [SerializeField] private int _maxStack = 1; + [SerializeField] private HUDNPC npsUI; [SerializeField] private int currentTargetNPCID; @@ -36,7 +39,7 @@ public class CECUIManager : MonoSingleton // Task update timer / 任务更新计时器 private float _nextTaskUpdateTime = 0f; - private const float TASK_UPDATE_INTERVAL = .1f; + private const float TASK_UPDATE_INTERVAL = .1f; protected override void Awake() { @@ -59,11 +62,14 @@ public class CECUIManager : MonoSingleton ShowUI("Win_Hpmpxp"); } + private void Update() { // _fpsText.text = $"{Mathf.RoundToInt(1f / Time.deltaTime)}"; if (m_pDlgQuickBar1 != null) - { m_pDlgQuickBar1.UpdateShortcuts(); } + { + m_pDlgQuickBar1.UpdateShortcuts(); + } // Periodically update task UI (DlgTask and DlgTaskTrace) / 定期更新任务UI(DlgTask和DlgTaskTrace) if (Time.unscaledTime >= _nextTaskUpdateTime) @@ -84,16 +90,16 @@ public class CECUIManager : MonoSingleton if (inGameUIMan == null) return; var dlgTaskDialog = inGameUIMan.GetDialog("Win_Quest"); - var dlgTaskTraceDialog = inGameUIMan.GetDialog("Win_QuestMinion"); + var dlgTaskTraceDialog = inGameUIMan.GetDialog("Win_QuestMinion"); if (dlgTaskDialog != null && dlgTaskDialog is BrewMonster.Scripts.Task.UI.DlgTask dlgTaskForTrace) { if (dlgTaskTraceDialog != null && dlgTaskTraceDialog.IsShow()) { dlgTaskForTrace.RefreshTaskTrace(); } + dlgTaskForTrace.Tick(); } - } catch (System.Exception) { @@ -127,10 +133,12 @@ public class CECUIManager : MonoSingleton { npsUI.gameObject.SetActive(false); } + public CDlgQuickBar GetCDlgQuickBar() { return m_pDlgQuickBar1; } + private void TryHideUINPC(NPCDiedEvent obj) { if (obj.NPCID != currentTargetNPCID) return; @@ -143,41 +151,29 @@ public class CECUIManager : MonoSingleton /// Name of component ("DlgTask", "EC_InventoryUI") /// If true, push onto stack (show + SetAsLastSibling + track); if false, just show. /// just hide current ui- not pop - public void ShowUI(string componentName, bool isStack = true, bool hideCurrentUI = true) + public void ShowUI(string componentName) { if (string.IsNullOrEmpty(componentName) || canvasDlg == null) { if (canvasDlg == null) Debug.LogError("canvasDlg chưa được gán"); return; } - if (hideCurrentUI) + + var currentDialogue = GetCurrentDialog(); + if (currentDialogue != null) { - var currentDialogue = GetCurrentDialog(); - if(currentDialogue !=null) - { - currentDialogue.Show(false); - } + currentDialogue.Show(false); } - if (isStack) - { - Push(componentName); - return; - } - - var dialogue = GetInGameUIMan().GetDialog(componentName); - dialogue.Show(true); - dialogue.transform.SetAsLastSibling(); + Push(componentName); } + public void HideCurrentUIInStack() { - var currentDialogue = GetCurrentDialog(); - Debug.Log(_uiStack.Count); Pop(); - Debug.Log(_uiStack.Count); //show next one - currentDialogue = GetCurrentDialog(); - if(currentDialogue !=null) + var currentDialogue = GetCurrentDialog(); + if (currentDialogue != null) { currentDialogue.Show(true); } @@ -192,6 +188,9 @@ public class CECUIManager : MonoSingleton _uiStack.Remove(componentName); + if (_maxStack > 0 && _uiStack.Count >= _maxStack) + Pop(); + var dlg = GetInGameUIMan().GetDialog(componentName); if (dlg == null) return; @@ -267,6 +266,7 @@ public class CECUIManager : MonoSingleton if (dlg != null) dlg.Show(false); } } + _uiStack.Clear(); } @@ -282,10 +282,12 @@ public class CECUIManager : MonoSingleton { Debug.LogError("DlgMessageBox not found in InGameUIMan"); } + return null; } - public CDlgMessageBox ShowMessageBox(string title, string message, MessageBoxType messageBoxType, Action onClickedYes = null, Action onClickedNo = null) + public CDlgMessageBox ShowMessageBox(string title, string message, MessageBoxType messageBoxType, + Action onClickedYes = null, Action onClickedNo = null) { var msgBox = GetInGameUIMan().GetDialog("DlgMessageBox") as CDlgMessageBox; if (msgBox != null) @@ -304,8 +306,10 @@ public class CECUIManager : MonoSingleton { Debug.LogError("DlgMessageBox not found in InGameUIMan"); } + return null; } + public void UpdateSkillRelatedUI() { // ¸üм¼ÄÜÏà¹ØµÄ½çÃæÏÔʾ @@ -343,7 +347,8 @@ public class CECUIManager : MonoSingleton } else { - Debug.LogWarning("[CECUIManager] CDlgSkillTooltip not found in dialog resource. Tooltip will not be displayed."); + Debug.LogWarning( + "[CECUIManager] CDlgSkillTooltip not found in dialog resource. Tooltip will not be displayed."); return; } } @@ -382,14 +387,14 @@ public class CECUIManager : MonoSingleton if (string.Equals(pDlg.GetName(), "Game_Quit", StringComparison.OrdinalIgnoreCase)) { - } - else if ((string.Equals(pDlg.GetName(), "Game_TeachSkill", StringComparison.OrdinalIgnoreCase) && DialogBoxCommandIDs.IDOK == iRetVal) || - (string.Equals(pDlg.GetName(), "Game_LearnSkill", StringComparison.OrdinalIgnoreCase) && DialogBoxCommandIDs.IDOK == iRetVal)) + else if ((string.Equals(pDlg.GetName(), "Game_TeachSkill", StringComparison.OrdinalIgnoreCase) && + DialogBoxCommandIDs.IDOK == iRetVal) || + (string.Equals(pDlg.GetName(), "Game_LearnSkill", StringComparison.OrdinalIgnoreCase) && + DialogBoxCommandIDs.IDOK == iRetVal)) { if (string.Equals(pDlg.GetName(), "Game_TeachSkill", StringComparison.OrdinalIgnoreCase)) { - } else if (string.Equals(pDlg.GetName(), "Game_LearnSkill", StringComparison.OrdinalIgnoreCase)) @@ -416,12 +421,13 @@ public class CECUIManager : MonoSingleton else if (12 == nCondition) AddChatMessage(GetStringFromTable(11168), GP_CHAT_MISC);*/ } - } - else if (pDlg is DlgInstall dlgInstall && dlgInstall.GetInstallMode == DlgInstall.InstallMode.Disenchase && DialogBoxCommandIDs.IDOK == iRetVal) - { - UnityGameSession.c2s_CmdNPCSevClearEmbeddedChip(dlgInstall.FirstSlotIndex, dlgInstall.SelectedEquip.GetTemplateID()); - + else if (pDlg is DlgInstall dlgInstall && dlgInstall.GetInstallMode == DlgInstall.InstallMode.Disenchase && + DialogBoxCommandIDs.IDOK == iRetVal) + { + UnityGameSession.c2s_CmdNPCSevClearEmbeddedChip(dlgInstall.FirstSlotIndex, + dlgInstall.SelectedEquip.GetTemplateID()); + dlgInstall.Show(false); pHost.EndNPCService(); // m_pCurNPCEssence = NULL; @@ -567,6 +573,7 @@ public class CECUIManager : MonoSingleton gameUI.SetDependency(dialogResouce, canvasDlg); gameUI.Init(); } + return gameUI; } @@ -592,27 +599,29 @@ public class CECUIManager : MonoSingleton //todo: change this code to other place private int slot = 0; - public Sprite[] IconlistIvtr + public Sprite[] IconlistIvtr { - get + get { - if(m_iconlistIvtr == null || m_iconlistIvtr.Length == 0) + if (m_iconlistIvtr == null || m_iconlistIvtr.Length == 0) { m_iconlistIvtr = Resources.LoadAll("UI/IconSprites/iconlist_ivtrm_multisprite"); } - return m_iconlistIvtr; - } + + return m_iconlistIvtr; + } } public Sprite GetSpriteInListIvtr(string name) { - foreach(var item in IconlistIvtr) + foreach (var item in IconlistIvtr) { if (item.name.Equals(name)) { return item; } } + return null; } @@ -622,6 +631,7 @@ public class CECUIManager : MonoSingleton { EC_Game.GetGameRun().StartGame(0, Vector3.zero); } + CECShortcut pSC = EC_Game.GetGameRun().GetPoseCmdShortcuts().GetShortcut(slot); if (pSC != null) // && pObjSrc->GetDataPtr("ptr_CECShortcut") == pSC { @@ -629,6 +639,7 @@ public class CECUIManager : MonoSingleton pSC.Execute(); } } + public void OnClickFly() { CECHostPlayer hostPlayer = EC_Game.GetGameRun()?.GetHostPlayer(); @@ -637,6 +648,7 @@ public class CECUIManager : MonoSingleton hostPlayer.CmdFly(true); } } + public void OnChangeSkillShortcut() { CECHostPlayer hostPlayer = EC_Game.GetGameRun()?.GetHostPlayer(); @@ -665,10 +677,12 @@ public class CECUIManager : MonoSingleton } } } + public struct MessageBoxEvent { public int iRetVal; public AUIDialog pDlg; + public MessageBoxEvent(int retVal, AUIDialog dlg) { iRetVal = retVal; @@ -696,6 +710,7 @@ public class CECUIManager : MonoSingleton { chars[pos + j] = '*'; } + pos += badLwr.Length; } } diff --git a/UIManagerV1.md b/UIManagerV1.md new file mode 100644 index 0000000000..488a294ef0 --- /dev/null +++ b/UIManagerV1.md @@ -0,0 +1,66 @@ +# UI Manager (CECUIManager) – V1 + +`CECUIManager` is a singleton that manages in-game dialogs and UI. Access it via `CECUIManager.Instance`. Dialogs are resolved by name through `GetInGameUIMan().GetDialog(componentName)` (e.g. `"Win_Quest"`, `"DlgMessageBox"`). + +--- + +## Stack / Push / Pop + +The manager keeps an internal **UI stack**: an ordered list of dialog names. The **top** of the stack (index 0) is the dialog currently shown on top; Unity draws it last via `SetAsLastSibling()` on the canvas. Pushing adds to the top; popping removes from the top. + +### Behaviour + +- **Show UI (stacked)** + `ShowUI(string componentName)` hides the current top dialog (if any) and **pushes** the named dialog: it is shown, added to the front of the stack, and brought to front in the hierarchy. If the same name is already in the stack, it is moved to the top. + +- **Max stack size** + A `_maxStack` limit (Inspector, default 1) caps how many dialogs can be in the stack. When the stack is already at or above this limit and a new dialog is pushed, the **top** dialog is **popped once** (hidden and removed from the stack), then the new dialog is pushed. If `_maxStack <= 0`, the stack is unlimited. + +- **Hide current / pop** + `HideCurrentUIInStack()` **pops** the top dialog (hides it and removes it from the stack), then shows the new top (if any) and brings it to front. + +### Stack-related API + +| Method | Description | +|--------|--------------| +| `ShowUI(string componentName)` | Hides current top dialog and pushes the named dialog (show + add to front of stack + SetAsLastSibling). | +| `HideCurrentUIInStack()` | Pops the top dialog, then shows and brings to front the new top if the stack is not empty. | +| `GetCurrentUI()` | Returns the **name** of the dialog at the top of the stack, or `null` if the stack is empty. | +| `GetCurrentDialog()` | Returns the **AUIDialog** instance at the top of the stack, or `null` if empty. | +| `GetStackCount()` | Returns the number of dialogs in the stack (0 when empty). | +| `IsInStack(string componentName)` | Returns whether the given dialog name is in the stack. | +| `PeekStack(int index = 0)` | Returns the dialog **name** at `index` (0 = top) without removing it; returns `null` if index is out of range. | +| `ClearStack(bool hideAll = true)` | Clears the stack. If `hideAll` is true, calls `Show(false)` on every dialog in the stack before clearing. | + +### Internal notes + +- Stack is stored as `List`; front = index 0 = top. +- Push is implemented internally (show, insert at 0, SetAsLastSibling); when at capacity, the top is popped once before pushing. +- Pop is implemented internally (remove top, hide it, SetAsLastSibling on new top if any). + +--- + +## Other APIs + +- **Message box** + `ShowMessageBox(MessageBoxData)` / `ShowMessageBox(string title, string message, MessageBoxType, ...)` – show the DlgMessageBox with the given data or title/message/type. + +- **Skill tooltip** + `ShowSkillTooltip(string hintText, RectTransform sourceButton)` – show skill tooltip; `HideSkillTooltip()` – hide it. + +- **In-game UI / dialogs** + `GetInGameUIMan()` returns `CECGameUIMan`; use it to resolve dialogs by name and call `Show`/other methods as needed. + +- **Player options** + `ShowPlayerOptionsDialog(int characterId, Vector2 position)` – show the player options menu for the given character at the given position. + +- **Misc** + `GetCDlgQuickBar()`, `GetCurrentTargetNPCID()`, `FilterBadWords(ref string str)`, etc. as used elsewhere. + +--- + +## File reference + +- Manager: `Assets/Scripts/CECUIManager.cs` +- Base dialog: `Assets/PerfectWorld/Scripts/UI/Dialogs/AUIDialog.cs` +- In-game UI manager: `Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs`