using System.Collections.Generic; using DG.Tweening; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; public class TeamSelectorMiniCardHover : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerMoveHandler { [SerializeField] float hoverScale = 1.05f; [SerializeField] float hoverTweenTime = 0.12f; [SerializeField] float hoverExpand = 0.18f; [SerializeField] float hoverCompress = 0.08f; [SerializeField] float gyroMaxAngle = 6f; [SerializeField] float gyroFollow = 8f; bool isHovering; Camera lastEventCamera; LayoutElement layoutElement; class LayoutInfo { public TeamSelectorMiniCardHover card; public LayoutElement layout; public float baseSize; } static readonly Dictionary> layoutCache = new(); static readonly Dictionary layoutAxisIsHorizontal = new(); void Awake() { layoutElement = GetComponent() ?? gameObject.AddComponent(); var g = GetComponent(); if (g != null) g.raycastTarget = true; } public void OnPointerEnter(PointerEventData eventData) { isHovering = true; lastEventCamera = eventData.enterEventCamera; ApplyHoverLayout(true); transform.DOKill(); transform.DOScale(hoverScale, hoverTweenTime).SetEase(Ease.OutCubic); } public void OnPointerExit(PointerEventData eventData) { isHovering = false; ApplyHoverLayout(false); transform.DOKill(); transform.DOScale(1f, hoverTweenTime).SetEase(Ease.OutCubic); transform.localRotation = Quaternion.identity; } public void OnPointerMove(PointerEventData eventData) { lastEventCamera = eventData.enterEventCamera; } void LateUpdate() { if (!isHovering) return; RectTransform rt = transform as RectTransform; if (rt == null) return; if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, Input.mousePosition, lastEventCamera, out Vector2 localPoint)) return; Vector2 size = rt.rect.size; if (size.x <= 0f || size.y <= 0f) return; Vector2 norm = new Vector2(localPoint.x / size.x, localPoint.y / size.y); norm = Vector2.ClampMagnitude(norm * 2f, 1f); float xRot = -norm.y * gyroMaxAngle; float yRot = norm.x * gyroMaxAngle; Quaternion target = Quaternion.Euler(xRot, yRot, 0f); transform.localRotation = Quaternion.Slerp(transform.localRotation, target, Time.unscaledDeltaTime * gyroFollow); } void ApplyHoverLayout(bool active) { Transform parent = transform.parent; if (parent == null) return; List group = GetOrCacheGroup(parent); if (group == null || group.Count == 0) return; bool horizontal = layoutAxisIsHorizontal.TryGetValue(parent, out bool h) ? h : true; for (int i = 0; i < group.Count; i++) { LayoutInfo info = group[i]; if (info == null || info.layout == null) continue; float baseSize = info.baseSize; float target = baseSize; if (active) { target = info.card == this ? baseSize * (1f + hoverExpand) : baseSize * Mathf.Max(0.1f, 1f - hoverCompress); } if (horizontal) info.layout.preferredWidth = target; else info.layout.preferredHeight = target; } ForceRebuildParentLayout(); } List GetOrCacheGroup(Transform parent) { if (layoutCache.TryGetValue(parent, out var cached) && cached != null && cached.Count > 0) { return cached; } bool horizontal = parent.GetComponent() != null; bool vertical = parent.GetComponent() != null; layoutAxisIsHorizontal[parent] = !vertical || horizontal; var list = new List(); TeamSelectorMiniCardHover[] cards = parent.GetComponentsInChildren(true); for (int i = 0; i < cards.Length; i++) { var card = cards[i]; if (card == null) continue; var le = card.layoutElement ?? card.GetComponent() ?? card.gameObject.AddComponent(); RectTransform rt = card.transform as RectTransform; float baseSize = 100f; if (rt != null) { baseSize = horizontal ? rt.rect.width : rt.rect.height; } if (horizontal) le.preferredWidth = baseSize; else le.preferredHeight = baseSize; list.Add(new LayoutInfo { card = card, layout = le, baseSize = baseSize }); } layoutCache[parent] = list; return list; } void ForceRebuildParentLayout() { if (transform.parent == null) return; RectTransform prt = transform.parent as RectTransform; if (prt != null) LayoutRebuilder.ForceRebuildLayoutImmediate(prt); } }