unity圆形Slider

风流意气都作罢 提交于 2019-12-28 02:36:42

如题
Runtime

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

/// <summary>
/// 环状进度条
/// </summary>

public class CircleSlider : Selectable , IDragHandler , IEndDragHandler , IBeginDragHandler
{
    [SerializeField]
    private RectTransform m_HandleRect;

    public RectTransform handleRect
    {
        get { return m_HandleRect; }
        set { if(SetPropertyUtility.SetClass(ref m_HandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } }
    }

    [SerializeField]
    private RectTransform m_FillRect;

    public RectTransform fillrect
    {
        get { return m_FillRect; }
        set { if (SetPropertyUtility.SetClass(ref m_FillRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } }
    }

    [SerializeField]
    private float m_MinValue = 0;
    public float minValue { get { return m_MinValue; } set { if (SetPropertyUtility.SetStruct(ref m_MinValue, value)) { Set(m_Value); UpdateVisuals(); } } }

    [SerializeField]
    private float m_MaxValue = 1;
    public float maxValue { get { return m_MaxValue; } set { if (SetPropertyUtility.SetStruct(ref m_MaxValue, value)) { Set(m_Value); UpdateVisuals(); } } }

    [SerializeField]
    private float m_Value;
    public float value {
        get { 
            if (wholeNumbers)
                return Mathf.Round(m_Value);
            return m_Value;
        }
        set
        {
            Set(value); 
        }
    }

    [SerializeField]
    private float m_Angle;
    public float angle
    {
        get
        {
            return m_Angle;
        }
        set
        {
            m_Angle = value;
        }
    }

    [SerializeField]
    private Image.Origin360 m_FillOrigin;
    public Image.Origin360 fillOrigin { get { return m_FillOrigin; } set { if (SetPropertyUtility.SetStruct(ref m_FillOrigin, value)) { Set(m_Value); UpdateVisuals(); } } }

    [SerializeField]
    private float m_Radius;
    public float radius { get { return m_Radius; } set { if (SetPropertyUtility.SetStruct(ref m_Radius, value)) { UpdateVisuals(); } } }

    [SerializeField]
    private bool m_ClockWise;

    public bool clockWise {
        get { return m_ClockWise; }
        set {
            if (SetPropertyUtility.SetStruct(ref m_ClockWise, value))
            {
                UpdateCachedReferences();
                UpdateVisuals();
            } }
    }

    [SerializeField]
    private bool m_WholeNumbers = false;
    public bool wholeNumbers { get { return m_WholeNumbers; } set { if (SetPropertyUtility.SetStruct(ref m_WholeNumbers, value)) { Set(m_Value); UpdateVisuals(); } } }

    public float normalizedValue
    {
        get
        {
            if (Mathf.Approximately(m_MinValue, m_MaxValue))
                return 0;
            return Mathf.InverseLerp(m_MinValue, m_MaxValue, value);
        }

        set
        {
            this.value = Mathf.Lerp(m_MinValue, m_MaxValue, value);
        }
    }

    private Action<float> m_OnValueChanged;
    private Action m_OnDragEnd;
    private Action m_OnTweenComplete;
    private Image m_FillImage;
    private bool m_DelayedUpdateVisuals = false;
    private bool m_dragHandler;
    private Camera m_eventCamera;
    private Coroutine m_TweenCor;
    private Vector2 lastPoint;

    protected override void OnEnable()
    {
        UpdateCachedReferences();
        Set(m_Value);
    }

#if UNITY_EDITOR
    protected override void OnValidate()
    {
        base.OnValidate();
        if (IsActive())
        {
            UpdateCachedReferences();
            Set(m_Value, false);
            m_DelayedUpdateVisuals = true;
        }
    }
#endif

    void Update()
    {
        if (m_DelayedUpdateVisuals)
        {
            m_DelayedUpdateVisuals = false;
            UpdateVisuals();
            
        }
        if(!(IsActive() && IsInteractable()))
        {
            return;
        }
        if (m_dragHandler)
        {
            if (m_eventCamera == null)
            {
                m_eventCamera = GetComponentInParent<Canvas>().worldCamera;
            }
            Vector2 localCursor;
            if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(m_FillRect, Input.mousePosition, m_eventCamera, out localCursor))
                return;
            UpdateDrag(localCursor);
        }
    }

    void UpdateDrag(Vector2 point)
    {

        var a = Mathf.Atan2(point.y, point.x);
        var b = Mathf.Atan2(lastPoint.y, lastPoint.x);
        var c = a - b;
        if(c > Mathf.PI)
        {
            c -= 2 * Mathf.PI;
        }else if(c < -Mathf.PI)
        {
            c += 2 * Mathf.PI;
        }
        
        float valAngle = -c * Mathf.Rad2Deg;
        angle = angle + valAngle * (clockWise ? 1 : -1);
        normalizedValue = (angle / 360);
        lastPoint = point;
    }

    void UpdateDrag(PointerEventData eventData, Camera cam)
    {
        if (m_HandleRect != null)
        {
            Vector2 localCursor;
            if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(m_FillRect, eventData.position, cam, out localCursor))
                return;
            angle = GetAngle(localCursor);
            angle = clockWise ? angle : 360 - angle;
            normalizedValue = (angle / 360);
            lastPoint = localCursor;
        }
    }

