2026-04-25 18:20:16 +07:00
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.UIElements;
|
2026-04-28 00:07:42 +07:00
|
|
|
using PrimeTween;
|
|
|
|
|
using System.Threading.Tasks;
|
2026-04-25 18:20:16 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
namespace Hallucinate.UI
|
2026-04-25 18:20:16 +07:00
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
public class MainMenuController : BaseUIController
|
2026-04-25 18:20:16 +07:00
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
public enum MenuState { Idle, Ribbon }
|
|
|
|
|
private MenuState _currentState = MenuState.Idle;
|
|
|
|
|
|
2026-04-26 04:39:59 +07:00
|
|
|
private VisualElement _logo;
|
|
|
|
|
private VisualElement _ribbon;
|
2026-04-28 00:07:42 +07:00
|
|
|
private VisualElement _virtualCursor;
|
2026-04-26 05:20:47 +07:00
|
|
|
|
|
|
|
|
private float _lastInteractionTime;
|
2026-04-28 00:07:42 +07:00
|
|
|
private const float IDLE_TIMEOUT = 5.0f;
|
2026-04-25 18:20:16 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
private Tween _pulseTween;
|
2026-04-26 04:39:59 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
public override void Initialize(VisualElement uxmlRoot, UIManager manager)
|
|
|
|
|
{
|
|
|
|
|
base.Initialize(uxmlRoot, manager);
|
2026-04-26 04:39:59 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
_logo = root.Q<VisualElement>("Logo");
|
|
|
|
|
_ribbon = root.Q<VisualElement>("Ribbon");
|
|
|
|
|
_virtualCursor = root.Q<VisualElement>("VirtualCursor");
|
2026-04-25 18:20:16 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
if (_logo == null)
|
2026-04-27 15:48:17 +07:00
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
Debug.LogError($"[MainMenuController] Element 'Logo' not found in UXML! Root children: {root.childCount}");
|
|
|
|
|
return;
|
2026-04-27 15:48:17 +07:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
_logo.RegisterCallback<PointerDownEvent>(OnLogoClicked);
|
2026-04-26 05:20:47 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
// Bind Buttons with null checks
|
|
|
|
|
var settingsBtn = root.Q<Button>("SettingsBtn");
|
|
|
|
|
if (settingsBtn != null) settingsBtn.clicked += () => uiManager.Push<SettingsController>();
|
|
|
|
|
|
|
|
|
|
var joinBtn = root.Q<Button>("JoinBtn");
|
|
|
|
|
if (joinBtn != null) joinBtn.clicked += () => uiManager.Push<LobbyController>();
|
|
|
|
|
|
|
|
|
|
var createBtn = root.Q<Button>("CreateBtn");
|
|
|
|
|
if (createBtn != null) createBtn.clicked += () => uiManager.Push<LobbyController>();
|
|
|
|
|
|
|
|
|
|
var profileBtn = root.Q<Button>("ProfileBtn");
|
|
|
|
|
if (profileBtn != null) profileBtn.clicked += () => uiManager.Push<ProfileController>();
|
|
|
|
|
|
|
|
|
|
var exitBtn = root.Q<Button>("ExitBtn");
|
|
|
|
|
if (exitBtn != null) exitBtn.clicked += () => Application.Quit();
|
2026-04-26 05:20:47 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
StartPulse();
|
|
|
|
|
_lastInteractionTime = Time.time;
|
2026-04-26 05:20:47 +07:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
public override async Task PlayTransitionIn()
|
2026-04-26 05:20:47 +07:00
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
await base.PlayTransitionIn();
|
|
|
|
|
UnityEngine.Cursor.visible = false;
|
2026-04-26 05:20:47 +07:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
public override async Task PlayTransitionOut()
|
2026-04-26 04:39:59 +07:00
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
UnityEngine.Cursor.visible = true;
|
|
|
|
|
await base.PlayTransitionOut();
|
|
|
|
|
}
|
2026-04-27 13:27:40 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
private void StartPulse()
|
|
|
|
|
{
|
|
|
|
|
// Use Vector3.one * 1.1f for target scale
|
|
|
|
|
_pulseTween = Tween.Scale(_logo.transform, Vector3.one * 1.1f, duration: 0.8f, cycles: -1, cycleMode: CycleMode.Yoyo, ease: Ease.InOutSine);
|
2026-04-27 15:48:17 +07:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
private void OnLogoClicked(PointerDownEvent evt)
|
2026-04-27 15:48:17 +07:00
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
_lastInteractionTime = Time.time;
|
|
|
|
|
|
|
|
|
|
if (_currentState == MenuState.Idle)
|
2026-04-27 15:48:17 +07:00
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
TransitionToRibbon();
|
2026-04-27 15:48:17 +07:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
_ = uiManager.Push<LobbyController>();
|
2026-04-26 05:02:49 +07:00
|
|
|
}
|
2026-04-26 04:39:59 +07:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
private void TransitionToRibbon()
|
2026-04-26 05:20:47 +07:00
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
_currentState = MenuState.Ribbon;
|
|
|
|
|
|
|
|
|
|
// Transition Logo using Custom tween for offset
|
|
|
|
|
Tween.Custom(0f, -300f, duration: 0.5f, ease: Ease.OutQuad,
|
|
|
|
|
onValueChange: val => _logo.style.left = val);
|
2026-04-26 05:20:47 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
_ribbon.style.display = DisplayStyle.Flex;
|
|
|
|
|
_ribbon.style.opacity = 0;
|
|
|
|
|
|
|
|
|
|
// Fade in Ribbon
|
|
|
|
|
Tween.Custom(0f, 1f, duration: 0.5f, onValueChange: val => _ribbon.style.opacity = val);
|
2026-04-27 13:27:40 +07:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
private void TransitionToIdle()
|
2026-04-26 05:20:47 +07:00
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
_currentState = MenuState.Idle;
|
2026-04-27 13:27:40 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
Tween.Custom(-300f, 0f, duration: 0.5f, ease: Ease.OutQuad,
|
|
|
|
|
onValueChange: val => _logo.style.left = val);
|
|
|
|
|
|
|
|
|
|
Tween.Custom(1f, 0f, duration: 0.5f, onValueChange: val => _ribbon.style.opacity = val)
|
|
|
|
|
.OnComplete(() => _ribbon.style.display = DisplayStyle.None);
|
2026-04-26 05:20:47 +07:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
public void Update()
|
2026-04-26 04:39:59 +07:00
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
UpdateVirtualCursor();
|
2026-04-26 05:20:47 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
if (_currentState == MenuState.Ribbon)
|
|
|
|
|
{
|
|
|
|
|
if (Time.time - _lastInteractionTime > IDLE_TIMEOUT)
|
|
|
|
|
{
|
|
|
|
|
TransitionToIdle();
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-26 04:39:59 +07:00
|
|
|
}
|
2026-04-26 05:20:47 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
private void UpdateVirtualCursor()
|
2026-04-26 05:20:47 +07:00
|
|
|
{
|
2026-04-28 00:07:42 +07:00
|
|
|
if (_virtualCursor == null) return;
|
2026-04-27 13:27:40 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
Vector2 mousePos = Input.mousePosition;
|
|
|
|
|
float x = mousePos.x;
|
|
|
|
|
float y = Screen.height - mousePos.y;
|
2026-04-26 05:20:47 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
if (_currentState == MenuState.Ribbon)
|
|
|
|
|
{
|
|
|
|
|
y = Screen.height / 2f + 50;
|
|
|
|
|
}
|
2026-04-26 05:20:47 +07:00
|
|
|
|
2026-04-28 00:07:42 +07:00
|
|
|
_virtualCursor.style.left = x - _virtualCursor.layout.width / 2;
|
|
|
|
|
_virtualCursor.style.top = y - _virtualCursor.layout.height / 2;
|
2026-04-26 05:20:47 +07:00
|
|
|
}
|
2026-04-25 18:20:16 +07:00
|
|
|
}
|
|
|
|
|
}
|