179 lines
6.9 KiB
C#
179 lines
6.9 KiB
C#
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
using PrimeTween;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Hallucinate.UI
|
|
{
|
|
public class MainMenuController : BaseUIController
|
|
{
|
|
public enum MenuState { Idle, Ribbon }
|
|
private MenuState _currentState = MenuState.Idle;
|
|
|
|
private VisualElement _logo;
|
|
private VisualElement _ribbon;
|
|
private VisualElement _logoSpace;
|
|
|
|
private float _lastInteractionTime;
|
|
private const float IDLE_TIMEOUT = 5.0f;
|
|
|
|
private Tween _pulseTween;
|
|
private Tween _rotationTween;
|
|
|
|
public override void Initialize(VisualElement uxmlRoot, UIManager manager)
|
|
{
|
|
base.Initialize(uxmlRoot, manager);
|
|
|
|
_logo = root.Q<VisualElement>("Logo");
|
|
_ribbon = root.Q<VisualElement>("Ribbon");
|
|
_logoSpace = root.Q<VisualElement>("LogoSpace");
|
|
|
|
if (_logo == null)
|
|
{
|
|
Debug.LogError($"[MainMenuController] Element 'Logo' not found in UXML!");
|
|
return;
|
|
}
|
|
|
|
ResetLogoPosition();
|
|
_logo.RegisterCallback<ClickEvent>(OnLogoClicked);
|
|
|
|
// Bind Buttons
|
|
root.Q<Button>("SettingsBtn").clicked += () => uiManager.Push<SettingsController>();
|
|
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>("ExitBtn").clicked += () => Application.Quit();
|
|
|
|
StartPulse();
|
|
_lastInteractionTime = Time.time;
|
|
}
|
|
|
|
private void ResetLogoPosition()
|
|
{
|
|
if (_logo == null) return;
|
|
_logo.style.left = (Screen.width / 2f) - 100;
|
|
_logo.style.top = (Screen.height / 2f) - 100;
|
|
_logo.style.width = 200;
|
|
_logo.style.height = 200;
|
|
}
|
|
|
|
public void SetGameIcon(Texture2D icon)
|
|
{
|
|
if (icon == null || _logo == null) return;
|
|
_logo.style.backgroundImage = icon;
|
|
|
|
var radius = new StyleLength(new Length(50, LengthUnit.Percent));
|
|
_logo.style.borderTopLeftRadius = radius;
|
|
_logo.style.borderTopRightRadius = radius;
|
|
_logo.style.borderBottomLeftRadius = radius;
|
|
_logo.style.borderBottomRightRadius = radius;
|
|
_logo.style.overflow = Overflow.Hidden;
|
|
|
|
var label = _logo.Q<Label>();
|
|
if (label != null) label.style.display = DisplayStyle.None;
|
|
|
|
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))));
|
|
}
|
|
|
|
public override async Task PlayTransitionIn()
|
|
{
|
|
// 1. Đưa về trạng thái Idle và reset vị trí Logo về giữa trước khi hiện
|
|
_currentState = MenuState.Idle;
|
|
ResetLogoPosition();
|
|
if (_ribbon != null)
|
|
{
|
|
_ribbon.style.display = DisplayStyle.None;
|
|
_ribbon.style.opacity = 0;
|
|
}
|
|
|
|
// 2. Chạy hiệu ứng bay vào từ bên phải của Base class
|
|
// Hàm này sẽ gọi Show() và chạy Tween di chuyển toàn bộ root
|
|
await base.PlayTransitionIn();
|
|
|
|
// 3. Sau khi bay vào xong, tự động kích hoạt Ribbon
|
|
TransitionToRibbon();
|
|
}
|
|
|
|
public override async Task PlayTransitionOut()
|
|
{
|
|
if (_rotationTween.isAlive) _rotationTween.Stop();
|
|
await base.PlayTransitionOut();
|
|
}
|
|
|
|
private void OnLogoClicked(ClickEvent evt)
|
|
{
|
|
_lastInteractionTime = Time.time;
|
|
if (_currentState == MenuState.Idle) TransitionToRibbon();
|
|
else _ = uiManager.Push<LobbyController>();
|
|
}
|
|
|
|
private async void TransitionToRibbon()
|
|
{
|
|
// Tránh chạy đè nếu đang ở Ribbon rồi
|
|
if (_currentState == MenuState.Ribbon) return;
|
|
_currentState = MenuState.Ribbon;
|
|
|
|
// Hiện Ribbon
|
|
_ribbon.style.display = DisplayStyle.Flex;
|
|
Tween.Custom(0f, 1f, duration: 0.3f, onValueChange: val => _ribbon.style.opacity = val);
|
|
|
|
// Đợi UI Toolkit cập nhật layout
|
|
await Task.Yield();
|
|
|
|
if (_logoSpace == null) return;
|
|
Rect targetBounds = _logoSpace.worldBound;
|
|
|
|
// Di chuyển Logo vào vị trí trong Ribbon
|
|
Tween.Custom(_logo.style.left.value.value, targetBounds.x, duration: 0.5f, ease: Ease.OutQuad,
|
|
onValueChange: val => _logo.style.left = val);
|
|
|
|
Tween.Custom(_logo.style.top.value.value, targetBounds.y - 35, duration: 0.5f, ease: Ease.OutQuad,
|
|
onValueChange: val => _logo.style.top = val);
|
|
|
|
Tween.Custom(_logo.style.width.value.value, 120f, duration: 0.5f, ease: Ease.OutQuad,
|
|
onValueChange: val => _logo.style.width = val);
|
|
|
|
Tween.Custom(_logo.style.height.value.value, 120f, duration: 0.5f, ease: Ease.OutQuad,
|
|
onValueChange: val => _logo.style.height = val);
|
|
|
|
_lastInteractionTime = Time.time;
|
|
}
|
|
|
|
private void TransitionToIdle()
|
|
{
|
|
if (_currentState == MenuState.Idle) return;
|
|
_currentState = MenuState.Idle;
|
|
|
|
float targetX = (Screen.width / 2f) - 100;
|
|
float targetY = (Screen.height / 2f) - 100;
|
|
|
|
Tween.Custom(_logo.style.left.value.value, targetX, duration: 0.5f, ease: Ease.OutQuad,
|
|
onValueChange: val => _logo.style.left = val);
|
|
Tween.Custom(_logo.style.top.value.value, targetY, duration: 0.5f, ease: Ease.OutQuad,
|
|
onValueChange: val => _logo.style.top = val);
|
|
Tween.Custom(_logo.style.width.value.value, 200f, duration: 0.5f, ease: Ease.OutQuad,
|
|
onValueChange: val => _logo.style.width = val);
|
|
Tween.Custom(_logo.style.height.value.value, 200f, duration: 0.5f, ease: Ease.OutQuad,
|
|
onValueChange: val => _logo.style.height = val);
|
|
|
|
Tween.Custom(1f, 0f, duration: 0.5f, onValueChange: val => _ribbon.style.opacity = val)
|
|
.OnComplete(() => _ribbon.style.display = DisplayStyle.None);
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
if (_currentState == MenuState.Ribbon && Time.time - _lastInteractionTime > IDLE_TIMEOUT)
|
|
{
|
|
TransitionToIdle();
|
|
}
|
|
}
|
|
|
|
private void StartPulse()
|
|
{
|
|
_pulseTween = Tween.Scale(_logo.transform, Vector3.one * 1.1f, duration: 0.8f, cycles: -1, cycleMode: CycleMode.Yoyo, ease: Ease.InOutSine);
|
|
}
|
|
}
|
|
}
|