using UnityEngine; using UnityEngine.UI; using DG.Tweening; using System.Collections.Generic; using System.Collections; public class RankUpLooper : MonoBehaviour { public ScrollRect scrollRect; public RectTransform content; public RectTransform viewport; public GameObject itemPrefab; public int itemCount = 20; public int targetRankIndex = 2; public float scrollSpeed = 2000f; public float scrollDuration = 1.5f; public float stopDelay = 0.5f; private RectTransform highlightItem; private float itemHeight; private bool isAnimating = false; private float contentResetThreshold; private List allItems = new List(); public void OnClickRankUp() { PlayRankUp(currentRankIndex: 10, targetRankIndex: 2);// 从第10名冲到第2名(模拟) } void Start() { InitRankList(); } void InitRankList() { allItems.Clear(); foreach (Transform child in content) Destroy(child.gameObject); // 创建原始项 for (int i = 0; i < itemCount; i++) { var item = Instantiate(itemPrefab, content); item.GetComponentInChildren().text = $"#{i + 1}"; allItems.Add(item); } // 复制一轮,实现循环 for (int i = 0; i < itemCount; i++) { var clone = Instantiate(itemPrefab, content); clone.GetComponentInChildren().text = $"#{i + 1} (copy)"; allItems.Add(clone); } LayoutRebuilder.ForceRebuildLayoutImmediate(content); // 获取 item 高度 itemHeight = allItems[0].GetComponent().rect.height; contentResetThreshold = itemHeight * itemCount; } public void PlayRankUp(int currentRankIndex, int targetRankIndex) { if (isAnimating) return; this.targetRankIndex = targetRankIndex; var original = allItems[currentRankIndex - 1]; highlightItem = Instantiate(original, viewport).GetComponent(); highlightItem.name = "HighlightItem"; highlightItem.SetAsLastSibling(); if (!highlightItem.TryGetComponent(out CanvasGroup group)) group = highlightItem.gameObject.AddComponent(); group.alpha = 1f; highlightItem.localScale = Vector3.one * 1.2f; isAnimating = true; scrollRect.enabled = false; // 🔄 强制刷新布局 LayoutRebuilder.ForceRebuildLayoutImmediate(content); itemHeight = allItems[0].GetComponent().rect.height; contentResetThreshold = itemHeight * itemCount; // 🎯 提前计算目标位置 Vector3 targetPosition = CalculateTargetLocalPosition(targetRankIndex); // 启动滚动协程(传入目标位置) StartCoroutine(ScrollAndStop(targetPosition)); } // IEnumerator DelayedScroll() // { // // 🔄 强制更新布局 // LayoutRebuilder.ForceRebuildLayoutImmediate(content); // // ⏳ 等待一帧,确保布局完成 // yield return new WaitForEndOfFrame(); // // 再次确认布局高度等 // itemHeight = allItems[0].GetComponent().rect.height; // contentResetThreshold = itemHeight * itemCount; // // 🔥 启动滚动 // StartCoroutine(ScrollAndStop()); // } IEnumerator ScrollAndStop(Vector3 targetPos) { float elapsed = 0f; while (elapsed < scrollDuration) { content.anchoredPosition += Vector2.up * scrollSpeed * Time.deltaTime; // 无限滚动逻辑 if (content.anchoredPosition.y >= contentResetThreshold) { content.anchoredPosition -= Vector2.up * contentResetThreshold; } elapsed += Time.deltaTime; yield return null; } yield return new WaitForSeconds(stopDelay); SnapToTarget(targetPos); } void SnapToTarget(Vector3 targetPos) { content.DOAnchorPos(targetPos, 0.6f).SetEase(Ease.OutCubic); highlightItem.DOScale(Vector3.one, 0.3f).SetEase(Ease.OutBack); highlightItem.DOAnchorPosY(0, 0.4f).SetEase(Ease.OutSine).OnComplete(() => { isAnimating = false; }); } private Vector3 CalculateTargetLocalPosition(int targetRank) { float spacing = 0f; float totalItemHeight = itemHeight + spacing; float contentY = (targetRank - 1) * totalItemHeight; float centeredOffset = contentY - (viewport.rect.height - totalItemHeight) / 2f; // 限制 offset 范围 centeredOffset = Mathf.Clamp(centeredOffset, 0, content.rect.height - viewport.rect.height); return new Vector3(0f, centeredOffset, 0f); } void UpdateHighlightPosition() { RectTransform targetItem = allItems[targetRankIndex - 1].GetComponent(); Vector3 worldPos = targetItem.position; Vector2 localPoint; RectTransformUtility.ScreenPointToLocalPointInRectangle(viewport, worldPos, null, out localPoint); highlightItem.anchoredPosition = localPoint; } // void SnapToTarget() // { // // 停止后直接对 content 归位 // float targetY = itemHeight * (targetRankIndex - 1); // float totalContentHeight = content.rect.height; // float viewHeight = viewport.rect.height; // float offset = Mathf.Clamp(targetY - (viewHeight - itemHeight) / 2f, 0, totalContentHeight - viewHeight); // content.anchoredPosition = new Vector2(0, offset); // // 获取目标 item 的本地位置(在 content 中的位置) // RectTransform targetItem = allItems[targetRankIndex - 1].GetComponent(); // // 计算目标位置相对 viewport 的 localPosition // Vector3 worldPos = targetItem.position; // Vector2 localPoint; // RectTransformUtility.ScreenPointToLocalPointInRectangle(viewport, worldPos, null, out localPoint); // // Animate highlightItem 到目标位置 // highlightItem.DOAnchorPos(localPoint, 0.4f).SetEase(Ease.OutSine); // highlightItem.DOScale(Vector3.one, 0.3f).SetEase(Ease.OutBack).OnComplete(() => // { // isAnimating = false; // }); // } }