Update
This commit is contained in:
@@ -384,17 +384,17 @@ namespace Hallucinate.UI
|
||||
_content.Add(versionBox);
|
||||
|
||||
_content.Add(CreateSection("CURSOR & MOUSE"));
|
||||
_content.Add(CreateSliderWithInput("Cursor Size", 10, 150, PlayerPrefs.GetFloat("CursorSize", 40), val => PlayerPrefs.SetFloat("CursorSize", val)));
|
||||
_content.Add(CreateSliderWithInput("Cursor Size", 10, 150, PlayerPrefs.GetFloat("CursorSize", 40), val => uiManager.SetCursorSize(val)));
|
||||
|
||||
var trailToggle = new Toggle("Enable Cursor Trail") { value = PlayerPrefs.GetInt("CursorTrail", 1) == 1 };
|
||||
trailToggle.RegisterValueChangedCallback(evt => PlayerPrefs.SetInt("CursorTrail", evt.newValue ? 1 : 0));
|
||||
trailToggle.RegisterValueChangedCallback(evt => uiManager.SetCursorTrail(evt.newValue));
|
||||
_content.Add(trailToggle);
|
||||
|
||||
var rippleToggle = new Toggle("Enable Ripple Effects") { value = PlayerPrefs.GetInt("CursorRipples", 1) == 1 };
|
||||
rippleToggle.RegisterValueChangedCallback(evt => PlayerPrefs.SetInt("CursorRipples", evt.newValue ? 1 : 0));
|
||||
rippleToggle.RegisterValueChangedCallback(evt => uiManager.SetCursorRipples(evt.newValue));
|
||||
_content.Add(rippleToggle);
|
||||
|
||||
_content.Add(CreateSliderWithInput("Sensitivity", 0.1f, 5.0f, PlayerPrefs.GetFloat("MouseSensitivity", 1.0f), val => PlayerPrefs.SetFloat("MouseSensitivity", val)));
|
||||
_content.Add(CreateSliderWithInput("Sensitivity", 0.1f, 5.0f, PlayerPrefs.GetFloat("MouseSensitivity", 1.0f), val => uiManager.SetMouseSensitivity(val)));
|
||||
|
||||
var rawInputToggle = new Toggle("Raw Input (Bypass Acceleration)") { value = true };
|
||||
_content.Add(rawInputToggle);
|
||||
|
||||
@@ -7,7 +7,6 @@ using PrimeTween;
|
||||
using OnlyScove.Scripts;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
#endif
|
||||
|
||||
namespace Hallucinate.UI
|
||||
@@ -20,6 +19,7 @@ namespace Hallucinate.UI
|
||||
private UIDocument _uiDocument;
|
||||
private VisualElement _rootElement;
|
||||
public VisualElement Root => _rootElement;
|
||||
|
||||
private VisualElement _cursorLayer;
|
||||
private VisualElement _mainCursor;
|
||||
|
||||
@@ -68,6 +68,21 @@ namespace Hallucinate.UI
|
||||
[Header("Development Settings")]
|
||||
[SerializeField] private bool allowMultipleInstances = true;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
if (gameIcon == null)
|
||||
{
|
||||
var icons = PlayerSettings.GetIcons(UnityEditor.Build.NamedBuildTarget.Unknown, IconKind.Any);
|
||||
if (icons != null && icons.Length > 0)
|
||||
{
|
||||
gameIcon = icons[0];
|
||||
UnityEditor.EditorUtility.SetDirty(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
@@ -78,25 +93,45 @@ namespace Hallucinate.UI
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
// Single instance guard
|
||||
if (!Application.isEditor && !allowMultipleInstances)
|
||||
{
|
||||
var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
|
||||
var processes = System.Diagnostics.Process.GetProcessesByName(currentProcess.ProcessName);
|
||||
if (processes.Length > 1)
|
||||
{
|
||||
Debug.LogError("[UIManager] Another instance is already running. Quitting to prevent save conflict.");
|
||||
Application.Quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_uiDocument = GetComponent<UIDocument>();
|
||||
UnityEngine.Cursor.visible = false;
|
||||
|
||||
LoadGeneralSettings();
|
||||
ApplySavedUIScale();
|
||||
}
|
||||
|
||||
private void LoadGeneralSettings()
|
||||
{
|
||||
cursorSize = PlayerPrefs.GetFloat("CursorSize", 40f);
|
||||
enableRipples = PlayerPrefs.GetInt("CursorRipples", 1) == 1;
|
||||
bool trailEnabled = PlayerPrefs.GetInt("CursorTrail", 1) == 1;
|
||||
if (!trailEnabled) trailLength = 0;
|
||||
}
|
||||
|
||||
public void SetCursorSize(float size)
|
||||
{
|
||||
cursorSize = size;
|
||||
PlayerPrefs.SetFloat("CursorSize", size);
|
||||
SetupVirtualCursor();
|
||||
}
|
||||
|
||||
public void SetCursorTrail(bool enabled)
|
||||
{
|
||||
PlayerPrefs.SetInt("CursorTrail", enabled ? 1 : 0);
|
||||
SetupVirtualCursor();
|
||||
}
|
||||
|
||||
public void SetCursorRipples(bool enabled)
|
||||
{
|
||||
enableRipples = enabled;
|
||||
PlayerPrefs.SetInt("CursorRipples", enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
public void SetMouseSensitivity(float sensitivity)
|
||||
{
|
||||
PlayerPrefs.SetFloat("MouseSensitivity", sensitivity);
|
||||
}
|
||||
|
||||
public void OnGameStarted()
|
||||
{
|
||||
_ = Push<HUDController>();
|
||||
@@ -110,9 +145,6 @@ namespace Hallucinate.UI
|
||||
public void SetUIScale(float scale)
|
||||
{
|
||||
if (_uiDocument == null || _uiDocument.panelSettings == null) return;
|
||||
|
||||
// Unity UI Toolkit dùng panelSettings để điều khiển tỉ lệ
|
||||
// Chúng ta thay đổi scale multiplier. Mặc định 1.0 sẽ tương ứng với visual scale là 1.3
|
||||
_uiDocument.panelSettings.scale = scale * 1.3f;
|
||||
PlayerPrefs.SetFloat(UI_SCALE_KEY, scale);
|
||||
PlayerPrefs.Save();
|
||||
@@ -126,18 +158,7 @@ namespace Hallucinate.UI
|
||||
private void Start()
|
||||
{
|
||||
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";
|
||||
@@ -157,13 +178,6 @@ namespace Hallucinate.UI
|
||||
inputReader.OnCancelEvent += HandleCancel;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (gameIcon == null)
|
||||
{
|
||||
var icons = PlayerSettings.GetIcons(NamedBuildTarget.Unknown, IconKind.Any);
|
||||
if (icons != null && icons.Length > 0) gameIcon = icons[0];
|
||||
}
|
||||
#endif
|
||||
InitializeControllers();
|
||||
CheckLoginStatus();
|
||||
}
|
||||
@@ -171,21 +185,11 @@ namespace Hallucinate.UI
|
||||
private void CheckLoginStatus()
|
||||
{
|
||||
string savedName = PlayerPrefs.GetString("Username", "");
|
||||
if (string.IsNullOrEmpty(savedName))
|
||||
{
|
||||
_ = Push<LoginController>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"[UIManager] Welcome back, {savedName}!");
|
||||
_ = Push<MainMenuController>();
|
||||
}
|
||||
if (string.IsNullOrEmpty(savedName)) _ = Push<LoginController>();
|
||||
else _ = Push<MainMenuController>();
|
||||
}
|
||||
|
||||
public void OnLoginSuccess()
|
||||
{
|
||||
_ = Push<MainMenuController>();
|
||||
}
|
||||
public void OnLoginSuccess() => _ = Push<MainMenuController>();
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
@@ -196,15 +200,11 @@ namespace Hallucinate.UI
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCancel()
|
||||
{
|
||||
if (_isSettingsOpen) ToggleSettings();
|
||||
}
|
||||
private void HandleCancel() { if (_isSettingsOpen) ToggleSettings(); }
|
||||
|
||||
public async void ToggleSettings()
|
||||
{
|
||||
if (_settingsController == null) return;
|
||||
|
||||
if (!_isSettingsOpen)
|
||||
{
|
||||
_isSettingsOpen = true;
|
||||
@@ -228,7 +228,6 @@ namespace Hallucinate.UI
|
||||
private void SetupVirtualCursor()
|
||||
{
|
||||
if (_cursorLayer == null) return;
|
||||
|
||||
_cursorLayer.Clear();
|
||||
_trailSegments.Clear();
|
||||
_posHistory.Clear();
|
||||
@@ -242,13 +241,11 @@ namespace Hallucinate.UI
|
||||
segment.style.width = cursorSize;
|
||||
segment.style.height = cursorSize;
|
||||
segment.style.backgroundImage = new StyleBackground(Background.FromSprite(cursorTrailSprite));
|
||||
|
||||
float ratio = 1f - ((float)i / trailLength);
|
||||
segment.style.opacity = 0f;
|
||||
segment.style.scale = new StyleScale(new Scale(new Vector3(ratio, ratio, 1f)));
|
||||
segment.style.translate = new StyleTranslate(new Translate(Length.Percent(-50), Length.Percent(-50)));
|
||||
segment.pickingMode = PickingMode.Ignore;
|
||||
|
||||
_cursorLayer.Add(segment);
|
||||
_trailSegments.Add(segment);
|
||||
}
|
||||
@@ -268,52 +265,27 @@ namespace Hallucinate.UI
|
||||
for (int i = 0; i < trailLength * trailSpacing + 1; i++) _posHistory.Add(startPos);
|
||||
}
|
||||
|
||||
private float GetCurrentScale()
|
||||
{
|
||||
if (_uiDocument != null && _uiDocument.panelSettings != null)
|
||||
return _uiDocument.panelSettings.scale;
|
||||
return 1.0f;
|
||||
}
|
||||
private float GetCurrentScale() => (_uiDocument != null && _uiDocument.panelSettings != null) ? _uiDocument.panelSettings.scale : 1.0f;
|
||||
|
||||
private void OnGlobalClick(PointerDownEvent evt)
|
||||
{
|
||||
if (!enableRipples || _cursorLayer == null) return;
|
||||
|
||||
var ripple = new VisualElement();
|
||||
ripple.style.position = Position.Absolute;
|
||||
ripple.style.width = cursorSize;
|
||||
ripple.style.height = cursorSize;
|
||||
ripple.style.translate = new StyleTranslate(new Translate(Length.Percent(-50), Length.Percent(-50)));
|
||||
|
||||
var radius = new StyleLength(new Length(50, LengthUnit.Percent));
|
||||
ripple.style.borderTopLeftRadius = radius;
|
||||
ripple.style.borderTopRightRadius = radius;
|
||||
ripple.style.borderBottomLeftRadius = radius;
|
||||
ripple.style.borderBottomRightRadius = radius;
|
||||
|
||||
ripple.style.borderTopColor = rippleColor;
|
||||
ripple.style.borderBottomColor = rippleColor;
|
||||
ripple.style.borderLeftColor = rippleColor;
|
||||
ripple.style.borderRightColor = rippleColor;
|
||||
ripple.style.borderTopWidth = 2;
|
||||
ripple.style.borderBottomWidth = 2;
|
||||
ripple.style.borderLeftWidth = 2;
|
||||
ripple.style.borderRightWidth = 2;
|
||||
|
||||
// PointerDownEvent.localPosition đã được Unity tự động scale theo Panel
|
||||
ripple.style.left = evt.localPosition.x;
|
||||
ripple.style.top = evt.localPosition.y;
|
||||
ripple.style.borderTopLeftRadius = radius; ripple.style.borderTopRightRadius = radius;
|
||||
ripple.style.borderBottomLeftRadius = radius; ripple.style.borderBottomRightRadius = radius;
|
||||
ripple.style.borderTopColor = rippleColor; ripple.style.borderBottomColor = rippleColor;
|
||||
ripple.style.borderLeftColor = rippleColor; ripple.style.borderRightColor = rippleColor;
|
||||
ripple.style.borderTopWidth = 2; ripple.style.borderBottomWidth = 2;
|
||||
ripple.style.left = evt.localPosition.x; ripple.style.top = evt.localPosition.y;
|
||||
ripple.pickingMode = PickingMode.Ignore;
|
||||
|
||||
_cursorLayer.Add(ripple);
|
||||
|
||||
// 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());
|
||||
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()
|
||||
@@ -323,38 +295,19 @@ namespace Hallucinate.UI
|
||||
if (_cursorLayer != null) _cursorLayer.style.display = DisplayStyle.None;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 mousePos = Input.mousePosition;
|
||||
bool isMouseInWindow = mousePos.x >= 0 && mousePos.x <= Screen.width && mousePos.y >= 0 && mousePos.y <= Screen.height;
|
||||
|
||||
if (!isMouseInWindow)
|
||||
{
|
||||
_cursorLayer.style.display = DisplayStyle.None;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isMouseInWindow) { _cursorLayer.style.display = DisplayStyle.None; return; }
|
||||
_cursorLayer.style.display = DisplayStyle.Flex;
|
||||
|
||||
// QUAN TRỌNG: Chia tọa độ pixel cho scale của UI để có tọa độ local chính xác
|
||||
float scale = GetCurrentScale();
|
||||
Vector2 uiPos = new Vector2(mousePos.x / scale, (Screen.height - mousePos.y) / scale);
|
||||
|
||||
float mouseSpeed = Vector2.Distance(uiPos, _lastMousePos);
|
||||
_lastMousePos = uiPos;
|
||||
|
||||
if (mouseSpeed > 0.1f) _trailOpacity = Mathf.MoveTowards(_trailOpacity, 1f, Time.deltaTime * 5f);
|
||||
else _trailOpacity = Mathf.MoveTowards(_trailOpacity, 0f, Time.deltaTime * 3f);
|
||||
|
||||
_posHistory.Insert(0, uiPos);
|
||||
if (_posHistory.Count > trailLength * trailSpacing + 1)
|
||||
_posHistory.RemoveAt(_posHistory.Count - 1);
|
||||
|
||||
if (_mainCursor != null)
|
||||
{
|
||||
_mainCursor.style.left = uiPos.x;
|
||||
_mainCursor.style.top = uiPos.y;
|
||||
}
|
||||
|
||||
if (_posHistory.Count > trailLength * trailSpacing + 1) _posHistory.RemoveAt(_posHistory.Count - 1);
|
||||
if (_mainCursor != null) { _mainCursor.style.left = uiPos.x; _mainCursor.style.top = uiPos.y; }
|
||||
for (int i = 0; i < _trailSegments.Count; i++)
|
||||
{
|
||||
int historyIndex = (i + 1) * trailSpacing;
|
||||
@@ -374,54 +327,26 @@ namespace Hallucinate.UI
|
||||
{
|
||||
_mainMenuController = RegisterController<MainMenuController>(mainMenuTemplate);
|
||||
if (_mainMenuController != null && gameIcon != null) _mainMenuController.SetGameIcon(gameIcon);
|
||||
|
||||
_lobbyController = RegisterController<LobbyController>(lobbyTemplate);
|
||||
if (_lobbyController != null) _lobbyController.SetRoomTemplate(roomItemTemplate);
|
||||
|
||||
RegisterController<ProfileController>(profileTemplate);
|
||||
_settingsController = RegisterController<SettingsController>(settingsTemplate);
|
||||
RegisterController<HUDController>(hudTemplate);
|
||||
|
||||
_loginController = RegisterController<LoginController>(loginTemplate);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[UIManager] Failed to initialize controllers: {e}");
|
||||
}
|
||||
catch (Exception e) { Debug.LogError($"[UIManager] Failed to initialize controllers: {e}"); }
|
||||
}
|
||||
|
||||
private T RegisterController<T>(VisualTreeAsset template) where T : BaseUIController
|
||||
{
|
||||
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 (template == null || _rootElement == null) return null;
|
||||
VisualElement instance = template.Instantiate();
|
||||
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.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();
|
||||
|
||||
if (_cursorLayer != null) _cursorLayer.BringToFront(); // Giữ cursor layer trên cùng của root
|
||||
var controller = ScriptableObject.CreateInstance<T>();
|
||||
controller.Initialize(instance, this);
|
||||
_controllers[typeof(T)] = controller;
|
||||
@@ -433,7 +358,6 @@ namespace Hallucinate.UI
|
||||
if (!_controllers.TryGetValue(typeof(T), out var newScreen)) return;
|
||||
if (_history.Count > 0 && _history.Peek() == newScreen) return;
|
||||
if (_history.Count > 0) await _history.Peek().PlayTransitionOut();
|
||||
|
||||
_history.Push(newScreen);
|
||||
await newScreen.PlayTransitionIn();
|
||||
if (_cursorLayer != null) _cursorLayer.BringToFront();
|
||||
|
||||
Reference in New Issue
Block a user