Después de tanto luchar hemos dado con la solución de este inconveniente que tantos lectores de este sitio presentan. Si tienes alguna información que aportar no dudes en aportar tu información.
En primer lugar, debe comprender que solo existen 3
formas de detectar clic en un objeto con el OnPointerDown
función:
1.Necesita un componente de interfaz de usuario para detectar el clic con el OnPointerDown
función. Esto se aplica a otros eventos de IU similares.
2.Otro método para detectar un clic con el OnPointerDown
función en un GameObject 2D / Sprite es adjuntar Physics2DRaycaster
a la cámara y luego OnPointerDown
se llamará cuando se haga clic en él. Tenga en cuenta que un Colisionador 2D debe estar unido a él.
3.Si se trata de un objeto 3D con un colisionador, no Colisionador 2D, debes tener PhysicsRaycaster
conectado a la cámara para que el OnPointerDown
función a llamar.
Hacer esto con el primer método parece más razonable en lugar de tener un colisionador grande o un colisionador 2D cubriendo la pantalla. Todo lo que haces es crear un Canvas
, Panel GameObject y adjuntar Image
componente que se extiende por toda la pantalla.
Amigo, simplemente no veo el uso de .UI como una solución seria: imagina que estamos haciendo un gran juego y estás liderando un equipo que está haciendo toda la interfaz de usuario (me refiero a botones, menús y todo). Dirijo un equipo de robots andantes. De repente te digo “oh, por cierto, no puedo manejar el tacto (“! “), ¿Podrías colocar una interfaz de usuario? Panel, no olvides guardarlo debajo de todo lo que estás haciendo, oh y poner uno en cualquiera / todos los lienzos o cámaras entre los que intercambias, y pásame esa información, ¡de acuerdo! ” 🙂 Quiero decir que es una tontería. Básicamente, uno no puede decir: “oh, Unity no maneja el tacto”.
No es tan difícil como lo describiste. Puede escribir un código largo que podrá crear un Canvas
, Panel
y un Image
. Cambiar la imagen alfa a 0
. Todo lo que tienes que hacer es adjuntar ese código a la cámara o un GameObject vacío y realizará todo esto automáticamente en el modo de juego.
Haga que todos los GameObject que quieran recibir eventos en la pantalla se suscriban, luego use ExecuteEvents.Execute
para enviar el evento a todas las interfaces en el script adjunto a ese GameObject.
Por ejemplo, el código de muestra a continuación enviará OnPointerDown
evento al GameObject llamado target.
ExecuteEvents.Execute(target,
eventData,
ExecuteEvents.pointerDownHandler);
Problema con el que te encontrarás:
Lo oculto Image
El componente bloqueará otra interfaz de usuario o GameObject para que no reciba raycast. Este es el mayor problema aquí.
Solución:
Dado que causará algunos problemas de bloqueo, es mejor hacer que el Lienzo de la imagen esté encima de todo. Esto asegurará que ahora esté 100 bloqueando todas las demás UI / GameObject. Canvas.sortingOrder = 12;
debería ayudarnos a hacer esto.
Cada vez que detectamos un evento como OnPointerDown
de la imagen, lo haremos a mano enviar reenviar el OnPointerDown
evento a todos los demás UI / GameObjects debajo del Image
.
En primer lugar, lanzamos un raycast con GraphicRaycaster
(Interfaz de usuario), Physics2DRaycaster
(Colisionador 2D), PhysicsRaycaster
(3D Collider) y almacene el resultado en un List
.
Ahora, recorremos el resultado en la Lista y reenviamos el evento que recibimos enviando un evento artificial a los resultados con:
ExecuteEvents.Execute(currentListLoop,
eventData,
ExecuteEvents.pointerDownHandler);
Otros problemas con los que te encontrarás:
No podrá enviar eventos de emulación en el Toggle
componente con GraphicRaycaster
. Este es un error en Unity. Me tomó 2 días darme cuenta de esto.
Tampoco se pudo enviar un evento de movimiento deslizante falso al Slider
componente. No puedo decir si esto es un error o no.
Aparte de estos problemas mencionados anteriormente, pude implementar esto. Viene en 3 partes. Simplemente cree una carpeta y coloque todos los scripts en ella.
GUIONES:
1.WholeScreenPointer.cs
– La parte principal del guión que crea Canvas
, GameObject y oculto Image
. Hace todas las cosas complicadas para asegurarse de que el Image
siempre cubre la pantalla. También envía eventos a todos los GameObject suscritos.
public class WholeScreenPointer : MonoBehaviour
//////////////////////////////// SINGLETON BEGIN ////////////////////////////////
private static WholeScreenPointer localInstance;
public static WholeScreenPointer Instance get return localInstance;
public EventUnBlocker eventRouter;
private void Awake()
if (localInstance != null && localInstance != this)
Destroy(this.gameObject);
else
localInstance = this;
//////////////////////////////// SINGLETON END ////////////////////////////////
//////////////////////////////// SETTINGS BEGIN ////////////////////////////////
public bool simulateUIEvent = true;
public bool simulateColliderEvent = true;
public bool simulateCollider2DEvent = true;
public bool hideWholeScreenInTheEditor = false;
//////////////////////////////// SETTINGS END ////////////////////////////////
private GameObject hiddenCanvas;
private List registeredGameobjects = new List();
//////////////////////////////// USEFUL FUNCTIONS BEGIN ////////////////////////////////
public void registerGameObject(GameObject objToRegister)
if (!isRegistered(objToRegister))
registeredGameobjects.Add(objToRegister);
public void unRegisterGameObject(GameObject objToRegister)
if (isRegistered(objToRegister))
registeredGameobjects.Remove(objToRegister);
public bool isRegistered(GameObject objToRegister)
return registeredGameobjects.Contains(objToRegister);
public void enablewholeScreenPointer(bool enable)
hiddenCanvas.SetActive(enable);
//////////////////////////////// USEFUL FUNCTIONS END ////////////////////////////////
// Use this for initialization
private void Start()
makeAndConfigWholeScreenPinter(hideWholeScreenInTheEditor);
private void makeAndConfigWholeScreenPinter(bool hide = true)
//Create and Add Canvas Component
createCanvas(hide);
//Add Rect Transform Component
//addRectTransform();
//Add Canvas Scaler Component
addCanvasScaler();
//Add Graphics Raycaster Component
addGraphicsRaycaster();
//Create Hidden Panel GameObject
GameObject panel = createHiddenPanel(hide);
//Make the Image to be positioned in the middle of the screen then fix its anchor
stretchImageAndConfigAnchor(panel);
//Add EventForwarder script
addEventForwarder(panel);
//Add EventUnBlocker
addEventRouter(panel);
//Add EventSystem and Input Module
addEventSystemAndInputModule();
//Creates Hidden GameObject and attaches Canvas component to it
private void createCanvas(bool hide)
//Create Canvas GameObject
hiddenCanvas = new GameObject("___HiddenCanvas");
if (hide)
hiddenCanvas.hideFlags = HideFlags.HideAndDontSave;
//Create and Add Canvas Component
Canvas cnvs = hiddenCanvas.AddComponent
2.EventForwarder.cs
– Simplemente recibe cualquier evento de lo oculto Image
y se lo pasa al WholeScreenPointer.cs
script para su procesamiento.
public class EventForwarder : MonoBehaviour, IDragHandler, IPointerDownHandler, IPointerUpHandler
WholeScreenPointer wcp = null;
void Start()
wcp = WholeScreenPointer.Instance;
public void OnDrag(PointerEventData eventData)
wcp.forwardDragEvent(eventData);
public void OnPointerDown(PointerEventData eventData)
wcp.forwardPointerDownEvent(eventData);
public void OnPointerUp(PointerEventData eventData)
wcp.forwardPointerUpEvent(eventData);
3.EventUnBlocker.cs
– Desbloquea los rayos ocultos Image
está bloqueando enviando un evento falso a cualquier Objeto por encima de él. Ya sea interfaz de usuario, colisionador 2D o 3D.
public class EventUnBlocker : MonoBehaviour
List grRayCast = new List(); //UI
List phy2dRayCast = new List(); //Collider 2D (Sprite Renderer)
List phyRayCast = new List(); //Normal Collider(3D/Mesh Renderer)
List resultList = new List();
//For Detecting button click and sending fake Button Click to UI Buttons
Dictionary pointerIdToGameObject = new Dictionary();
// Use this for initialization
void Start()
public void sendArtificialUIEvent(Component grRayCast, PointerEventData eventData, PointerEventType evType)
//Route to all Object in the RaycastResult
for (int i = 0; i < resultList.Count; i++)
/*Do something if it is NOT this GameObject.
We don't want any other detection on this GameObject
*/
if (resultList[i].gameObject != this.gameObject)
//Check if this is UI
if (grRayCast is GraphicRaycaster)
//Debug.Log("UI");
routeEvent(resultList[i], eventData, evType, true);
//Check if this is Collider 2D/SpriteRenderer
if (grRayCast is Physics2DRaycaster)
//Debug.Log("Collider 2D/SpriteRenderer");
routeEvent(resultList[i], eventData, evType, false);
//Check if this is Collider/MeshRender
if (grRayCast is PhysicsRaycaster)
//Debug.Log("Collider 3D/Mesh");
routeEvent(resultList[i], eventData, evType, false);
//Creates fake PointerEventData that will be used to make PointerEventData for the callback functions
PointerEventData createEventData(RaycastResult rayResult)
PointerEventData fakeEventData = new PointerEventData(EventSystem.current);
fakeEventData.pointerCurrentRaycast = rayResult;
return fakeEventData;
private void routeEvent(RaycastResult rayResult, PointerEventData eventData, PointerEventType evType, bool isUI = false)
bool foundKeyAndValue = false;
GameObject target = rayResult.gameObject;
//Make fake GameObject target
PointerEventData fakeEventData = createEventData(rayResult);
switch (evType)
case PointerEventType.Drag:
//Send/Simulate Fake OnDrag event
ExecuteEvents.Execute(target, fakeEventData,
ExecuteEvents.dragHandler);
break;
case PointerEventType.Down:
//Send/Simulate Fake OnPointerDown event
ExecuteEvents.Execute(target,
fakeEventData,
ExecuteEvents.pointerDownHandler);
//Code Below is for UI. break out of case if this is not UI
if (!isUI)
break;
//Prepare Button Click. Should be sent in the if PointerEventType.Up statement
Button buttonFound = target.GetComponent
Uso:
1.Adjunta WholeScreenPointer
script a un GameObject vacío o la cámara.
2.Para recibir cualquier evento en la escena, simplemente implemente IDragHandler
, IPointerDownHandler
, IPointerUpHandler
en cualquier script, luego llame WholeScreenPointer.Instance.registerGameObject(this.gameObject);
una vez. Cualquier evento de la pantalla ahora se enviará a ese script. No olvide darse de baja en el OnDisable()
función.
Por ejemplo, adjunte Test
a cualquier GameObject que desee recibir eventos táctiles:
public class Test : MonoBehaviour, IDragHandler, IPointerDownHandler, IPointerUpHandler
void Start()
//Register this GameObject so that it will receive events from WholeScreenPointer script
WholeScreenPointer.Instance.registerGameObject(this.gameObject);
public void OnDrag(PointerEventData eventData)
Debug.Log("Dragging: ");
public void OnPointerDown(PointerEventData eventData)
Debug.Log("Pointer Down: ");
public void OnPointerUp(PointerEventData eventData)
Debug.Log("Pointer Up: ");
void OnDisable()
WholeScreenPointer.Instance.unRegisterGameObject(this.gameObject);
NOTA:
Solo necesitas llamar WholeScreenPointer.Instance.registerGameObject(this.gameObject);
si desea recibir el evento en cualquier lugar de la pantalla. Si solo desea recibir un evento del objeto actual, no tiene que llamar a esto. Si lo hace, recibirá varios eventos.
Otras funciones importantes:
Habilitar evento de pantalla completa - WholeScreenPointer.Instance.enablewholeScreenPointer(true);
Deshabilitar el evento de pantalla completa - WholeScreenPointer.Instance.enablewholeScreenPointer(false);
Finalmente, esto se puede mejorar más.
La pregunta y la respuesta que voy a publicar parecen basarse en opiniones. Sin embargo, voy a responder lo mejor que pueda.
Si está intentando detectar eventos de puntero en la pantalla, no hay nada de malo en representar la pantalla con un objeto. En su caso, utiliza un colisionador 3D para cubrir todo el tronco de la cámara. Sin embargo, hay una forma nativa de hacer esto en Unity, utilizando un objeto de interfaz de usuario 2D que cubre toda la pantalla. La pantalla se puede representar mejor con un objeto 2D. Para mí, esta parece una forma natural de hacerlo.
Utilizo un código genérico para este propósito:
public class Screen : MonoSingleton, IPointerClickHandler, IDragHandler, IBeginDragHandler, IEndDragHandler, IPointerDownHandler, IPointerUpHandler, IScrollHandler
private bool holding = false;
private PointerEventData lastPointerEventData;
#region Events
public delegate void PointerEventHandler(PointerEventData data);
static public event PointerEventHandler OnPointerClick = delegate ;
static public event PointerEventHandler OnPointerDown = delegate ;
/// Dont use delta data as it will be wrong. If you are going to use delta, use OnDrag instead.
static public event PointerEventHandler OnPointerHold = delegate ;
static public event PointerEventHandler OnPointerUp = delegate ;
static public event PointerEventHandler OnBeginDrag = delegate ;
static public event PointerEventHandler OnDrag = delegate ;
static public event PointerEventHandler OnEndDrag = delegate ;
static public event PointerEventHandler OnScroll = delegate ;
#endregion
#region Interface Implementations
void IPointerClickHandler.OnPointerClick(PointerEventData e)
lastPointerEventData = e;
OnPointerClick(e);
// And other interface implementations, you get the point
#endregion
void Update()
if (holding)
OnPointerHold(lastPointerEventData);
los Screen
es un singleton, porque solo hay una pantalla en el contexto del juego. Los objetos (como la cámara) se suscriben a sus eventos de puntero y se organizan en consecuencia. Esto también mantiene intacta la responsabilidad única.
Usaría esto como anexarlo a un objeto que representa el llamado vidrio (superficie de la pantalla). Si cree que los botones de la interfaz de usuario están saliendo de la pantalla, el vidrio estaría debajo de ellos. Para ello, el vaso tiene que ser el primer hijo del Canvas
. Por supuesto, el Canvas
tiene que ser renderizado en el espacio de la pantalla para que tenga sentido.
Un truco aquí, que no tiene sentido es agregar un invisible Image
componente al vidrio, por lo que recibiría eventos. Esto actúa como el objetivo raycast del vidrio.
También podrías usar Input
(Input.touches
etc.) para implementar este objeto de vidrio. Funcionaría como comprobar si la entrada cambia en cada Update
llama. Esto me parece un enfoque basado en encuestas, mientras que el anterior es un enfoque basado en eventos.
Su pregunta parece buscar una forma de justificar el uso de Input
clase. En mi humilde opinión, no te lo pongas más difícil. Usa lo que funciona. Y acepta el hecho de que Unity no es perfecto.
Si estás de acuerdo, puedes dejar una crónica acerca de qué le añadirías a este enunciado.