环形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