1. 程式人生 > 其它 >仿方舟獎勵列表(左半部分)移動按鈕列表元件

仿方舟獎勵列表(左半部分)移動按鈕列表元件

技術標籤: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;
        }
    }
}