问题
I tried to implement IPointerEnter, IPointerExit, IPointerDown and IPointerUp.
All of them were working fine until they start overlapping.
My question is how do I make IPointer... to work on their respective collider?
A and B are BoxCollider2D, B is inside of A. Both of them have a script that has above IPointer... implemented. Below are the issues that I am having with this condition.
- Upon clicking mouse down inside
B, only 1 box will trigger theIPointerDown. (This is solved by this link) How to detect multiple/Overlapping GameObjects with the EventSystem? - If my pointer (mouse) is inside of
Aand I move the pointer (mouse) toB,Awill get triggered (IPointerExit). What I try to achieve isAIPointerExitshould be triggered on it's respective collider.
If I enter A then 'IPointerEnter' A should be triggered and if enter B, IPointerEnter B should then be triggered.
回答1:
I manage to create a script for above issue, It works for simple overlapping. which means any BoxCollider2D that need to be inside another box must be the most top order layer. Just extend OverlapEventTrigger.
using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using System.Linq;
namespace TFTM.Event
{
public enum EventExecuteType
{
PointerEnter,
PointerExit,
PointerDown,
PointerUp,
PointerDrag,
}
public abstract class OverlapEventTrigger : EventTrigger, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler, IDragHandler
{
public virtual void MouseDown(PointerEventData eventData){}
public virtual void MouseUp(PointerEventData eventData){}
public virtual void MouseEnter(PointerEventData eventData){}
public virtual void MouseExit(PointerEventData eventData){}
public virtual void MouseDrag(PointerEventData eventData){}
List<GameObject> ObjectsInCollider = new List<GameObject>();
public List<RaycastResult> lastTotalRaycastResult = new List<RaycastResult>();
public override void OnPointerDown(PointerEventData eventData)
{
Debug.Log("Down: " + eventData.pointerCurrentRaycast.gameObject.name);
MouseDown(eventData);
rethrowRaycast(eventData, eventData.pointerCurrentRaycast.gameObject, EventExecuteType.PointerDown);
}
public override void OnPointerUp(PointerEventData eventData)
{
Debug.Log("Up: " + eventData.pointerCurrentRaycast.gameObject.name);
MouseUp(eventData);
rethrowRaycast(eventData, eventData.pointerCurrentRaycast.gameObject, EventExecuteType.PointerUp);
}
public override void OnPointerEnter(PointerEventData eventData)
{
if (IsPointerInsideCollider(eventData) && ObjectsInCollider.Contains(gameObject))
return;
Debug.Log("Enter: " + eventData.pointerCurrentRaycast.gameObject.name);
MouseEnter(eventData);
ObjectsInCollider.Add(gameObject);
}
public override void OnPointerExit(PointerEventData eventData)
{
//Debug.Log("Is " + gameObject.name + " inside his respective collider : " + IsPointerInsideCollider(eventData));
if (IsPointerInsideCollider(eventData))
return;
Debug.Log("Exit: " + gameObject.name);
MouseExit(eventData);
ObjectsInCollider.Remove(gameObject);
}
public override void OnDrag(PointerEventData eventData)
{
//Debug.Log("Drag: " + eventData.pointerCurrentRaycast.gameObject.name);
MouseDrag(eventData);
rethrowRaycast(eventData, eventData.pointerCurrentRaycast.gameObject, EventExecuteType.PointerDrag);
}
bool IsPointerInsideCollider(PointerEventData eventData)
{
PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
pointerEventData.position = eventData.position;
List<RaycastResult> raycastResult = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointerEventData, raycastResult);
for (int i = 0; i < raycastResult.Count; i++)
{
if (raycastResult[i].gameObject == gameObject)
{
return true;
}
}
return false;
}
void rethrowRaycast(PointerEventData eventData, GameObject excludeGameObject, EventExecuteType eventType)
{
PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
pointerEventData.position = eventData.pressPosition;
//pointerEventData.position = eventData
//Where to store Raycast Result
List<RaycastResult> raycastResult = new List<RaycastResult>();
//Rethrow the raycast to include everything regardless of their Z position
EventSystem.current.RaycastAll(pointerEventData, raycastResult);
//Debug.Log("Other GameObject hit");
for (int i = 0; i < raycastResult.Count; i++)
{
//Debug.Log(raycastResult[i].gameObject.name);
//Don't Rethrow Raycayst for the first GameObject that is hit
if (excludeGameObject != null && raycastResult[i].gameObject != excludeGameObject || eventType == EventExecuteType.PointerDrag)
{
//Re-simulate OnPointerDown on every Object hit
simulateCallbackFunction(raycastResult[i].gameObject, eventType);
}
}
}
//This causes functions such as OnPointerDown to be called again
void simulateCallbackFunction(GameObject target, EventExecuteType eventType)
{
PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
//pointerEventData.ra
RaycastResult res = new RaycastResult();
res.gameObject = target;
pointerEventData.pointerCurrentRaycast = res;
pointerEventData.position = Input.mousePosition;
switch (eventType) {
case EventExecuteType.PointerDown:
ExecuteEvents.Execute(target, pointerEventData, ExecuteEvents.pointerDownHandler);
break;
case EventExecuteType.PointerUp:
ExecuteEvents.Execute(target, pointerEventData, ExecuteEvents.pointerUpHandler);
break;
case EventExecuteType.PointerDrag:
ExecuteEvents.Execute(target, pointerEventData, ExecuteEvents.dragHandler);
break;
default:
break;
}
}
}
来源:https://stackoverflow.com/questions/46274674/how-to-make-ipointer-work-on-respective-box-collider