UGUI源码分析:Slider滑动条组件

系列

UGUI源码分析系列总览
相关前置:
UGUI EventSystem源码分析
UGUI源码分析:Selectable交互组件的基类


UML图一览

在这里插入图片描述

Slider

BaseClass: Selectable

Interface: IInitializePotentialDragHandler,IDragHandler,ICanvasElement

Intro: UGUI中滑动条组件

  • initializePotentialDrag:提前告知可能触发拖拽的接口,这个接口只有在存在IDragHandler接口时才会触发,当点击或触碰时便触发了(会发生在BeginDrag之前
  • IDragHandler:拖拽过程事件监听
  • ICanvasElement :Canvas元素(重建接口),当Canvas发生更新时重建(void Rebuild)

Slider,是UGUI中滑动条组件。通过操作滑块控制一个值在minValue ~ maxValue变化,并提供了一个事件接口onValueChanged来监听值的变化。

属性介绍

在这里插入图片描述

  • Interactable:是否可被交互(false时无法通过EventSystem进行交互)
  • Transition:状态变化过渡模式(相关详情
  • Navigation:导航(相关详情)
  • Fill Rect :填充物体的RectTransform
  • Handle Rect :滑块物体的RectTransform
  • Direction :滑动方式(左到右、右到左、上到下、下到上)
  • Min Value :最小值
  • Max Value :最大值
  • Whole Numbers :整数开关(只运行整数变化)
  • Value :当前值
  • On Value Changed :value变化的事件监听

初始化

Enable阶段:只做了初始值的Set操作,以及显示上的刷新

protected override void OnEnable()
{
    base.OnEnable();
    UpdateCachedReferences();
    Set(m_Value, false);
    //更新显示
    UpdateVisuals();
}

Disable阶段:无特殊处理


交互事件

Slider主要通过PointerDownDragMove三种事件进行交互上的处理。

点击&拖拽

点击与拖拽的逻辑是相同的,通过UpdateDrag方法来计算出当前位置与整体区域的系数,通过该系数更新value值的变化。

public override void OnPointerDown(PointerEventData eventData)
{
    //若发生拖拽则不需要点击单独处理
    if (!MayDrag(eventData))
        return;
    base.OnPointerDown(eventData);
    m_Offset = Vector2.zero;
    if (m_HandleContainerRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, eventData.position, eventData.enterEventCamera))
    {
        //计算触点在Slider中的本地坐标
        Vector2 localMousePos;
        if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.position, eventData.pressEventCamera, out localMousePos))
            m_Offset = localMousePos;
    }
    else
    {
        // 超出Slider范围时 交给拖拽时的逻辑处理外部点
        UpdateDrag(eventData, eventData.pressEventCamera);
    }
}
public virtual void OnDrag(PointerEventData eventData)
{
    if (!MayDrag(eventData))
        return;
    //更新拖拽
    UpdateDrag(eventData, eventData.pressEventCamera);
}

计算插值

void UpdateDrag(PointerEventData eventData, Camera cam)
{
    RectTransform clickRect = m_HandleContainerRect ?? m_FillContainerRect;
    if (clickRect != null && clickRect.rect.size[(int)axis] > 0)
    {
        Vector2 localCursor;
        if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out localCursor))
            return;
        localCursor -= clickRect.rect.position;
		//计算区域大小来确定插值数
        float val = Mathf.Clamp01((localCursor - m_Offset)[(int)axis] / clickRect.rect.size[(int)axis]);
        normalizedValue = (reverseValue ? 1f - val : val);
    }
}

在normalizedValue的Set方法中通过插值的方式算出当前value的值

this.value = Mathf.Lerp(minValue, maxValue, value);

移动

在OnMove事件中,是直接通过Move的数据进行value值的计算。

每次移动的变化由stepSize值决定:float stepSize { get { return wholeNumbers ? 1 : (maxValue - minValue) * 0.1f; } }

public override void OnMove(AxisEventData eventData)
{
    if (!IsActive() || !IsInteractable())
    {
        base.OnMove(eventData);
        return;
    }
	//通过方向判断值的加减
    switch (eventData.moveDir)
    {
        case MoveDirection.Left:
            if (axis == Axis.Horizontal && FindSelectableOnLeft() == null)
                Set(reverseValue ? value + stepSize : value - stepSize);
            else
                base.OnMove(eventData);
            break;
        case MoveDirection.Right:
            if (axis == Axis.Horizontal && FindSelectableOnRight() == null)
                Set(reverseValue ? value - stepSize : value + stepSize);
            else
                base.OnMove(eventData);
            break;
        case MoveDirection.Up:
            if (axis == Axis.Vertical && FindSelectableOnUp() == null)
                Set(reverseValue ? value - stepSize : value + stepSize);
            else
                base.OnMove(eventData);
            break;
        case MoveDirection.Down:
            if (axis == Axis.Vertical && FindSelectableOnDown() == null)
                Set(reverseValue ? value + stepSize : value - stepSize);
            else
                base.OnMove(eventData);
            break;
    }
}

Set

protected virtual void Set(float input, bool sendCallback)
{
    //对输入值进行范围限制,并根据整数开关进行是否四舍五入的变化。
    float newValue = ClampValue(input);
    //值若没发生改变则不做任何处理
    if (m_Value == newValue)
        return;
    m_Value = newValue;
    //更新显示
    UpdateVisuals();
    if (sendCallback)
    {
        UISystemProfilerApi.AddMarker("Slider.value", this);
        //执行value变化的事件
        m_OnValueChanged.Invoke(newValue);
    }
}

.
.
.
.
.


嗨,我是作者Vin129,逐儿时之梦正在游戏制作的技术海洋中漂泊。知道的越多,不知道的也越多。希望我的文章对你有所帮助:)


原创文章 39 获赞 59 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_28820675/article/details/106173162