Update
This commit is contained in:
@@ -6,6 +6,9 @@ namespace OnlyScove.Scripts
|
||||
{
|
||||
public class InputReader : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private InputActionAsset inputActions;
|
||||
public InputActionAsset InputActions => inputActions;
|
||||
|
||||
// Continuous Inputs
|
||||
public virtual Vector2 MoveInput { get; protected set; }
|
||||
public virtual Vector2 LookInput { get; protected set; }
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Hallucinate.UI
|
||||
{
|
||||
public abstract class BaseUIController
|
||||
public abstract class BaseUIController : ScriptableObject
|
||||
{
|
||||
protected VisualElement root;
|
||||
protected UIManager uiManager;
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Hallucinate.UI
|
||||
// Event Bindings
|
||||
root.Q<Button>("GoToCreateBtn").clicked += ShowCreate;
|
||||
root.Q<Button>("CancelCreateBtn").clicked += ShowJoin;
|
||||
root.Q<Button>("BackToMenuBtn").clicked += () => uiManager.Pop();
|
||||
root.Q<Button>("BackToMenuBtn").clicked += async () => await uiManager.Pop();
|
||||
root.Q<Button>("ConfirmCreateBtn").clicked += OnCreateRoomClicked;
|
||||
root.Q<Button>("ConfirmJoinBtn").clicked += OnConfirmPasswordClicked;
|
||||
root.Q<Button>("ClosePassBtn").clicked += () => _passOverlay.style.display = DisplayStyle.None;
|
||||
@@ -88,7 +88,6 @@ namespace Hallucinate.UI
|
||||
if (string.IsNullOrEmpty(id)) return;
|
||||
|
||||
await _spawner.StartHost(id, pass);
|
||||
// Logic chuyển sang Lounge sẽ được xử lý qua sự kiện OnPlayerJoined trong Spawner
|
||||
}
|
||||
|
||||
private void UpdateRoomList(List<SessionInfo> sessions)
|
||||
@@ -100,7 +99,7 @@ namespace Hallucinate.UI
|
||||
item.Q<Label>("RoomName").text = session.Name;
|
||||
item.Q<Label>("PlayerCount").text = $"{session.PlayerCount}/{session.MaxPlayers}";
|
||||
|
||||
bool needsPass = session.Properties.ContainsKey("pw"); // Giả sử dùng metadata
|
||||
bool needsPass = session.Properties.ContainsKey("pw");
|
||||
item.Q<Label>("LockIcon").style.display = needsPass ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
|
||||
var joinBtn = item.Q<Button>("JoinBtn");
|
||||
@@ -113,7 +112,6 @@ namespace Hallucinate.UI
|
||||
private void OnRoomItemClicked(SessionInfo session)
|
||||
{
|
||||
_selectedSession = session;
|
||||
// Fusion dùng Password trong StartGameArgs, nên ta hiện popup nhập trước
|
||||
_passOverlay.style.display = DisplayStyle.Flex;
|
||||
_joinPassError.style.display = DisplayStyle.None;
|
||||
_joinPassInput.value = "";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using PrimeTween;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hallucinate.UI
|
||||
@@ -84,7 +85,7 @@ namespace Hallucinate.UI
|
||||
root.style.opacity = 0;
|
||||
}
|
||||
Show();
|
||||
await PrimeTween.Tween.Custom(0f, 1f, duration: 0.5f, onValueChange: val => root.style.opacity = val);
|
||||
await Tween.Custom(0f, 1f, duration: 0.5f, onValueChange: val => root.style.opacity = val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Hallucinate.UI
|
||||
|
||||
private float _lastInteractionTime;
|
||||
private const float IDLE_TIMEOUT = 5.0f;
|
||||
private bool _isFirstLoad = true; // Biến cờ để nhận biết lần đầu vào game
|
||||
private bool _isFirstLoad = true;
|
||||
|
||||
private Tween _pulseTween;
|
||||
private Tween _rotationTween;
|
||||
@@ -38,13 +38,12 @@ namespace Hallucinate.UI
|
||||
|
||||
_logo.RegisterCallback<ClickEvent>(OnLogoClicked);
|
||||
|
||||
// Bind Buttons
|
||||
var settingsBtn = root.Q<Button>("SettingsBtn");
|
||||
if (settingsBtn != null) settingsBtn.clicked += () => uiManager.ToggleSettings();
|
||||
|
||||
root.Q<Button>("JoinBtn").clicked += () => uiManager.Push<LobbyController>();
|
||||
root.Q<Button>("CreateBtn").clicked += () => uiManager.Push<LobbyController>();
|
||||
root.Q<Button>("ProfileBtn").clicked += () => uiManager.Push<ProfileController>();
|
||||
root.Q<Button>("JoinBtn").clicked += async () => await uiManager.Push<LobbyController>();
|
||||
root.Q<Button>("CreateBtn").clicked += async () => await uiManager.Push<LobbyController>();
|
||||
root.Q<Button>("ProfileBtn").clicked += async () => await uiManager.Push<ProfileController>();
|
||||
root.Q<Button>("ExitBtn").clicked += () => Application.Quit();
|
||||
|
||||
ResetLogoPosition();
|
||||
@@ -86,8 +85,10 @@ namespace Hallucinate.UI
|
||||
if (_currentIcon == null) return;
|
||||
if (_rotationTween.isAlive) _rotationTween.Stop();
|
||||
|
||||
_rotationTween = Tween.Custom(0f, 360f, duration: 4f, cycles: -1, ease: Ease.Linear,
|
||||
onValueChange: val => _logo.style.rotate = new StyleRotate(new Rotate(Angle.Degrees(val))));
|
||||
_rotationTween = Tween.Custom(0f, 360f, duration: 4f,
|
||||
onValueChange: val => _logo.style.rotate = new StyleRotate(new Rotate(Angle.Degrees(val))),
|
||||
cycles: -1,
|
||||
ease: Ease.Linear);
|
||||
}
|
||||
|
||||
public override async Task PlayTransitionIn()
|
||||
@@ -102,20 +103,11 @@ namespace Hallucinate.UI
|
||||
_ribbon.style.opacity = 0;
|
||||
}
|
||||
|
||||
// Khởi động lại rotation nếu có icon
|
||||
StartRotation();
|
||||
|
||||
await base.PlayTransitionIn();
|
||||
|
||||
// Nếu không phải lần đầu load (tức là quay lại bằng nút Back), tự động bung Ribbon
|
||||
if (!_isFirstLoad)
|
||||
{
|
||||
TransitionToRibbon();
|
||||
}
|
||||
else
|
||||
{
|
||||
_isFirstLoad = false; // Đã xong lần đầu, các lần sau sẽ tự động bung
|
||||
}
|
||||
if (!_isFirstLoad) TransitionToRibbon();
|
||||
else _isFirstLoad = false;
|
||||
}
|
||||
|
||||
public override async Task PlayTransitionOut()
|
||||
@@ -124,11 +116,11 @@ namespace Hallucinate.UI
|
||||
await base.PlayTransitionOut();
|
||||
}
|
||||
|
||||
private void OnLogoClicked(ClickEvent evt)
|
||||
private async void OnLogoClicked(ClickEvent evt)
|
||||
{
|
||||
_lastInteractionTime = Time.time;
|
||||
if (_currentState == MenuState.Idle) TransitionToRibbon();
|
||||
else _ = uiManager.Push<LobbyController>();
|
||||
else await uiManager.Push<LobbyController>();
|
||||
}
|
||||
|
||||
private void TransitionToRibbon()
|
||||
@@ -149,17 +141,25 @@ namespace Hallucinate.UI
|
||||
Rect targetBounds = _logoSpace.worldBound;
|
||||
if (targetBounds.width <= 0) return;
|
||||
|
||||
Tween.Custom(_logo.resolvedStyle.left, targetBounds.x, duration: 0.5f, ease: Ease.OutQuad,
|
||||
onValueChange: val => _logo.style.left = val);
|
||||
// Center logo in LogoSpace (within the centered Ribbon)
|
||||
float targetX = targetBounds.x + (targetBounds.width / 2f) - 50f;
|
||||
float targetY = targetBounds.y + (targetBounds.height / 2f) - 50f;
|
||||
|
||||
Tween.Custom(_logo.resolvedStyle.left, targetX, duration: 0.5f,
|
||||
onValueChange: val => _logo.style.left = val,
|
||||
ease: Ease.OutQuad);
|
||||
|
||||
Tween.Custom(_logo.resolvedStyle.top, targetBounds.y - 35, duration: 0.5f, ease: Ease.OutQuad,
|
||||
onValueChange: val => _logo.style.top = val);
|
||||
Tween.Custom(_logo.resolvedStyle.top, targetY, duration: 0.5f,
|
||||
onValueChange: val => _logo.style.top = val,
|
||||
ease: Ease.OutQuad);
|
||||
|
||||
Tween.Custom(_logo.resolvedStyle.width, 120f, duration: 0.5f, ease: Ease.OutQuad,
|
||||
onValueChange: val => _logo.style.width = val);
|
||||
Tween.Custom(_logo.resolvedStyle.width, 100f, duration: 0.5f,
|
||||
onValueChange: val => _logo.style.width = val,
|
||||
ease: Ease.OutQuad);
|
||||
|
||||
Tween.Custom(_logo.resolvedStyle.height, 120f, duration: 0.5f, ease: Ease.OutQuad,
|
||||
onValueChange: val => _logo.style.height = val);
|
||||
Tween.Custom(_logo.resolvedStyle.height, 100f, duration: 0.5f,
|
||||
onValueChange: val => _logo.style.height = val,
|
||||
ease: Ease.OutQuad);
|
||||
|
||||
_lastInteractionTime = Time.time;
|
||||
}
|
||||
@@ -172,14 +172,10 @@ namespace Hallucinate.UI
|
||||
float targetX = (Screen.width / 2f) - 100;
|
||||
float targetY = (Screen.height / 2f) - 100;
|
||||
|
||||
Tween.Custom(_logo.resolvedStyle.left, targetX, duration: 0.5f, ease: Ease.OutQuad,
|
||||
onValueChange: val => _logo.style.left = val);
|
||||
Tween.Custom(_logo.resolvedStyle.top, targetY, duration: 0.5f, ease: Ease.OutQuad,
|
||||
onValueChange: val => _logo.style.top = val);
|
||||
Tween.Custom(_logo.resolvedStyle.width, 200f, duration: 0.5f, ease: Ease.OutQuad,
|
||||
onValueChange: val => _logo.style.width = val);
|
||||
Tween.Custom(_logo.resolvedStyle.height, 200f, duration: 0.5f, ease: Ease.OutQuad,
|
||||
onValueChange: val => _logo.style.height = val);
|
||||
Tween.Custom(_logo.resolvedStyle.left, targetX, duration: 0.5f, onValueChange: val => _logo.style.left = val, ease: Ease.OutQuad);
|
||||
Tween.Custom(_logo.resolvedStyle.top, targetY, duration: 0.5f, onValueChange: val => _logo.style.top = val, ease: Ease.OutQuad);
|
||||
Tween.Custom(_logo.resolvedStyle.width, 200f, duration: 0.5f, onValueChange: val => _logo.style.width = val, ease: Ease.OutQuad);
|
||||
Tween.Custom(_logo.resolvedStyle.height, 200f, duration: 0.5f, onValueChange: val => _logo.style.height = val, ease: Ease.OutQuad);
|
||||
|
||||
Tween.Custom(1f, 0f, duration: 0.5f, onValueChange: val => _ribbon.style.opacity = val)
|
||||
.OnComplete(() => _ribbon.style.display = DisplayStyle.None);
|
||||
@@ -200,7 +196,12 @@ namespace Hallucinate.UI
|
||||
|
||||
private void StartPulse()
|
||||
{
|
||||
_pulseTween = Tween.Scale(_logo.transform, Vector3.one * 1.1f, duration: 0.8f, cycles: -1, cycleMode: CycleMode.Yoyo, ease: Ease.InOutSine);
|
||||
if (_pulseTween.isAlive) _pulseTween.Stop();
|
||||
_pulseTween = Tween.Custom(Vector3.one, Vector3.one * 1.1f, duration: 0.8f,
|
||||
onValueChange: val => _logo.style.scale = new StyleScale(new Scale(val)),
|
||||
cycles: -1,
|
||||
cycleMode: CycleMode.Yoyo,
|
||||
ease: Ease.InOutSine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Hallucinate.UI
|
||||
_winRateBar = root.Q<ProgressBar>("WinRateBar");
|
||||
_winRateText = root.Q<Label>("WinRateText");
|
||||
|
||||
root.Q<Button>("BackBtn").clicked += () => uiManager.Pop();
|
||||
root.Q<Button>("BackBtn").clicked += async () => await uiManager.Pop();
|
||||
|
||||
LoadProfileData();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.InputSystem;
|
||||
using PrimeTween;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine.SceneManagement;
|
||||
using OnlyScove.Scripts; // Namespace của InputReader
|
||||
|
||||
namespace Hallucinate.UI
|
||||
{
|
||||
@@ -10,6 +15,10 @@ namespace Hallucinate.UI
|
||||
private VisualElement _sidebar;
|
||||
private Label _tabTitle;
|
||||
private ScrollView _content;
|
||||
private InputActionAsset _inputActions;
|
||||
|
||||
private Dictionary<string, Button> _tabButtons = new Dictionary<string, Button>();
|
||||
private string _activeTab = "GENERAL";
|
||||
|
||||
public override void Initialize(VisualElement uxmlRoot, UIManager manager)
|
||||
{
|
||||
@@ -19,28 +28,201 @@ namespace Hallucinate.UI
|
||||
_tabTitle = root.Q<Label>("TabTitle");
|
||||
_content = root.Q<ScrollView>("SettingsContent");
|
||||
|
||||
// Đăng ký sự kiện Click vào vùng nền tối
|
||||
// Ưu tiên 1: Lấy từ InputReader đã gán trong UIManager
|
||||
_inputActions = uiManager.InputReader?.InputActions;
|
||||
|
||||
// Ưu tiên 2: Nếu null, thử tìm PlayerInput trong scene
|
||||
if (_inputActions == null)
|
||||
{
|
||||
_inputActions = GameObject.FindAnyObjectByType<PlayerInput>()?.actions;
|
||||
}
|
||||
|
||||
// Click outside to close
|
||||
root.RegisterCallback<PointerDownEvent>(evt => {
|
||||
// Nếu click trực tiếp vào SettingsRoot (không phải sidebar)
|
||||
if (evt.target == root)
|
||||
{
|
||||
Debug.Log("[Settings] Clicked outside sidebar, closing...");
|
||||
uiManager.ToggleSettings();
|
||||
}
|
||||
});
|
||||
|
||||
root.Q<Button>("GeneralTab").clicked += () => SwitchTab("GENERAL");
|
||||
root.Q<Button>("VideoTab").clicked += () => SwitchTab("VIDEO");
|
||||
root.Q<Button>("SoundTab").clicked += () => SwitchTab("SOUND");
|
||||
root.Q<Button>("ControlTab").clicked += () => SwitchTab("CONTROL");
|
||||
root.Q<Button>("CloseSettingsBtn").clicked += () => uiManager.ToggleSettings();
|
||||
SetupTab("GeneralTab", "GENERAL");
|
||||
SetupTab("VideoTab", "VIDEO");
|
||||
SetupTab("SoundTab", "SOUND");
|
||||
SetupTab("ControlTab", "CONTROL");
|
||||
|
||||
var closeBtn = root.Q<Button>("CloseSettingsBtn");
|
||||
if (closeBtn != null) closeBtn.clicked += () => uiManager.ToggleSettings();
|
||||
|
||||
// Default tab
|
||||
SwitchTab("GENERAL");
|
||||
}
|
||||
|
||||
private void SwitchTab(string title)
|
||||
private void SetupTab(string btnName, string tabId)
|
||||
{
|
||||
_tabTitle.text = title;
|
||||
var btn = root.Q<Button>(btnName);
|
||||
if (btn != null)
|
||||
{
|
||||
_tabButtons[tabId] = btn;
|
||||
btn.clicked += () => SwitchTab(tabId);
|
||||
}
|
||||
}
|
||||
|
||||
private void SwitchTab(string tabId)
|
||||
{
|
||||
_activeTab = tabId;
|
||||
_tabTitle.text = tabId;
|
||||
|
||||
// Update tab styles
|
||||
foreach (var kvp in _tabButtons)
|
||||
{
|
||||
if (kvp.Key == tabId) kvp.Value.AddToClassList("active-tab");
|
||||
else kvp.Value.RemoveFromClassList("active-tab");
|
||||
}
|
||||
|
||||
_content.Clear();
|
||||
_content.Add(new Label($"Settings for {title} coming soon..."));
|
||||
|
||||
switch (tabId)
|
||||
{
|
||||
case "GENERAL":
|
||||
RenderGeneralSettings();
|
||||
break;
|
||||
case "CONTROL":
|
||||
RenderControlSettings();
|
||||
break;
|
||||
default:
|
||||
var comingSoon = new Label($"Settings for {tabId} coming soon...");
|
||||
comingSoon.AddToClassList("text-body");
|
||||
_content.Add(comingSoon);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderGeneralSettings()
|
||||
{
|
||||
var section = CreateSection("ACCOUNT & DATA");
|
||||
|
||||
var wipeBtn = new Button { text = "WIPE ALL USER DATA (TEST ONLY)" };
|
||||
wipeBtn.AddToClassList("button-spring");
|
||||
wipeBtn.AddToClassList("btn-exit");
|
||||
wipeBtn.style.marginTop = 20;
|
||||
wipeBtn.style.backgroundColor = new Color(0.8f, 0.2f, 0.2f, 0.8f);
|
||||
|
||||
wipeBtn.clicked += () => {
|
||||
bool confirm = true;
|
||||
#if UNITY_EDITOR
|
||||
confirm = UnityEditor.EditorUtility.DisplayDialog("Wipe Data", "This will delete your local username and restart the game. Proceed?", "Yes", "Cancel");
|
||||
#endif
|
||||
if (confirm)
|
||||
{
|
||||
PlayerPrefs.DeleteAll();
|
||||
PlayerPrefs.Save();
|
||||
Debug.Log("[Settings] Data wiped. Restarting...");
|
||||
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
|
||||
}
|
||||
};
|
||||
|
||||
_content.Add(section);
|
||||
_content.Add(wipeBtn);
|
||||
|
||||
_content.Add(new Label("\nNote: This will force the game to ask for your name again on next launch.") {
|
||||
style = { fontSize = 12, color = new Color(0.6f, 0.6f, 0.6f), whiteSpace = WhiteSpace.Normal, marginTop = 10 }
|
||||
});
|
||||
}
|
||||
|
||||
private void RenderControlSettings()
|
||||
{
|
||||
if (_inputActions == null)
|
||||
{
|
||||
var errorLabel = new Label("Input Actions not found. Cannot rebind.");
|
||||
errorLabel.AddToClassList("text-body");
|
||||
_content.Add(errorLabel);
|
||||
return;
|
||||
}
|
||||
|
||||
var playerMap = _inputActions.FindActionMap("Player");
|
||||
if (playerMap == null) return;
|
||||
|
||||
// Categories
|
||||
RenderSection("MOVEMENT", playerMap, new[] { "Move", "Jump", "Sprint", "Crouch" });
|
||||
RenderSection("COMBAT", playerMap, new[] { "Attack" });
|
||||
RenderSection("INTERACTION", playerMap, new[] { "Interact", "Next", "Previous" });
|
||||
RenderSection("VIEW", playerMap, new[] { "Look", "ToggleView", "Scroll" });
|
||||
}
|
||||
|
||||
private void RenderSection(string header, InputActionMap map, string[] actionNames)
|
||||
{
|
||||
_content.Add(CreateSection(header));
|
||||
foreach (var name in actionNames)
|
||||
{
|
||||
var action = map.FindAction(name);
|
||||
if (action != null)
|
||||
{
|
||||
_content.Add(CreateRebindRow(action));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private VisualElement CreateSection(string title)
|
||||
{
|
||||
var header = new Label(title);
|
||||
header.AddToClassList("setting-section-header");
|
||||
return header;
|
||||
}
|
||||
|
||||
private VisualElement CreateRebindRow(InputAction action)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("rebind-row");
|
||||
|
||||
var label = new Label(action.name.ToUpper());
|
||||
label.AddToClassList("rebind-label");
|
||||
|
||||
var rebindBtn = new Button();
|
||||
rebindBtn.AddToClassList("rebind-button");
|
||||
|
||||
// Get current binding text
|
||||
UpdateBindingText(action, rebindBtn);
|
||||
|
||||
rebindBtn.clicked += () => StartRebind(action, rebindBtn);
|
||||
|
||||
row.Add(label);
|
||||
row.Add(rebindBtn);
|
||||
return row;
|
||||
}
|
||||
|
||||
private void UpdateBindingText(InputAction action, Button btn)
|
||||
{
|
||||
int bindingIndex = action.GetBindingIndexForControl(action.controls[0]);
|
||||
btn.text = action.GetBindingDisplayString(bindingIndex);
|
||||
}
|
||||
|
||||
private void StartRebind(InputAction action, Button btn)
|
||||
{
|
||||
string oldText = btn.text;
|
||||
btn.text = "...";
|
||||
btn.style.color = Color.yellow;
|
||||
|
||||
action.Disable();
|
||||
|
||||
var rebindOperation = action.PerformInteractiveRebinding()
|
||||
.WithControlsExcluding("<Mouse>/delta") // Don't bind to mouse movement
|
||||
.WithControlsExcluding("<Mouse>/scroll")
|
||||
.OnMatchWaitForAnother(0.1f)
|
||||
.OnComplete(operation => {
|
||||
btn.style.color = new Color(0f, 1f, 0.8f); // Reset color
|
||||
UpdateBindingText(action, btn);
|
||||
action.Enable();
|
||||
operation.Dispose();
|
||||
// Save bindings here if you have a save system
|
||||
})
|
||||
.OnCancel(operation => {
|
||||
btn.style.color = new Color(0f, 1f, 0.8f);
|
||||
btn.text = oldText;
|
||||
action.Enable();
|
||||
operation.Dispose();
|
||||
});
|
||||
|
||||
rebindOperation.Start();
|
||||
}
|
||||
|
||||
public override async Task PlayTransitionIn()
|
||||
@@ -49,9 +231,9 @@ namespace Hallucinate.UI
|
||||
{
|
||||
root.style.translate = new StyleTranslate(new Translate(0, 0));
|
||||
root.style.display = DisplayStyle.Flex;
|
||||
root.style.opacity = 1;
|
||||
}
|
||||
|
||||
// Hiệu ứng trượt sidebar
|
||||
_sidebar.style.translate = new StyleTranslate(new Translate(Length.Percent(-100), 0));
|
||||
await Tween.Custom(-100f, 0f, duration: 0.4f, ease: Ease.OutQuad,
|
||||
onValueChange: val => _sidebar.style.translate = new StyleTranslate(new Translate(Length.Percent(val), 0)));
|
||||
|
||||
@@ -7,6 +7,7 @@ using PrimeTween;
|
||||
using OnlyScove.Scripts;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
#endif
|
||||
|
||||
namespace Hallucinate.UI
|
||||
@@ -26,6 +27,7 @@ namespace Hallucinate.UI
|
||||
|
||||
[Header("References")]
|
||||
[SerializeField] private InputReader inputReader;
|
||||
public InputReader InputReader => inputReader;
|
||||
|
||||
[Header("Game Metadata")]
|
||||
[SerializeField] private Texture2D gameIcon;
|
||||
@@ -43,7 +45,7 @@ namespace Hallucinate.UI
|
||||
[SerializeField] private VisualTreeAsset loginTemplate;
|
||||
[SerializeField] private VisualTreeAsset mainMenuTemplate;
|
||||
[SerializeField] private VisualTreeAsset lobbyTemplate;
|
||||
[SerializeField] private VisualTreeAsset roomItemTemplate; // Template cho dòng phòng
|
||||
[SerializeField] private VisualTreeAsset roomItemTemplate;
|
||||
[SerializeField] private VisualTreeAsset profileTemplate;
|
||||
[SerializeField] private VisualTreeAsset settingsTemplate;
|
||||
[SerializeField] private VisualTreeAsset hudTemplate;
|
||||
@@ -68,8 +70,19 @@ namespace Hallucinate.UI
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_uiDocument == null) return;
|
||||
if (_uiDocument == null) _uiDocument = GetComponent<UIDocument>();
|
||||
if (_uiDocument == null)
|
||||
{
|
||||
Debug.LogError("[UIManager] UIDocument component missing!");
|
||||
return;
|
||||
}
|
||||
|
||||
_rootElement = _uiDocument.rootVisualElement;
|
||||
if (_rootElement == null)
|
||||
{
|
||||
Debug.LogError("[UIManager] Root VisualElement is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
_cursorLayer = new VisualElement();
|
||||
_cursorLayer.name = "CursorLayer";
|
||||
@@ -92,7 +105,7 @@ namespace Hallucinate.UI
|
||||
#if UNITY_EDITOR
|
||||
if (gameIcon == null)
|
||||
{
|
||||
var icons = PlayerSettings.GetIconsForTargetGroup(BuildTargetGroup.Unknown);
|
||||
var icons = PlayerSettings.GetIcons(NamedBuildTarget.Unknown, IconKind.Any);
|
||||
if (icons != null && icons.Length > 0) gameIcon = icons[0];
|
||||
}
|
||||
#endif
|
||||
@@ -159,6 +172,8 @@ namespace Hallucinate.UI
|
||||
|
||||
private void SetupVirtualCursor()
|
||||
{
|
||||
if (_cursorLayer == null) return;
|
||||
|
||||
_cursorLayer.Clear();
|
||||
_trailSegments.Clear();
|
||||
_posHistory.Clear();
|
||||
@@ -200,7 +215,7 @@ namespace Hallucinate.UI
|
||||
|
||||
private void OnGlobalClick(PointerDownEvent evt)
|
||||
{
|
||||
if (!enableRipples) return;
|
||||
if (!enableRipples || _cursorLayer == null) return;
|
||||
|
||||
var ripple = new VisualElement();
|
||||
ripple.style.position = Position.Absolute;
|
||||
@@ -229,14 +244,18 @@ namespace Hallucinate.UI
|
||||
|
||||
_cursorLayer.Add(ripple);
|
||||
|
||||
Tween.Scale(ripple.transform, Vector3.one * 2.5f, duration: 0.4f, ease: Ease.OutQuad);
|
||||
// Correct Fluent API for PrimeTween
|
||||
Tween.Custom(Vector3.one, Vector3.one * 2.5f, duration: 0.4f,
|
||||
onValueChange: val => ripple.style.scale = new StyleScale(new Scale(val)),
|
||||
ease: Ease.OutQuad);
|
||||
|
||||
Tween.Custom(1f, 0f, duration: 0.4f, onValueChange: val => ripple.style.opacity = val)
|
||||
.OnComplete(() => ripple.RemoveFromHierarchy());
|
||||
}
|
||||
|
||||
private void UpdateCursorAndTrail()
|
||||
{
|
||||
if (!Application.isFocused)
|
||||
if (!Application.isFocused || _cursorLayer == null)
|
||||
{
|
||||
if (_cursorLayer != null) _cursorLayer.style.display = DisplayStyle.None;
|
||||
return;
|
||||
@@ -247,11 +266,11 @@ namespace Hallucinate.UI
|
||||
|
||||
if (!isMouseInWindow)
|
||||
{
|
||||
if (_cursorLayer != null) _cursorLayer.style.display = DisplayStyle.None;
|
||||
_cursorLayer.style.display = DisplayStyle.None;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cursorLayer != null) _cursorLayer.style.display = DisplayStyle.Flex;
|
||||
_cursorLayer.style.display = DisplayStyle.Flex;
|
||||
Vector2 uiPos = new Vector2(mousePos.x, Screen.height - mousePos.y);
|
||||
|
||||
float mouseSpeed = Vector2.Distance(uiPos, _lastMousePos);
|
||||
@@ -285,32 +304,59 @@ namespace Hallucinate.UI
|
||||
|
||||
private void InitializeControllers()
|
||||
{
|
||||
_mainMenuController = RegisterController<MainMenuController>(mainMenuTemplate);
|
||||
if (_mainMenuController != null && gameIcon != null) _mainMenuController.SetGameIcon(gameIcon);
|
||||
try
|
||||
{
|
||||
_mainMenuController = RegisterController<MainMenuController>(mainMenuTemplate);
|
||||
if (_mainMenuController != null && gameIcon != null) _mainMenuController.SetGameIcon(gameIcon);
|
||||
|
||||
_lobbyController = RegisterController<LobbyController>(lobbyTemplate);
|
||||
if (_lobbyController != null) _lobbyController.SetRoomTemplate(roomItemTemplate);
|
||||
_lobbyController = RegisterController<LobbyController>(lobbyTemplate);
|
||||
if (_lobbyController != null) _lobbyController.SetRoomTemplate(roomItemTemplate);
|
||||
|
||||
RegisterController<ProfileController>(profileTemplate);
|
||||
_settingsController = RegisterController<SettingsController>(settingsTemplate);
|
||||
RegisterController<HUDController>(hudTemplate);
|
||||
RegisterController<ProfileController>(profileTemplate);
|
||||
_settingsController = RegisterController<SettingsController>(settingsTemplate);
|
||||
RegisterController<HUDController>(hudTemplate);
|
||||
|
||||
_loginController = RegisterController<LoginController>(loginTemplate);
|
||||
_loginController = RegisterController<LoginController>(loginTemplate);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[UIManager] Failed to initialize controllers: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
private T RegisterController<T>(VisualTreeAsset template) where T : BaseUIController, new()
|
||||
private T RegisterController<T>(VisualTreeAsset template) where T : BaseUIController
|
||||
{
|
||||
if (template == null) return null;
|
||||
var instance = template.Instantiate();
|
||||
if (template == null)
|
||||
{
|
||||
Debug.LogWarning($"[UIManager] Template for {typeof(T).Name} is missing in Inspector.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_rootElement == null) return null;
|
||||
|
||||
VisualElement instance = null;
|
||||
try
|
||||
{
|
||||
instance = template.Instantiate();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[UIManager] Failed to instantiate template for {typeof(T).Name}: {e.Message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (instance == null) return null;
|
||||
|
||||
instance.style.flexGrow = 1;
|
||||
instance.style.position = Position.Absolute;
|
||||
instance.style.width = Length.Percent(100);
|
||||
instance.style.height = Length.Percent(100);
|
||||
instance.style.display = DisplayStyle.None;
|
||||
|
||||
_rootElement.Add(instance);
|
||||
if (_cursorLayer != null) _cursorLayer.BringToFront();
|
||||
|
||||
var controller = new T();
|
||||
var controller = ScriptableObject.CreateInstance<T>();
|
||||
controller.Initialize(instance, this);
|
||||
_controllers[typeof(T)] = controller;
|
||||
return controller;
|
||||
|
||||
Reference in New Issue
Block a user