This commit is contained in:
2026-04-28 00:07:42 +07:00
parent e051667037
commit 252489f48a
570 changed files with 2423 additions and 10875 deletions

View File

@@ -1,137 +1,140 @@
# **Game UI/UX Architecture & Routing Specification (Unity UI Toolkit Edition)**
Đặc tả Kiến trúc & Định tuyến Game UI/UX (Phiên bản Unity UI Toolkit)
Tài liệu này phác thảo toàn bộ bố cục cấu trúc, thiết kế trực quan, hiệu ứng chuyển động (animation) và logic định tuyến cho giao diện người dùng của game, được tối ưu hóa đặc biệt cho Unity UI Toolkit.
1. Hệ thống Thiết kế Tổng thể & Phân bổ Dự án
Để duy trì tính nhất quán, dễ bảo trì và làm việc nhóm hiệu quả, dự án tuân thủ nghiêm ngặt nguyên tắc chia tách giao diện (View) và logic (Controller).
1.1. Cấu trúc Thư mục (Directory Structure)
* /Assets/UI: Chứa TOÀN BỘ các file thiết kế giao diện (Visual Assets).
* Các file module hóa: MainMenu.uxml, Lobby.uxml (chứa cả UI Create Room & Join Room), Profile.uxml, Settings.uxml, MainGameHUD.uxml.
* Style toàn cục: Global.uss.
* Fonts, Hình ảnh, Icons, Custom Shaders cho UI.
* /Assets/Scripts/UI: Chứa TOÀN BỘ các script logic (.cs).
* UIManager.cs (Quản lý luồng chính).
* BaseUIController.cs (Lớp cha chứa logic chung).
* Các controller kế thừa: MainMenuController.cs, LobbyController.cs, v.v.
1.2. Hệ thống Controller (OOP - Kế thừa)
* BaseUIController.cs: Lớp cơ sở (Base Class) cho mọi màn hình UI. Định nghĩa các virtual method cốt lõi như Show(), Hide(), PlayTransitionIn(), PlayTransitionOut(), và đăng ký sự kiện.
* Inheritance: Các class khác (MainMenuController, LobbyController...) sẽ kế thừa (inherit) từ BaseUIController và override lại logic cụ thể của từng màn hình, giúp code không bị lặp lại (DRY - Don't Repeat Yourself).
1.3. UIManager & Hierarchy (Phân cấp)
* Kiến trúc Hierarchy: Trong Scene, UIManager là một GameObject root duy nhất gắn component UIDocument. Nó đóng vai trò là "Thành phần UI Cha", chứa toàn bộ các template UXML của các màn hình con (MainMenu, Lobby, Settings...) bên trong nó.
* Tính năng Debug: UIManager được thiết kế có custom Inspector, cho phép Dev/Designer có thể tick bật/tắt nhanh các màn hình trực tiếp trên Editor, và theo dõi/thay đổi nhanh các chỉ số debug (ping, trạng thái UI) theo thời gian thực mà không cần viết lệnh console.
* DontDestroyOnLoad: UIManager (và EventSystem đi kèm) sẽ được đánh dấu DontDestroyOnLoad. Nó tồn tại xuyên suốt qua nhiều Scene, cực kỳ lý tưởng để duy trì kết nối mạng (Networked UI) khi sử dụng Photon Fusion hoặc Unity Relay mà không bị ngắt quãng UI giữa các lần load map.
1.4. Xử lý WorldSpace UI (Giao diện trong không gian 3D)
Đối với các UI xuất hiện trong WorldSpace (VD: Thanh máu trên đầu nhân vật, Tên người chơi, Tương tác vật phẩm 3D):
* Không bỏ chung vào UIManager chính (vốn là ScreenSpace - Overlay).
* Sử dụng một GameObject 3D riêng biệt, gắn component UIDocument.
* Thay vì dùng PanelSettings của màn hình chính, gán cho nó một WorldSpace PanelSettings riêng hoặc sử dụng kĩ thuật render UI Toolkit vào một RenderTexture và chiếu nó lên một Quad (phụ thuộc vào phiên bản Unity đang dùng).
* Logic WorldSpace UI được quản lý cục bộ bởi Entity đó (VD: PlayerWorldUIController.cs).
1.5. Các Framework & Thư viện Cần thiết
1. PrimeTween: Hệ thống Animation cốt lõi (Xử lý các hiệu ứng lò xo, trượt, fade).
2. Unity New Input System: Xử lý input chuột đặc biệt và phím tắt.
3. UniTask: Quản lý luồng bất đồng bộ (chờ animation chuyển cảnh).
4. VContainer / Zenject: Dependency Injection.
5. Photon Fusion / UGS: Backend mạng.
2. Trải nghiệm Tương tác Cốt lõi (Core UX)
2.1. Logic "Con trỏ vô hình" & Pulse Effect (Chỉ ở Main Menu)
* Ẩn có điều kiện: Con trỏ chuột của hệ điều hành Cursor.visible = false CHỈ được áp dụng khi người chơi ở màn hình MainMenu. Ở các màn hình khác (Lobby, Settings), con trỏ chuột bình thường sẽ xuất hiện lại để dễ nhập liệu.
* Tương tác Top-Down: Có một "Con trỏ ảo" (Virtual Cursor) bằng UI Toolkit luôn nằm trên cùng (z-index cao nhất). Các thao tác Raycast sẽ bắn từ trên xuống.
* Hiệu ứng Pulse (Tính toán khoảng cách): Tương tự osu!lazer, hệ thống liên tục tính toán khoảng cách vector từ tọa độ con trỏ ảo đến tâm (center) của các component UI tương tác. Khi con trỏ tiến lại gần, component đó sẽ dần dần phóng to lên (Pulse/Scale) mượt mà dựa trên cự ly, tạo cảm giác UI "đang sống" và hút chuột.
2.2. Hiệu ứng Nút bấm lò xo (Osu! Spring Effect)
Tất cả các nút bấm (Button) trong game khi có sự kiện PointerDownEvent sẽ bị ép/nén (scale nhỏ lại, VD: 0.9x). Khi nhả chuột (PointerUpEvent), PrimeTween sẽ kích hoạt hiệu ứng nảy (elastic/bounce) vọt lố (overshoot) lên 1.1x rồi dao động từ từ về lại kích thước 1.0x. Cảm giác bấm phải rất đầm và có lực nén như lò xo.
2.3. Hiệu ứng Chuyển cảnh Toàn cục (Global Screen Transitions)
* Background tĩnh: Hình nền phía sau (hoặc cảnh 3D) luôn giữ nguyên, không bị giật hoặc nhấp nháy khi chuyển màn hình.
* Fly-Out & Fly-In:
* Khi gọi UIManager.Push(NewScreen): Màn hình cũ (BaseUIController.PlayTransitionOut()) sẽ chia làm 2 nửa, hoặc trượt toàn bộ bay ra khỏi mép màn hình (trái và phải).
* Ngay sau đó, Màn hình mới (BaseUIController.PlayTransitionIn()) sẽ bay vào từ 2 mép màn hình tiến vào giữa, kết thúc bằng một hiệu ứng Bounce nhẹ để tiếp đất.
3. Chi tiết Triển khai Từng Màn hình (Visual Layout & Components)
3.1. Main Menu (MainMenu.uxml)
Đây là màn hình phô diễn kỹ thuật UI với trạng thái máy trạng thái (State Machine) phức tạp.
* Trạng thái 1: Mới tải (Idle)
* Trực quan: Nền trong suốt hoặc Blur cảnh nền. Chỉ có một khối VisualElement hình tròn lớn (Logo) nằm chính giữa màn hình. Liên tục "đập" (pulse) scale từ 1.0 -> 1.05 theo nhịp nhạc.
* Trạng thái 2: Menu Hoạt động (Ribbon State)
* Chuyển từ State 1 -> 2: Khi click vào Logo, Logo trượt mượt mà sang vị trí lệch trái. Dải băng (VisualElement dạng hình chữ nhật dài tràn viền, có màu nền gradient) mở ra.
* Cấu trúc Ribbon (Flex-direction: Row): [ Settings ] (Nút vát chéo, icon răng cưa) -> [ LOGO ] -> [ Join ] -> [ Create ] -> [ Profile ] -> [ Exit ].
3.2. Lobby / Sảnh chờ (Lobby.uxml)
Màn hình Lobby sử dụng chung layout Chia đôi đường chéo (Diagonal Split).
* Khung Trái (Left Pane - 60% width): Nền trong suốt (background-color: clear), chỉ dùng để render hiển thị nhân vật 3D đứng trong sảnh.
* Khung Phải (Right Pane - 40% width): Nền kính mờ (Frosted glass/Blur backdrop).
Bên trong Khung Phải sẽ linh hoạt thay đổi nội dung tùy vào việc người chơi chọn Join hay Create:
* A. Giao diện Join Room: Có TextField tìm kiếm, DropdownField sắp xếp, và ScrollView chứa danh sách phòng.
* B. Giao diện Create Room: Các ô TextField nhập Tên phòng, Checkbox Toggle Mật khẩu, và nút CREATE khổng lồ.
* C. Giao diện Lounge: Chia chéo 50/50. Nền Host ám xanh, Guest ám cam. Cả 2 đều có nút Toggle "Ready".
3.3. Profile (Profile.uxml)
Áp dụng lại khung Split chéo tương tự Lobby. Khung trái chiếu nhân vật.
* Khung Phải (Right Pane):
* Header: Avatar (Mask tròn), Username lớn, Badge Rank.
* Stats: Các thanh ProgressBar tỷ lệ thắng, Label số trận, Elo Rating.
* Inventory: ScrollView chứa thẻ Skin/Banner.
3.4. HUD Trong Game (MainGameHUD.uxml)
Màn hình bao phủ toàn cảnh, set picking-mode: ignore gốc.
* Top-Left: 2 ProgressBar (Máu, Thể lực) dùng shader Fluid lấp đầy.
* Top-Right: Minimap dạng tròn/vuông bo viền, có fade opacity ở mép.
* Bottom-Left: Túi đồ ngang, 1 ô lớn (Đang cầm) và 3 ô nhỏ (Khe phím 1,2,3).
* Bottom-Center: Text Ping & FPS tối giản.
3.5. Menu Cài đặt / Settings (Settings.uxml)
Thiết kế dạng Sidebar trượt ngang (Slide-in Panel) từ lề trái và không chặn tương tác (Non-blocking UI).
* Bố cục 2 Cột (Split-Pane):
* Cột Trái (Tabs - 30%): Chứa các nút [ General ], [ Video ], [ Sound ], [ Control ]. Nút đang chọn sẽ đổi màu và có viền mép trái.
* Cột Phải (Details - 70%): Chứa một ScrollView duy nhất. Nội dung bên trong (Sliders, Dropdowns, Toggles) thay đổi động dựa theo Tab đang chọn ở cột trái.
* Nút Close: Một nút < Back hoặc [ X ] ở góc trên lề trái.
4. Logic Định tuyến (UIManager.cs)
[Game Launch]
|
[Main Menu] (State 1: Idle) <---(Timeout/No Input)---+
| |
(Click Logo) |
| |
[Main Menu] (State 2: Ribbon) -----------------------+
|
+-------(Click Logo Lần 2)------> [Lobby: Create]
|
+-------------------+-------------------+-------------------+
| | | |
v v v v
[Lobby: Create] [Lobby: Join] [Profile] [Settings (Sidebar)]
| | | |
v v | |
[Lounge (Host)] [Lounge (Guest)] +---(Back)----------+
| |
+--- [All Ready] ---+
|
(Host Clicks Start)
|
v
[IN-GAME HUD]
This document outlines the complete structural layout, visual design, animations, and routing logic for the game's user interface, specifically optimized for **Unity UI Toolkit**. It serves as a direct guide for the associated .uxml, .uss, and .cs controller files.
## **1\. Global Design System & Technical Approach**
To maintain consistency and reduce development overhead, the UI is built using UXML templates, styled with USS, and driven by specific C\# controllers mapped to your project structure.
### **Core UI Toolkit Architecture & File Mapping**
* **Controllers:** UI logic is managed by dedicated scripts (UIManager.cs, MainMenuController.cs, LobbyController.cs, ProfileController.cs, SettingsController.cs, HUDController.cs).
* **Views (UXML):** Layouts are defined in modular files (MainMenu.uxml, Lobby.uxml, Profile.uxml, Settings.uxml, MainGameHUD.uxml).
* **Styles (USS):** Global styling is handled in Global.uss.
### **Reusable UI Components & Visual Styles**
* **Cursorless Navigation (Focus-Based Selection):** The game does not display a standard OS cursor (Cursor.visible \= false). Instead, the UI interprets mouse movement natively to determine focus. As the hidden cursor moves, the nearest interactive element highlights and scales up. This provides a smart, "snapping" feel to UI selection without needing a visible pointer.
* **The "Osu-Style" Slanted Button:** A custom VisualElement utilizing USS transform: skew(...) properties to create angled edges. Contains an inner text element counter-skewed to remain horizontal.
* **Diagonal Split-Screen Container:** Used in Lobby and Profile screens. Instead of a boring vertical line, the split is a sharp diagonal line (like a lightning slash).
* *Implementation:* Achieved by applying a negative skew to the Right Pane's parent container, and a positive skew to its inner content to keep text readable.
* *Left Pane (approx. 60% width):* Transparent background, showing the Unity 3D Camera rendering the character model and procedural map.
* *Right Pane (approx. 40% width):* Frosted glass effect.
* **Right-Pane Navigation Bar (Top):** Unlike standard top bars, the navigation is contained *entirely within the Right Pane*.
* \[ \< Back \] button is pinned to the Top-Left of the right panel.
* \[ Settings \] button is pinned to the Top-Right of the right panel.
* **Modal Overlay Backdrop:** A full-screen VisualElement with a semi-transparent black USS background. Used to dim the screen and block inputs behind the Settings panel, Password Prompts, and Confirmation Dialogs.
## **2\. Screen-by-Screen Implementation Details**
### **2.1. Main Menu (MainMenu.uxml & MainMenuController.cs)**
Inspired by osu\!lazer, this screen is highly dynamic and relies on smooth, physics-based transitions.
* **State 1: Initial Load (Idle)**
* **Visuals:** The background is a heavily blurred, atmospheric map preview. The OS cursor is hidden. Dead center is the **Beat Logo**.
* **Animation:** The Beat Logo dynamically pulses (scales up and down) precisely in sync with the audio source's beat/BPM. There are no other UI elements visible.
* **State 2: Active Menu (Transition & Active State)**
* **Interaction:** The user moves the mouse toward the center, the logo snaps into focus (glows), and they click.
* **Smooth Transition:** 1\. The Beat Logo shrinks slightly and smoothly translates from the dead center to its designated position in the horizontal menu row.
2\. A horizontal VisualElement container (the ribbon) unfolds from behind the logo using an elastic/spring animation.
3\. The slanted buttons slide out from the logo's position horizontally: \[Settings\] to the left, and \[Create Room\], \[Join Room\], \[Profile\], \[Exit\] to the right. They fade in (opacity: 1\) with a staggered C\# coroutine delay to create a cascading reveal effect.
### **2.2. Lobby (Lobby.uxml & LobbyController.cs)**
* **View Setup:** Utilizes the Diagonal Split-Screen Container. The view is dynamic; the Right Pane content switches based on whether the user selected "Join Room" or "Create Room" from the Main Menu.
* **Left Pane (Transparent):** Shows the player model and live procedural background map generation.
* **Right Pane (Static UI Area) \- Divided into Two Stages:**
* **Navigation (Shared):** \[ \< Back \] (Top-Left) and \[ Settings \] (Top-Right) are always visible.
* **Stage 1: Join Room (Room Browser)**
* *Triggered when entering via the \[Join Room\] button.*
* **Filters & Search:** The top of this view includes a TextField for **Searching** by Room Name, and a DropdownField (or sort icons) for **Sorting** the list (e.g., by Ping, Name, or Open Slots).
* **Room List:** A ScrollView dynamically populated with active rooms via C\#. Each list item displays the Room Name, Host Name, Ping, and a clear "Lock" icon if the room requires a password.
* **Interaction Logic:**
* Clicking an unlocked room immediately joins it and transitions the player to the Lounge.
* Clicking a locked room triggers a Modal Overlay popup asking for the password (contains a TextField and \[Submit\] / \[Cancel\] buttons) before joining.
* **Stage 2: Create Room (Configuration Setup)**
* *Triggered when entering via the \[Create Room\] button.*
* **Content:** Dedicated input fields for setting up a new session. Uses TextField for Room Name and an optional Password. *Note: Max players is strictly locked to 2, so no dropdown field is necessary.*
* **Toggles:** A "Require password to join" checkbox/toggle is present to enable or disable the password field.
* **Action:** A large "CREATE ROOM" button sits at the bottom. Clicking this instantiates the room on the server and transitions the player to the Lounge as the Host.
### **2.3. Profile (Profile.uxml & ProfileController.cs)**
* **Layout:** Reuses the Diagonal Split-Screen structure. The 3D camera shifts to a "Profile Studio" lighting setup for the Left Pane.
* **Right Pane Content:**
* **Navigation:** \[ \< Back \] (Top-Left) and \[ Settings \] (Top-Right).
* **Header:** Player Avatar (circular mask), Username, and Rank/Level badge.
* **Statistics:** Text labels and progress bars for Win Rate, Matches Played, and current Elo/Rating.
* **Customization:** A scrollable grid view at the bottom for equipping character skins or banners.
### **2.4. Lounge / Pre-Match (Lobby.uxml extensions or new UXML)**
Since the game is strictly 2-player, the Lounge is an intimate VS screen.
* **Layout:** The screen is split directly down the middle via a diagonal slash. Player 1 (Host) is on the left, Player 2 (Guest) is on the right.
* **Visuals:** Each half has a distinctly tinted background color (e.g., Blue for Host, Red/Orange for Guest) behind the transparent 3D character models.
* **Navigation:** \[ \< Back \] (Top-Left) and \[ Settings \] (Top-Right) overlay the entire screen.
* **Confirmation Logic:** Clicking \[ \< Back \] triggers a Modal Overlay popup: "Are you sure you want to leave the room? \[Yes\] \[No\]".
* **Ready System:**
* Bottom of each player's panel has a Ready toggle.
* The Host has a large \[ START GAME \] button in the bottom center.
* **Logic:** The \[ START GAME \] button is greyed out (SetEnabled(false)) until *both* players have toggled their status to "Ready".
### **2.5. In-Game HUD (MainGameHUD.uxml & HUDController.cs)**
The HUD strictly follows a "Zero Obstruction" philosophy with specific interactive zones.
* **Area 1: The 3D World (Background)**
* The root UI element is fully transparent and set to picking-mode: ignore so all mouse inputs register for aiming/gameplay.
* **Area 2: Player Stats (Top Left)**
* **Visuals:** Liquid progress bars for Health and Stamina. Instead of standard flat bars, these utilize a UI Toolkit custom shader or animated sprite mask to look like fluid filling/draining from a container.
* **Area 3: Minimap (Top Right)**
* **Visuals:** The minimap continuously updates based on player position. It features a soft fade effect at its borders (achieved via an alpha gradient mask/texture applied to the VisualElement), seamlessly blending it into the gameplay view without harsh square borders.
* **Area 4: Inventory (Bottom Left)**
* **Structure:** 4 distinct slots: \[ Current Holding \] (larger, highlighted), and three quick-slots \[ I \], \[ II \], \[ III \].
* **Logic:** Pressing 1, 2, or 3 on the keyboard fires an event in HUDController.cs. The selected item's icon swaps into the \[ Current Holding \] slot, and the index updates visually to show what is equipped.
* **Area 5: Game Info (Bottom Center)**
* **Visuals:** Minimalist text displaying current Ping (ms) and FPS.
* **Auto-Hide Logic:**
* To maximize visibility, Area 2 (Stats), Area 4 (Inventory), and Area 5 (Game Info) dynamically fade out.
* In HUDController.cs, track input activity (mouse movement, ability use, damage taken). If Time.time \- lastActivityTime \> 5f (5 seconds), tween the opacity of these areas to 0 or 0.2. Any new interaction instantly fades them back to 1\.
### **2.6. Settings Menu (Settings.uxml & SettingsController.cs)**
* **Structure:** A floating, centered panel (e.g., occupies roughly 60% width and 70% height) rather than a full-screen view. It sits on top of a full-screen semi-transparent backdrop that blocks interactions with the underlying UI.
* **Global Shortcut:** The Settings menu can be toggled open or closed from anywhere in the game by pressing **Ctrl \+ O**.
* **Visuals:** The panel has rounded corners and distinct styling to elevate it from the background.
* **Layout:** Standard settings configuration (Audio sliders, Graphics toggles) using UI Toolkit elements, with a prominent \[ X Close \] button in the top right corner of the panel.
## **3\. C\# State Machine & Routing Logic (UIManager.cs)**
To manage UXML templates, UIManager.cs controls the flow using a stack-based approach.
\[Game Launch\]
|
\[Main Menu (Idle \-\> Active Logo Bar)\]
|
\+-------------------+-------------------+-------------------+
| | | |
v v v v
\[Lobby: Create\] \[Lobby: Join\] \[Profile\] \[Settings (Overlay)\]
| | | |
v v | |
\[Lounge (Host)\] \[Lounge (Guest)\] \+---(Back)----------+
| |
\+--- \[All Ready\] \---+
|
(Host Clicks Start)
|
v
\[IN-GAME HUD\]
### **UI Toolkit State Management Implementation**
1. **Screen Controller Pattern:** Each controller (MainMenuController, LobbyController, etc.) holds a reference to its root VisualElement defined in its corresponding .uxml file.
2. **Navigation Stack (Stack\<VisualElement\>):** UIManager.cs pushes screens to this stack. Pressing the Top-Left \[ \< Back \] button calls a method to pop the current state, hiding it (display: none) and showing the previous one (display: flex).
3. **Global Action Listeners (Ctrl \+ O):** UIManager.cs actively listens to the input system for Ctrl \+ O to instantly trigger SettingsController.ToggleSettings() without affecting the current navigation stack path.
4. **Cursorless Input Routing:** UIManager.cs constantly monitors Input.mousePosition. It calculates the nearest interactive element within the active screen and applies a .hover USS class to it, overriding default Unity pointer events.
* Transition Flow: UIManager.Push(T extends BaseUIController). Hàm này gọi CurrentScreen.PlayTransitionOut() (Fly-out) -> Chờ hoàn thành -> Kích hoạt màn hình mới -> Gọi NewScreen.PlayTransitionIn() (Fly-in Bounce).
5. Chi tiết Hệ thống Script & Logic Controller (C#)
Mục này quy định trách nhiệm của từng file script (.cs) điều khiển các thành phần UXML, xử lý logic tương tác và event binding thông qua query (root.Q<T>).
5.1. BaseUIController.cs
Lớp cha trừu tượng (Abstract) cung cấp các hàm nền tảng cho mọi màn hình.
* Biến: protected VisualElement root;, protected UIManager uiManager;
* Hàm Initialize(VisualElement uxmlRoot, UIManager manager): Khởi tạo controller, nhận tham chiếu đến nhánh UXML cụ thể của nó.
* Hàm Show() / Hide(): Thay đổi thuộc tính style.display thành Flex hoặc None.
* Hàm PlayTransitionIn() / PlayTransitionOut(): (Virtual) Khai báo logic PrimeTween để trượt/bay màn hình ra/vào. Mặc định là trượt ngang toàn bộ nhánh root.
5.2. MainMenuController.cs
Quản lý cỗ máy trạng thái (State Machine) của Main Menu và khóa trục chuột.
* State Management: Có biến enum MenuState { Idle, Ribbon }.
* Event Binding: Lấy phần tử Logo (root.Q<VisualElement>("Logo")), gán sự kiện PointerDownEvent.
* Nếu click khi đang Idle: Chuyển state sang Ribbon, dùng PrimeTween trượt Logo sang trái, bung dải Ribbon ra. Khởi động Timer Timeout.
* Nếu click khi đang Ribbon: Gọi uiManager.Push<LobbyController>().
* Idle Timeout Logic: Hàm Update() liên tục đếm thời gian từ lần cuối có tương tác (mouse di chuyển/click). Nếu vượt quá X giây (vd: 5s), tự động trigger tween trả Logo về giữa và đóng Ribbon.
* Mouse Axis Locking: Giao tiếp với Input System. Khi ở trạng thái Ribbon, ép tọa độ trục Y của con trỏ ảo luôn bằng với tọa độ Y của dải Ribbon.
5.3. LobbyController.cs
Xử lý logic 3 lớp giao diện bên trong Lobby (Create, Join, Lounge) mà không cần chuyển Screen mới.
* Sub-Views Toggling: Lấy tham chiếu đến 3 VisualElement container (JoinContainer, CreateContainer, LoungeContainer). Xử lý ẩn/hiện (display: none) chúng dựa theo path định tuyến từ UIManager.
* Join Logic: Gán danh sách giả/mạng vào ScrollView. Khởi tạo các item đúc sẵn (prefabs bằng uxml) bằng vòng lặp. Lắng nghe event click của từng item phòng để hiện Modal nhập Password nếu cần.
* Lounge Logic: Lắng nghe nút Toggle Ready. Gửi RPC mạng báo cho người chơi kia. Khi cả 2 biến isPlayer1Ready và isPlayer2Ready = true, gỡ khóa (Enable) nút [ START GAME ] cho Host.
5.4. SettingsController.cs
Xử lý thanh Sidebar và cơ chế chia tab.
* Tab Switching: Gán sự kiện ClickEvent cho 4 nút Tab ở Cột trái. Khi click, dùng vòng lặp xóa class .active-tab của tất cả, thêm .active-tab cho nút vừa click. Đồng thời Clear() nội dung của ScrollView ở Cột phải và Load/Instantiate layout của tab tương ứng vào.
* Non-blocking Transition: Hàm PlayTransitionIn() của riêng SettingsController chỉ trượt nhánh root (Sidebar) từ -500px (bên ngoài mép trái) về 0px thay vì ẩn toàn bộ các màn hình khác đi.
* Keybind Logic: Lắng nghe phím Esc để gọi hàm đóng Settings. Trong Tab Control, khi click đổi phím, script gọi luồng InputSystem.onAnyButtonPress để bắt phím tiếp theo và cập nhật tên Label. Lắng nghe thanh Slider để gọi AudioMixer.SetFloat().
5.5. ProfileController.cs
Cập nhật dữ liệu tài khoản hiển thị lên View.
* Data Binding: Khi gọi Show(), controller kéo dữ liệu từ hệ thống lưu trữ (PlayerPrefs/Cloud). Tìm các thành phần root.Q<Label>("WinRateText"), root.Q<ProgressBar>("WinRateBar") và cập nhật .text hoặc .value của chúng.
* Skin Inventory: Load kho đồ vào ScrollView ở cột phải. Lắng nghe ClickEvent vào các thẻ Skin để cập nhật Skin đang trang bị (báo cho Game Manager thay đổi vật liệu 3D ở Khung trái).
5.6. HUDController.cs
Quản lý thông tin theo thời gian thực (Real-time). Không chặn Input.
* Auto-Fade Logic: Giữ biến lastActionTime. Nếu Time.time - lastActionTime > 5f, dùng PrimeTween giảm opacity của túi đồ và thanh máu xuống 0.2f. Khi người chơi nhấn chuột, xài chiêu, hoặc bị sát thương -> gọi hàm WakeUpHUD() đẩy opacity về 1.0f ngay lập tức.
* Inventory Quick-slots: Lắng nghe phím 1, 2, 3. Hoán đổi thuộc tính style.backgroundImage của Ô nhỏ đang chọn lên Ô [ Đang Cầm ] lớn nhất.
* Fluid Shaders: Gửi dữ liệu biến thiên (VD: 0.0 -> 1.0) từ Máu/Thể lực hiện tại vào Material/Shader được gán trực tiếp trên VisualElement thông qua thuộc tính .customMaterial trong UI Toolkit.