仿方舟獎勵列表(左半部分)移動按鈕列表元件
阿新 • • 發佈:2021-01-29
技術標籤:unity
文章目錄
效果圖
原始碼
// ****************************************************************** // /\ /| @file MoveButtonlist.cs // \ V/ @brief 移動按鈕列表元件 // | "") @author Shadowrabbit, [email protected] // / | // / \\ @Modified 2021-01-25 18:00:40 // *(__\_\ @Copyright Copyright (c) 2021, Shadowrabbit // ****************************************************************** using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class MoveButtonList : MonoBehaviour { #region Field public int buttonCount; //按鈕的數量 public GameObject protoButton; //按鈕原型體 public float spacingY; //Y軸偏移座標 public float movingSpacingY; //Y軸位移偏移座標 protected Action<int> onClick; //按鈕點選回撥 <int:按鈕在按鈕組內的索引> protected bool isMoving; //是否在移動中 protected readonly Dictionary<int, Button> mapCompButtons = new Dictionary<int, Button>(); //按鈕字典 <int:按鈕索引,Button:按鈕元件例項> [SerializeField] protected int currentSelectedIndex; //當前選中的索引 private float m_protoWidth; //原型體寬度 private float m_protoHeight; //原型體高度 private float m_movingProgress; //移動進度 [SerializeField] private float movingSpeed; //移動速度 百分比/幀 private readonly List<Vector2> m_normalPostionList = new List<Vector2>(); //正常情況下的座標列表 private readonly List<Vector2> m_selectedPostionList = new List<Vector2>(); //被選中情況下的座標列表 #endregion /// <summary> /// 設定按鈕標題 /// </summary> /// <param name="_arrayTitle"></param> public void SetButtonTitles(string[] _arrayTitle) { if (_arrayTitle == null) { Debug.LogError("_arrayTitle == null"); return; } for (var i = 0; i < buttonCount; i++) { var button = mapCompButtons[i]; var compTextList = button.GetComponents<Text>(); foreach (var compText in compTextList) { compText.text = _arrayTitle[i]; } } } /// <summary> /// 設定點選回撥 /// </summary> /// <param name="_onClick"></param> public void AddOnClickListener(Action<int> _onClick) { onClick = _onClick; } /// <summary> /// 獲取移動狀態 /// </summary> /// <returns></returns> public bool GetMovingState() { return isMoving; } /// <summary> /// 選擇對應索引 /// </summary> /// <param name="_index"></param> public void SelectIndex(int _index) { currentSelectedIndex = _index; //小於索引的按鈕去上面 大於索引的按鈕去下面 StopCoroutine(nameof(PositionAdjust)); StartCoroutine(nameof(PositionAdjust)); } /// <summary> /// 初始化 /// </summary> private void Init() { //原型體校驗 CheckProtoTransform(); //內容節點校驗 CheckContentTransform(); //修正內容節點尺寸 FixContentSize(); //計算每個按鈕在兩種狀態下的座標 CalcButtonPostion(); } /// <summary> /// 原型體校驗 /// </summary> private void CheckProtoTransform() { //原型體校驗 if (protoButton == null) { Debug.LogError("找不到原型體"); return; } var compProtoRectTransform = protoButton.GetComponent<RectTransform>(); compProtoRectTransform.pivot = new Vector2(0.5f, 1); compProtoRectTransform.anchorMin = new Vector2(0.5f, 1); compProtoRectTransform.anchorMax = new Vector2(0.5f, 1); compProtoRectTransform.anchoredPosition = Vector2.zero; //獲取寬高 var rect = compProtoRectTransform.rect; m_protoWidth = rect.width; m_protoHeight = rect.height; } /// <summary> /// 內容節點校驗 /// </summary> private void CheckContentTransform() { var compRectTransformContent = GetComponent<RectTransform>(); compRectTransformContent.pivot = new Vector2(0, 1); compRectTransformContent.anchorMin = new Vector2(0, 1); compRectTransformContent.anchorMax = new Vector2(0, 1); } /// <summary> /// 修正內容節點尺寸 /// </summary> private void FixContentSize() { var compProtoRectTransform = transform.GetComponent<RectTransform>(); var width = m_protoWidth; var height = m_protoHeight * buttonCount + (buttonCount - 2) * spacingY + movingSpacingY; compProtoRectTransform.sizeDelta = new Vector2(width, height); } /// <summary> /// 按鈕組建立 /// </summary> protected virtual void ButtonGroupCreate() { //根據數量建立按鈕 for (var i = 0; i < buttonCount; i++) { var buttonIndex = i; //當前的按鈕索引 var objButton = Instantiate(protoButton, transform); //按鈕克隆體 objButton.name = protoButton.name + i; //設定名字 var compButton = objButton.GetComponent<Button>(); //button元件 compButton.onClick.AddListener(() => { if (isMoving) return; onClick?.Invoke(buttonIndex); SelectIndex(buttonIndex); }); //為每個按鈕設定一個匿名點選方法 回撥index mapCompButtons.Add(i, compButton); } } /// <summary> /// 位置調整 /// </summary> /// <returns></returns> private IEnumerator PositionAdjust() { isMoving = true; m_movingProgress = 0f; var isMoveFinished = false; //移動是否完成 while (!isMoveFinished) { m_movingProgress += movingSpeed; //移動進度增加 isMoveFinished = true; //每次都假定本次完成 for (var i = 0; i < buttonCount; i++) { var compButton = mapCompButtons[i]; //按鈕元件 var compRectTransform = compButton.GetComponent<RectTransform>(); //transform元件 var uiPostion = compRectTransform.anchoredPosition; //當前按鈕的UI座標 var isSelected = i <= currentSelectedIndex; //當前按鈕是否處於選中狀態 var currentY = -uiPostion.y; //當前按鈕的Y軸座標 轉換成正數 //選中狀態下 if (isSelected) { //按鈕已經達到了選中狀態的位置 if (currentY <= m_selectedPostionList[i].y) continue; //按鈕沒有達到選中狀態的位置 插值 compRectTransform.anchoredPosition3D = new Vector3(0, -Mathf.Lerp(m_normalPostionList[i].y, m_selectedPostionList[i].y, m_movingProgress), 0); //存在沒有移動完成的按鈕 移動流程判定未完成 isMoveFinished = false; } //沒選中的狀態下 else { //按鈕已經達到了非選中狀態的位置 if (currentY >= m_normalPostionList[i].y) continue; compRectTransform.anchoredPosition3D = new Vector3(0, -Mathf.Lerp(m_selectedPostionList[i].y, m_normalPostionList[i].y, m_movingProgress), 0); isMoveFinished = false; } } yield return null; } isMoving = false; } /// <summary> /// 撤銷全部監聽 /// </summary> private void RemoveAllListeners() { foreach (var button in mapCompButtons.Values) { button.onClick.RemoveAllListeners(); } } /// <summary> /// 計算按鈕在兩種狀態下的座標 /// </summary> private void CalcButtonPostion() { m_selectedPostionList.Clear(); m_normalPostionList.Clear(); for (var i = 0; i < buttonCount; i++) { m_selectedPostionList.Add(new Vector2(0, m_protoHeight * i + spacingY * i)); //選中狀態下的座標 在上方 if (i == 0) { m_normalPostionList.Add(new Vector2(0, 0)); //第一個按鈕通常情況下也在上方 continue; } m_normalPostionList.Add(new Vector2(0, movingSpacingY + i * m_protoHeight + (i - 1) * spacingY)); //通常情況下的座標 在下方 } } /// <summary> /// 設定預設座標 /// </summary> private void SetDefaultPostion() { for (var i = 0; i < buttonCount; i++) { mapCompButtons.TryGetValue(i, out var compButton); //按鈕元件 if (compButton == null) { return; } var compRectTransform = compButton.GetComponent<RectTransform>(); //transform元件 compRectTransform.anchoredPosition3D = new Vector3(0, -m_normalPostionList[i].y, 0); } } #region Unity Life private void Awake() { //初始化 Init(); //按鈕組建立 ButtonGroupCreate(); //設定預設座標 SetDefaultPostion(); } private void OnDestroy() { RemoveAllListeners(); } #endregion #region Editor private void OnValidate() { Init(); SetDefaultPostion(); } /// <summary> /// 編輯器修正內容節點尺寸 /// </summary> protected void EditorFixContentSize() { Init(); FixContentSize(); } /// <summary> /// 編輯器預覽按鈕組 /// </summary> protected void EditorShowButtons() { Init(); EditorClearItems(); ButtonGroupCreate(); SetDefaultPostion(); } /// <summary> /// 編輯器模擬選中某個index 執行中限定 /// </summary> protected void EditorSelect() { Init(); SelectIndex(currentSelectedIndex); } /// <summary> /// 編輯器清理item /// </summary> protected void EditorClearItems() { Init(); for (var i = transform.childCount - 1; i >= 0; i--) { var compTransformChild = transform.GetChild(i); DestroyImmediate(compTransformChild.gameObject); } mapCompButtons.Clear(); } #endregion }
編輯器原始碼
// ****************************************************************** // /\ /| @file MoveButtonListEditor.cs // \ V/ @brief 移動按鈕列表元件編輯器 // | "") @author Shadowrabbit, [email protected] // / | // / \\ @Modified 2021-01-26 14:59:27 // *(__\_\ @Copyright Copyright (c) 2021, Shadowrabbit // ****************************************************************** using System.Reflection; using UnityEditor; using UnityEngine; [CustomEditor(typeof(MoveButtonList))] public class MoveButtonListEditor : Editor { private MethodInfo m_editorClearItems; //清理items private MethodInfo m_editorFixContentSize; //修正內容節點尺寸 private MethodInfo m_editorShowItems; //預覽佈局效果 private MethodInfo m_editorSelect; //模擬選中某個item private void OnEnable() { //反射獲取例項私有方法 m_editorClearItems = target.GetType().GetMethod("EditorClearItems", BindingFlags.Instance | BindingFlags.NonPublic); m_editorFixContentSize = target.GetType() .GetMethod("EditorFixContentSize", BindingFlags.Instance | BindingFlags.NonPublic); m_editorShowItems = target.GetType() .GetMethod("EditorShowButtons", BindingFlags.Instance | BindingFlags.NonPublic); m_editorSelect = target.GetType() .GetMethod("EditorSelect", BindingFlags.Instance | BindingFlags.NonPublic); } private void OnDisable() { m_editorClearItems = null; m_editorFixContentSize = null; } public override void OnInspectorGUI() { base.OnInspectorGUI(); serializedObject.Update(); if (GUILayout.Button("尺寸修正")) { m_editorFixContentSize?.Invoke(target, null); EditorUtility.SetDirty(target); } if (GUILayout.Button("清空子節點")) { m_editorClearItems?.Invoke(target, null); EditorUtility.SetDirty(target); } if (GUILayout.Button("預覽")) { m_editorShowItems?.Invoke(target, null); EditorUtility.SetDirty(target); } GUILayout.Label("********執行中限定********"); if (GUILayout.Button("模擬選中")) { m_editorSelect?.Invoke(target, null); EditorUtility.SetDirty(target); } serializedObject.ApplyModifiedProperties(); } }
配合狀態按鈕效果
狀態按鈕版原始碼
// ****************************************************************** // /\ /| @file MoveStateButtonList.cs // \ V/ @brief 移動狀態按鈕列表元件 // | "") @author Shadowrabbit, [email protected] // / | // / \\ @Modified 2021-01-26 16:06:58 // *(__\_\ @Copyright Copyright (c) 2021, Shadowrabbit // ****************************************************************** public class MoveStateButtonList : MoveButtonList { protected override void ButtonGroupCreate() { //根據數量建立按鈕 for (var i = 0; i < buttonCount; i++) { var buttonIndex = i; //當前的按鈕索引 var objButton = Instantiate(protoButton, transform); //按鈕克隆體 objButton.name = protoButton.name + i; //設定名字 var compStateButton = objButton.GetComponent<StateButton>(); //button元件 compStateButton.IsSelect = i == 0; //stateButton狀態 compStateButton.onClick.AddListener(() => { if (isMoving) return; onClick?.Invoke(buttonIndex); UpdateStateButtonListSelect(buttonIndex); SelectIndex(buttonIndex); }); //為每個按鈕設定一個匿名點選方法 回撥index mapCompButtons.Add(i, compStateButton); } } /// <summary> /// 設定stateButton列表選中狀態 /// </summary> /// <param name="_index"></param> private void UpdateStateButtonListSelect(int _index) { for (var i = 0; i < buttonCount; i++) { var compStateButton = mapCompButtons[i].GetComponent<StateButton>(); compStateButton.IsSelect = _index == i; } } }