環形ui管理器
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
using System.Collections;
using Unity.VisualScripting;public class LevelSelector : MonoBehaviour
{public GameObject levelButtonPrefab; // 關卡按鈕的Prefabpublic Transform levelGroup;public int levelCount = 6; // 關卡數量public float radius = 300f; // 半徑public float moveDuration = 1f; // 移動持續時間public float yOffsetStep = 80f; // Y軸偏移步長public Button nextLevelButton;public Button previousLevelButton; private List<GameObject> levelButtons = new List<GameObject>();private List<float> yOffsets;private List<int> sortOrders;private float angleStep;private float currentAngle = 0f;void Start(){// 計算每個按鈕之間的角度angleStep = 360f / levelCount;// 自動計算對稱的Y軸偏移量yOffsets = CalculateSymmetricYOffsets(levelCount);// 初始化排序順序sortOrders = CalculateRenderingOrder(levelCount);// 創建關卡按鈕并設置它們的位置for (int i = 0; i < levelCount; i++){GameObject levelButton = Instantiate(levelButtonPrefab, levelGroup);levelButton.gameObject.name = i.ToString();levelButton.GetComponentInChildren<Text>().text = i.ToString();levelButton.GetComponent<LevelButton>().RegisterLevelSelector(this);levelButtons.Add(levelButton);}UpdateButtonPositions();if (nextLevelButton != null){nextLevelButton.onClick.AddListener(OnNextLevelButtonClicked);}if (previousLevelButton != null){previousLevelButton.onClick.AddListener(OnPreviousLevelButtonClicked); // 添加上一關按鈕的監聽器}}void UpdateButtonPositions(){for (int i = 0; i < levelButtons.Count; i++){float angle = (360f / levelButtons.Count) * i;float x = Mathf.Sin(angle * Mathf.Deg2Rad) * radius;float z = Mathf.Cos(angle * Mathf.Deg2Rad) * radius;// 獲取計算后的Y軸偏移float yOffset = yOffsets[i];// 設置按鈕位置RectTransform rectTransform = levelButtons[i].GetComponent<RectTransform>();rectTransform.anchoredPosition3D = new Vector3(x, yOffset, z);// 設置Canvas的sortingOrderCanvas canvas = levelButtons[i].GetComponent<Canvas>();canvas.sortingOrder = sortOrders[i];// 設置透明度和縮放值SetButtonAppearance(levelButtons[i], sortOrders[i]);}// 打印最上層和最下層按鈕的名稱PrintTopButtonName();PrintBottomButtonName();}void OnNextLevelButtonClicked(){RotateButtons(-1);}void OnPreviousLevelButtonClicked(){RotateButtons(1);}Coroutine moveCoroutine;public void MoveLevelToBottom(int index){int bottomIndex = sortOrders.IndexOf(sortOrders.Count/2);int currentIndex =int.Parse(levelButtons[bottomIndex].name);bool isCounterclockwise = true;Debug.Log("當前顯示 "+currentIndex);int movesRequired =index - currentIndex;if (movesRequired < 0){isCounterclockwise=false;movesRequired = -movesRequired;}Debug.Log("需要偏移"+movesRequired);if (moveCoroutine != null){return;}moveCoroutine = StartCoroutine(MoveButtonCoroutine(movesRequired, isCounterclockwise));}IEnumerator MoveButtonCoroutine(int movesRequired ,bool isCounterclockwise){int t = 0;int diction = isCounterclockwise ? -1 : 1;while (movesRequired>t){RotateButtons(diction);Debug.Log("移動一次");yield return new WaitForSeconds(moveDuration);t++;}moveCoroutine = null;}void RotateButtons(int direction){nextLevelButton.interactable = false;previousLevelButton.interactable = false;List<Vector3> targetPositions = new List<Vector3>();List<float> targetYOffsets = new List<float>();List<int> newSortOrders = new List<int>();// 計算目標位置、Y軸偏移和順序for (int i = 0; i < levelButtons.Count; i++){int targetIndex = (i + direction + levelCount) % levelCount;float angle = currentAngle + angleStep * targetIndex;float x = Mathf.Sin(angle * Mathf.Deg2Rad) * radius;float z = Mathf.Cos(angle * Mathf.Deg2Rad) * radius;float yOffset = yOffsets[targetIndex];targetPositions.Add(new Vector3(x, yOffset, z));targetYOffsets.Add(yOffset);newSortOrders.Add(sortOrders[targetIndex]);}// 更新排序順序sortOrders = newSortOrders;for (int i = 0; i < levelButtons.Count; i++){Canvas canvas = levelButtons[i].GetComponent<Canvas>();canvas.sortingOrder = sortOrders[i];// 更新透明度和縮放值SetButtonAppearance(levelButtons[i], sortOrders[i]);}// 平滑移動并更新Y軸偏移和排序順序for (int i = 0; i < levelButtons.Count; i++){RectTransform rectTransform = levelButtons[i].GetComponent<RectTransform>();int index = i;// 平滑移動位置rectTransform.DOAnchorPos3D(targetPositions[i], moveDuration).OnComplete(() =>{// 更新Y軸偏移if (index < yOffsets.Count){yOffsets[index] = targetYOffsets[index];}if (index == levelButtons.Count - 1){Debug.Log("完成移動");// 重新啟用按鈕nextLevelButton.interactable = true;previousLevelButton.interactable = true;// 打印最上層和最下層按鈕的名稱PrintTopButtonName();PrintBottomButtonName();}});}// 更新角度currentAngle += angleStep * direction;}void PrintTopButtonName(){int topIndex = sortOrders.IndexOf(0); // 假設排序順序0是最上層if (topIndex >= 0 && topIndex < levelButtons.Count){string topButtonName = levelButtons[topIndex].name;Debug.Log("最上層按鈕的名稱: " + topButtonName);}}void PrintBottomButtonName(){int bottomIndex = sortOrders.IndexOf(sortOrders.Count/2);string bottomButtonName = levelButtons[bottomIndex].name;Debug.Log("最下層按鈕的名稱: " + bottomButtonName);}List<float> CalculateSymmetricYOffsets(int count){List<float> offsets = new List<float>();float step = yOffsetStep;int half = count / 2;float y = 0;for (int i = 0; i < count; i++){if (i != 0){y = i * step;if (i > half){y = (count - i) * step;}}offsets.Add(y);}return offsets;}List<int> CalculateRenderingOrder(int count){List<int> order = new List<int>();int middle = count / 2;int number = 0;for (int i = 0; i < count; i++){if (i <= middle){number = middle - i;}else{number = order[count - i];}order.Add(number);}return order;}void SetButtonAppearance(GameObject button, int sortOrder){float alpha = Mathf.Lerp(0.3f, 1f, (float)sortOrder / (levelCount / 2));float scale = Mathf.Lerp(0.8f, 1f, (float)sortOrder / (levelCount / 2));// 設置透明度CanvasGroup canvasGroup = button.GetComponent<CanvasGroup>();if (canvasGroup != null){canvasGroup.alpha = alpha;}// 設置縮放值RectTransform rectTransform = button.GetComponent<RectTransform>();if (rectTransform != null){rectTransform.localScale = new Vector3(scale, scale, scale);}}
}
關卡按鈕代碼
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class LevelButton : MonoBehaviour
{private LevelSelector levelSelector;[SerializeField] private Button levelButton;public void RegisterLevelSelector(LevelSelector levelSelector){this.levelSelector = levelSelector;levelButton.onClick.AddListener(() =>{levelSelector.MoveLevelToBottom(int.Parse(gameObject.name));});}}
配置
如果關卡按鈕被背景遮住
給添加canvas組件,Overrider Sorting 打開 sort order 為-1