c# - unity - ¿Cómo detectar GamePoints múltiples/superpuestos con EventSystem?
unity programming language (2)
Lo que trato de lograr: el cepillo de dientes debe aparecer donde el usuario haga clic dentro de BoxCollider
A
, incluido el espacio dentro de BoxCollider
B
Pero al parecer, hacer clic dentro de B
no mostrará el cepillo de dientes (OnPointerDown no se activa).
Lo que he intentado: cambiar el orden de las capas.
El cepillo de dientes se muestra después de que el usuario hace clic dentro del colisionador A
caja, pero si el usuario hace clic dentro del colisionador B
caja, el cepillo de dientes no se mostrará, lo que significa que OnPointerDown
no se activará.
Creo que es debido a la superposición de un BoxCollider2D
dentro de otro BoxCollider2D
. En mi caso B
dentro de A
, supongo que es el culpable, pero no tengo idea de cómo resolverlo o si tal vez hay otro método para implementar OnPointerDown
.
Estoy usando la cámara Perspective
. pero en esta escena, todos los elementos están en la misma z position
que es 0. ¿Es posible activar el evento IPointerHander en cada BoxCollider2D
respectivo?
DragableObject.cs
Este script está adjunto al cepillo de dientes. El BoxCollider2D
A también pertenece al cepillo de dientes.
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log("pointer down");
if (GetComponent<DragableObject>() == null)
return;
currentObject = GetComponent<DragableObject>();
MeshRenderer renderer = GetComponent<MeshRenderer>();
if (ShowOnTouch)
ShowObject();
// Store original state
originalPosition = transform.position;
originalOrderLayer = renderer.sortingOrder;
// Snap to mouse
Vector3 newPos = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 30));
newPos.z = 30;
transform.position = newPos;
if (BringToFront)
{
if (renderer != null)
{
renderer.sortingOrder = 90;
}
}
ObjectActive.Invoke();
}
TargetListener.cs
Este script está adjunto a BoxCollider2D
B.
public void OnPointerDown(PointerEventData eventData)
{
for (int i = 0; i < Affectors.Count; i++)
{
if (Affectors [i] == DragableObject.currentObject)
{
DragableObject.currentObject.OnEnterTarget(transform);
ITriggerEffect[] iTrigger = GetComponents<ITriggerEffect>();
for (int j = 0; j < iTrigger.Length; j++)
{
Debug.Log("iTrigger enter");
Debug.Log(iTrigger [j]);
iTrigger [j].Execute(eventData, PointerState.Down);
}
}
else
continue;
}
}
Si hago clic en A
se mostrará el cepillo de dientes, excepto cuando hago clic dentro de B
Aquí el registro de depuración.
Este es el BoxCollider2D
A al que se adjunta, que es el *Toothbrush
junto con el script dragable.cs
.
ACTUALIZACIÓN: Gracias a otros que responden, el problema se vuelve más claro para mí. A continuación se muestra BoxCollider2D
A y BoxCollider2D
B. Ambos tienen script que tienen la mayor parte de OnPointerHander
. ¿Cómo me aseguro de que todo OnPointerHandler
se OnPointerHandler
en BoxCollider2D
respectivo?
Problemas que estoy teniendo:
- OnPointerExit en
A
se activa cuando mi puntero ingresaB
- si hace clic dentro de
B
,OnPointerDown
solo se activa enB
pero no enA
¿Cuál es la funcionalidad prevista aquí? ¿Sospecho que quieres que el cepillo de dientes se muestre cada vez que un usuario hace clic dentro del espacio de la boca?
Si es así, una manera divertida de solucionar esto sería ajustar el orden de los objetos ''B'' para que estén por encima del objeto ''A'' en la Jerarquía, de modo que se superpongan al área de colisión A. Esto a su vez permitirá que su interacción bloquee la interacción del área definida debajo del área de colisión definida por cada colisionador ''B''.
Lo que estamos haciendo con esta metodología es forzar el evento OnPointerDown para que ocurra con el colisionador ''B'' antes de que tenga la oportunidad de colisionar con el colisionador ''A''.
Avíseme si tiene sentido y siéntase libre de hacer preguntas de seguimiento sobre el método.
Todavía no estoy seguro si su caso es lo que creo que es, pero aquí hay un video del método que mencioné: Video de demostración
Una de las ventajas y ventajas de EventSystem es que los eventos no pasan por GameObjects. El primero que es golpeado es devuelto. Aunque, parece que no quieres eso. Es complicado hacer que EventSystem devuelva múltiples GameObjects,
Hay dos soluciones para ti:
1. Obtenga ride de EventSystem
( OnPointerDown
y IPointerDownHandler
) y use el sistema raycast de la vieja escuela.
Physics2D.RaycastAll
y Physics2D.RaycastNonAlloc
pueden hacer esto. Este ejemplo usará RaycastNonAlloc
por razones de rendimiento. Es muy fácil.
Adjuntar a solo un GameObject (GameObject vacío) :
public class HitAll : MonoBehaviour
{
//Detect up to 100 Objects
const int raycastAmount = 100;
RaycastHit2D[] result = new RaycastHit2D[raycastAmount];
void Update()
{
#if UNITY_IOS || UNITY_ANDROID
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
checkRaycast(Input.GetTouch(0).position);
}
#else
if (Input.GetMouseButtonDown(0))
{
checkRaycast(Input.mousePosition);
}
#endif
}
void checkRaycast(Vector2 mousePos)
{
Vector3 origin = Camera.main.ScreenToWorldPoint(mousePos);
int hitCount = Physics2D.RaycastNonAlloc(origin, Vector2.zero, result, 200);
Debug.Log(hitCount);
for (int i = 0; i < hitCount; i++)
{
Debug.Log("Hit: " + result[i].collider.gameObject.name);
}
}
}
2. EventSystem
usando EventSystem
pero EventSystem
lanzar el evento.
En primer lugar, lanza raycast con EventSystem.current.RaycastAll
luego invoca manualmente el evento con ExecuteEvents.Execute
.
Adjunte a GameObject con 2D Collider y asegúrese de que Physics2DRaycaster
esté conectado a la cámara :
public class ThroughEventScript : MonoBehaviour, IPointerDownHandler
{
public void OnPointerDown(PointerEventData eventData)
{
rethrowRaycast(eventData, eventData.pointerCurrentRaycast.gameObject);
//DO STUFF WITH THE OBJECT HIT BELOW
Debug.Log("Hit: " + eventData.pointerCurrentRaycast.gameObject.name);
}
void rethrowRaycast(PointerEventData eventData, GameObject excludeGameObject)
{
PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
pointerEventData.position = eventData.pressPosition;
//pointerEventData.position = eventData.position;}
//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)
{
//Re-simulate OnPointerDown on every Object hit
simulateCallbackFunction(raycastResult[i].gameObject);
}
}
}
//This causes functions such as OnPointerDown to be called again
void simulateCallbackFunction(GameObject target)
{
PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
//pointerEventData.ra
RaycastResult res = new RaycastResult();
res.gameObject = target;
pointerEventData.pointerCurrentRaycast = res;
ExecuteEvents.Execute(target, pointerEventData, ExecuteEvents.pointerDownHandler);
}
}