    void UpdateCachedReferences()
    {
        if (m_FillRect != null)
        {
            m_FillImage = m_FillRect.GetComponent<Image>();
            m_FillImage.fillMethod = Image.FillMethod.Radial360;
            m_FillImage.fillOrigin = (int)m_FillOrigin;
            m_FillImage.fillClockwise = m_ClockWise;
        }
    }

    private bool MayDrag(PointerEventData eventData)
    {
        return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left;
    }

    public void OnDrag(PointerEventData eventData)
    {   
        if (!MayDrag(eventData))
            return;
    }

    public override void OnPointerDown(PointerEventData eventData)
    {
        if (!MayDrag(eventData))
            return;
        base.OnPointerDown(eventData);
        UpdateDrag(eventData , eventData.pressEventCamera);
    }

    public override void OnPointerUp(PointerEventData eventData)
    {
        base.OnPointerUp(eventData);
        if (m_OnDragEnd != null)
        {
            m_OnDragEnd();
        }

    }

    

    /// <summary>
    /// 返回一个顺时针角度
    /// </summary>
    /// <param name="pos"></param>
    /// <returns></returns>
    private float GetAngle(Vector2 pos)
    {
        float angle = Mathf.Atan2(pos.y, pos.x) * Mathf.Rad2Deg;
        if(angle> 90)
        {
            angle -= 90;
            angle = 360 - angle;
        }
        else
        {
            angle -= 90;
            angle *= -1;
        }
        return angle;
    }

    private void UpdateVisuals()
    {
#if UNITY_EDITOR
        if (!Application.isPlaying)
            UpdateCachedReferences();
#endif
        if (m_FillImage != null)
        {
            m_FillImage.fillAmount = normalizedValue;
        }
        
        if (m_HandleRect != null)
        {
            m_HandleRect.transform.localPosition = CalPos(normalizedValue * 360);
        }
    }

    private Vector2 CalPos(float angle)
    {
        
        Vector2 pos = Vector2.zero;
        switch (m_FillOrigin)
        {
            case Image.Origin360.Left:
                angle = m_ClockWise ? 180 -angle : angle + 180;
                break;
            case Image.Origin360.Top:
                angle = m_ClockWise ? 90 -angle : angle + 90;
                break;
            case Image.Origin360.Right:
                angle = m_ClockWise ? -angle : angle;
                break;
            case Image.Origin360.Bottom:
                angle = m_ClockWise ? 270 - angle : angle - 90;
                break;
        }
        angle = Mathf.Deg2Rad * angle;
        pos.y = Mathf.Sin(angle) * m_Radius;
        pos.x = Mathf.Cos(angle) * m_Radius;
        return pos;
    }

    

