问题
设想当前有A,B两个同级UI,按钮A在布局B上面,A有点击效果,B有滑动效果,现在需要在A上进行滑动操作时B滑动,并且A仍然支持点击。
思路
由于ugui默认只有一个UI接受射线并分发事件,导致在A上滑动时,滑动的事件和参数一定会终止于A,我们需要一个机制让事件传递下去,直到我们想要的目标B。
实现
实现原理也比较简单,我们编写一个组件用于传递事件,在触发ugui的事件时,重新发射一次射线,然后把挂载了这个组件的物体和当前的物体过滤掉,就可以找到我们想要的底层UI物体,把相关的事件再发给这个物体就好了
public class EventPassOn : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler,
IInitializePotentialDragHandler, IBeginDragHandler, IDragHandler, IEndDragHandler {
public bool dragging { get; private set; }
private PointerEventData _dragData;
private GameObject _pointerPress;
private GameObject _pointerDrag;
[SerializeField] private bool enable;
public bool Enable {
get => enable;
set {
enable = value;
if (enable == false && dragging) {
DoEndDrag(_dragData);
}
}
}
/// <summary>
/// 关闭面板的时候OnEndDrag不会触发,这里特殊处理一下
/// </summary>
private void OnDisable() {
if (dragging) {
OnEndDrag(_dragData);
}
}
public void OnPointerDown(PointerEventData eventData) {
_pointerPress = null;
_pointerDrag = null;
dragging = false;
if (!Enable)
return;
var raycast = GetFirstRaycastBehind(eventData);
if (raycast.gameObject != null) {
_pointerPress = ExecuteEvents.GetEventHandler<IPointerDownHandler>(raycast.gameObject);
_pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(raycast.gameObject);
}
if (_pointerPress != null) {
ExecuteEvents.Execute(_pointerPress, eventData, ExecuteEvents.pointerDownHandler);
}
}
public void OnPointerUp(PointerEventData eventData) {
if (Enable && _pointerPress != null) {
ExecuteEvents.Execute(_pointerPress, eventData, ExecuteEvents.pointerUpHandler);
}
}
public void OnPointerClick(PointerEventData eventData) {
if (Enable && _pointerPress != null) {
var raycast = GetFirstRaycastBehind(eventData);
if (raycast.gameObject != null) {
var pointerClick = ExecuteEvents.GetEventHandler<IPointerClickHandler>(raycast.gameObject);
if (pointerClick == _pointerPress) {
ExecuteEvents.Execute(pointerClick, eventData, ExecuteEvents.pointerClickHandler);
}
}
}
}
public void OnInitializePotentialDrag(PointerEventData eventData) {
if (Enable && _pointerDrag != null) {
ExecuteEvents.Execute(_pointerDrag, eventData, ExecuteEvents.initializePotentialDrag);
}
}
public void OnBeginDrag(PointerEventData eventData) {
if (!Enable)
return;
if (_pointerDrag != null) {
ExecuteEvents.Execute(_pointerDrag, eventData, ExecuteEvents.beginDragHandler);
if (_pointerDrag != _pointerPress && _pointerPress != null) {
ExecuteEvents.Execute(_pointerPress, eventData, ExecuteEvents.pointerUpHandler);
_pointerPress = null;
}
}
dragging = true;
_dragData = eventData;
}
public void OnDrag(PointerEventData eventData) {
if (!Enable)
return;
if (_pointerDrag != null) {
ExecuteEvents.Execute(_pointerDrag, eventData, ExecuteEvents.dragHandler);
}
_dragData = eventData;
}
public void OnEndDrag(PointerEventData eventData) {
if (!Enable)
return;
DoEndDrag(eventData);
}
private void DoEndDrag(PointerEventData eventData) {
if (_pointerDrag != null) {
ExecuteEvents.Execute(_pointerDrag, eventData, ExecuteEvents.endDragHandler);
}
dragging = false;
}
private RaycastResult GetFirstRaycastBehind(PointerEventData eventData) {
var results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventData, results);
foreach (var result in results) {
if (result.gameObject == gameObject)
continue;
if (result.gameObject.GetComponent<EventPassOn>() != null)
continue;
/* 如果有子节点,这里需要做额外的判断
var go = ExecuteEvents.GetEventHandler<IPointerDownHandler>(result.gameObject);
if (go == gameObject)
continue;
if (go != null && go.GetComponent<EventPassOn>() != null)
continue;
*/
return result;
}
return new RaycastResult();
}
}