如题
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();
}
}
来源:CSDN
作者:thrt520asd
链接:https://blog.csdn.net/thrt520asd/article/details/103736941