    private void Set(float input , bool sendCallback = true)
    {
        float newValue = ClampValue(input);
        if (m_Value == newValue)
            return;

        m_Value = newValue;
        UpdateVisuals();
        if (sendCallback)
        {
            if (m_OnValueChanged != null)
            {
                m_OnValueChanged.Invoke(newValue);
            }
        }
    }

    float ClampValue(float input)
    {
        float newValue = Mathf.Clamp(input, m_MinValue, m_MaxValue);
        if (wholeNumbers)
            newValue = Mathf.Round(newValue);
        return newValue;
    }

    public void OnValueChange(Action<float> call)
    {
        m_OnValueChanged = call;
    }

    public void OnDragEnd(Action call)
    {
        m_OnDragEnd = call;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        m_dragHandler = false;
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        if (m_HandleRect != null)
        {
            
            if (RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, eventData.position,
                eventData.enterEventCamera))
            {
                m_dragHandler = true;
            }
        }
    }

    public void TweenValue(float val , float time , Action call = null)
    {
        if(m_TweenCor != null)
        {
            StopCoroutine(m_TweenCor);
        }
        m_TweenCor = StartCoroutine(_tweenValue(val, time));
        m_OnTweenComplete = call;
    }

    public void StopTween()
    {
        if (m_TweenCor != null)
        {
            StopCoroutine(m_TweenCor);
        }

    }

    IEnumerator _tweenValue(float val , float time)
    {
        float t = 0;
        float oldValue = value;
        float interval = val - oldValue;

        while (t < time)
        {
            t += Time.deltaTime;
            if(t > time)
            {
                t = time;
            }
            value = oldValue + (t / time) * interval;
            
            yield return 0;
        }

        if (m_OnTweenComplete != null)
        {
            m_OnTweenComplete();
        }
        value = val;
    }

    
}

Editor

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UI;
using UnityEngine;

[CustomEditor(typeof(CircleSlider) , true)]
public class CircleSliderEditor : SelectableEditor
{
    private SerializedProperty m_Value;
    private SerializedProperty m_MaxValue;
    private SerializedProperty m_MinValue;
    private SerializedProperty m_FillRect;
    private SerializedProperty m_HandleRect;
    private SerializedProperty m_FillOrigin;
    private SerializedProperty m_Radius;
    private SerializedProperty m_ClockWise;
    private SerializedProperty m_WholeNumbers;
    private SerializedProperty m_angle;


    protected override void OnEnable()
    {
        base.OnEnable();
        
        m_FillRect = serializedObject.FindProperty("m_FillRect");
        m_HandleRect = serializedObject.FindProperty("m_HandleRect");
        m_MinValue = serializedObject.FindProperty("m_MinValue");
        m_MaxValue = serializedObject.FindProperty("m_MaxValue");
        m_Value = serializedObject.FindProperty("m_Value");
        m_FillOrigin = serializedObject.FindProperty("m_FillOrigin");
        m_Radius = serializedObject.FindProperty("m_Radius");
        m_ClockWise = serializedObject.FindProperty("m_ClockWise");
        m_WholeNumbers = serializedObject.FindProperty("m_WholeNumbers");
        m_angle = serializedObject.FindProperty("m_Angle");
        
    }

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        serializedObject.Update();

        EditorGUILayout.PropertyField(m_FillRect);
        EditorGUILayout.PropertyField(m_HandleRect);

        
        if (m_FillRect.objectReferenceValue != null )
        {
            EditorGUILayout.PropertyField(m_MinValue);
            EditorGUILayout.PropertyField(m_MaxValue);
            EditorGUILayout.PropertyField(m_WholeNumbers);
            EditorGUILayout.PropertyField(m_angle);
            EditorGUILayout.Slider(m_Value, m_MinValue.floatValue, m_MaxValue.floatValue);
            EditorGUILayout.PropertyField(m_FillOrigin);
            EditorGUILayout.PropertyField(m_ClockWise);

        }



        if (m_HandleRect.objectReferenceValue != null)
        {
            EditorGUILayout.PropertyField(m_Radius);
        }

        serializedObject.ApplyModifiedProperties();
    }
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!