系列
UGUI源码分析系列总览
相关前置:
UGUI EventSystem源码分析
UGUI源码分析:Selectable交互组件的基类
文章目录
- 系列
- UML图一览
- Slider
- 属性介绍
- 初始化
- 交互事件
- 点击&拖拽
- 移动
- Set
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主要通过PointerDown、Drag、Move三种事件进行交互上的处理。
点击&拖拽
点击与拖拽的逻辑是相同的,通过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,逐儿时之梦正在游戏制作的技术海洋中漂泊。知道的越多,不知道的也越多。希望我的文章对你有所帮助:)