c# - game - Usando el enfoque IPointerDownHandler de Unity3D, pero con "toda la pantalla"
unity touch count (2)
En Unity, diga que necesita detectar el toque con los dedos (dibujo con los dedos) en algo de la escena.
La única manera de hacer esto en la Unidad moderna, es muy simple :
Paso 1 Pon un colisionador en ese objeto. ("El suelo" o lo que sea.) 1
Paso 2. En su cámara, panel Inspector, haga clic para agregar un Physics Raycaster (2D o 3D, según corresponda).
Paso 3. Simplemente use el código como en el Ejemplo A a continuación.
(Sugerencia: no olvide asegurarse de que haya un sistema de eventos ... ¡a veces Unity agrega uno automáticamente, a veces no!)
Fantástico, no podría ser más fácil. Unity finalmente maneja la propagación / propagación correctamente a través de la capa UI. Funciona de manera uniforme y sin fallas en el escritorio, los dispositivos, el editor, etc.
Todo bien. Pero, ¿y si quieres dibujar solo "en la pantalla" ?
Por lo tanto, usted está queriendo, simplemente, deslizar / tocar / dibujar del usuario "en la pantalla". (Ejemplo, simplemente para operar una cámara orbital, por ejemplo). Así como en cualquier juego 3D común donde la cámara funciona y se mueve.
No desea la posición del dedo sobre algún objeto en el espacio del mundo, simplemente quiere "movimientos de dedo" abstractos (es decir, posición sobre el cristal).
¿Qué colisionador usas entonces? ¿Puedes hacerlo sin colisionador? Parece fatuo agregar un colisionador solo por esa razón.
Lo que hacemos es esto :
Acabo de hacer un colisionador plano de algún tipo, y en realidad lo coloco debajo de la cámara . Así que simplemente se sienta en el frustum de la cámara y cubre completamente la pantalla.
(Para el código, no hay necesidad de usar ScreenToWorldPoint, así que solo use el código como en el Ejemplo B: extremadamente simple, funciona perfectamente).
Mi pregunta, parece un poco extraño tener que usar el "más pequeño debajo de la cámara" que describo, solo para tocar el cristal.
¿Cuál es el problema aquí?
(Nota: no responda con el antiguo sistema de "toques" de Unity, que hoy en día no se puede utilizar para proyectos reales, no puede ignorar .UI utiliza el enfoque heredado)
Ejemplo de código A: dibujo en un objeto de escena. Utilice ScreenToWorldPoint.
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
public class FingerMove:MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
{
public void OnPointerDown (PointerEventData data)
{
Debug.Log("FINGER DOWN");
prevPointWorldSpace =
theCam.ScreenToWorldPoint( data.position );
}
public void OnDrag (PointerEventData data)
{
thisPointWorldSpace =
theCam.ScreenToWorldPoint( data.position );
realWorldTravel =
thisPointWorldSpace - prevPointWorldSpace;
_processRealWorldtravel();
prevPointWorldSpace = thisPointWorldSpace;
}
public void OnPointerUp (PointerEventData data)
{
Debug.Log("clear finger...");
}
Ejemplo de código B ... solo le importa lo que el usuario hace en la pantalla de cristal del dispositivo. Aún más fácil aquí:
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
public class FingerMove:MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
{
private Vector2 prevPoint;
private Vector2 newPoint;
private Vector2 screenTravel;
public void OnPointerDown (PointerEventData data)
{
Debug.Log("FINGER DOWN");
prevPoint = data.position;
}
public void OnDrag (PointerEventData data)
{
newPoint = data.position;
screenTravel = newPoint - prevPoint;
prevPoint = newPoint;
_processSwipe();
}
public void OnPointerUp (PointerEventData data)
{
Debug.Log("FINEGR UP...");
}
private void _processSwipe()
{
// your code here
Debug.Log("screenTravel left-right.. " + screenTravel.x.ToString("f2"));
}
}
1 Si solo eres nuevo en Unity: en ese paso es muy probable que sea una capa llamada "Draw"; en la configuración física, haga que "Draw" interactúe con nada; en el paso dos con el Raycaster solo establece la capa en "Dibujar".
En primer lugar, debe comprender que solo hay 3
formas de detectar el clic en un objeto con la función OnPointerDown
:
1. Necesita un componente de UI para detectar el clic con la función OnPointerDown
. Esto se aplica a otros eventos de interfaz de usuario similares.
2. Otro método para detectar un clic con la función OnPointerDown
en un OnPointerDown
2D / Sprite es adjuntar Physics2DRaycaster
a la cámara y luego se OnPointerDown
a OnPointerDown
cuando se haga clic en él. Tenga en cuenta que se debe adjuntar un Colisionador 2D .
3. Si este es un Objeto 3D con un Colisionador no Colisionador 2D , debe tener PhysicsRaycaster
conectado a la cámara para que se pueda llamar a la función OnPointerDown
.
Hacer esto con el primer método parece más razonable en lugar de tener un colisionador grande o un colisionador 2D que cubra la pantalla. Todo lo que debe hacer es crear un Canvas
, Panel GameObject y adjuntar un componente de Image
que se extienda por toda la pantalla.
Amigo, simplemente no veo usar .UI como una solución seria: imagina que estamos haciendo un gran juego y lideras un equipo que está haciendo toda la IU (me refiero a botones, menús y todo). Estoy liderando un equipo haciendo los robots andantes. De repente te digo "oh, por cierto, no puedo manejar el tacto ("! "), Podrías dejar caer una interfaz de usuario. Panel, no olvides mantenerlo debajo de todo lo que estás haciendo, oh y ponlo una en cualquier / todos los lienzos o cámaras con las que intercambies, y devuélveme esa información, ¡bien! " :) Quiero decir que es simplemente tonto. Esencialmente no se puede decir: "oh, la unidad no maneja el tacto"
No es tan difícil como lo describiste. Puede escribir un código largo que podrá crear un Canvas
, un Panel
y una Image
. Cambia la imagen alfa a 0
. Todo lo que tienes que hacer es adjuntar ese código a la cámara o a un GameObject vacío y todo esto se realizará automáticamente en el modo de reproducción.
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á el evento OnPointerDown
al GameObject llamado target.
ExecuteEvents.Execute<IPointerDownHandler>(target,
eventData,
ExecuteEvents.pointerDownHandler);
Problema que se encontrará con :
El componente de Image
oculto bloqueará a otra UI o GameObject para que no reciba Raycast. Este es el mayor problema aquí.
Solución :
Ya que causará algunos problemas de bloqueo, es mejor hacer que el Lienzo de la imagen esté encima de todo. Esto asegurará que ahora haya 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
desde la imagen, enviaremos manualmente reenviar el evento OnPointerDown
a todos los demás UI / GameObjects debajo de la Image
.
En primer lugar, lanzamos un raycast con GraphicRaycaster
(UI), Physics2DRaycaster
(2D PhysicsRaycaster
), PhysicsRaycaster
(3D Collider) y almacenamos el resultado en una List
.
Ahora, repasamos el resultado en la Lista y reenviamos el evento que recibimos enviando un evento artificial a los resultados con:
ExecuteEvents.Execute<IPointerDownHandler>(currentListLoop,
eventData,
ExecuteEvents.pointerDownHandler);
Otros problemas con los que se encontrará :
No podrá enviar eventos de emulación en el componente Toggle
con GraphicRaycaster
. Este es un bug en la unidad. Me tomó 2 días darme cuenta de esto.
Tampoco fue capaz de enviar un evento de movimiento deslizante falso al componente Slider
. No puedo decir si esto es un error o no.
Aparte de estos problemas mencionados anteriormente, pude implementar esto. Se presenta en 3 partes. Solo crea una carpeta y coloca todos los scripts en ellos.
ESCRITOS :
1 . WholeScreenPointer.cs
: la parte principal del script que crea Canvas
, GameObject y una Image
oculta. Hace todo lo complicado para asegurarse de que la Image
siempre cubra la pantalla. También envía eventos a todos los suscritos a GameObject.
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<GameObject> registeredGameobjects = new List<GameObject>();
//////////////////////////////// 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<Canvas>();
cnvs.renderMode = RenderMode.ScreenSpaceOverlay;
cnvs.pixelPerfect = false;
//Set Cavas sorting order to be above other Canvas sorting order
cnvs.sortingOrder = 12;
cnvs.targetDisplay = 0;
//Make it child of the GameObject this script is attached to
hiddenCanvas.transform.SetParent(gameObject.transform);
}
private void addRectTransform()
{
RectTransform rctrfm = hiddenCanvas.AddComponent<RectTransform>();
}
//Adds CanvasScaler component to the Canvas GameObject
private void addCanvasScaler()
{
CanvasScaler cvsl = hiddenCanvas.AddComponent<CanvasScaler>();
cvsl.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
cvsl.referenceResolution = new Vector2(800f, 600f);
cvsl.matchWidthOrHeight = 0.5f;
cvsl.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
cvsl.referencePixelsPerUnit = 100f;
}
//Adds GraphicRaycaster component to the Canvas GameObject
private void addGraphicsRaycaster()
{
GraphicRaycaster grcter = hiddenCanvas.AddComponent<GraphicRaycaster>();
grcter.ignoreReversedGraphics = true;
grcter.blockingObjects = GraphicRaycaster.BlockingObjects.None;
}
//Creates Hidden Panel and attaches Image component to it
private GameObject createHiddenPanel(bool hide)
{
//Create Hidden Panel GameObject
GameObject hiddenPanel = new GameObject("___HiddenPanel");
if (hide)
{
hiddenPanel.hideFlags = HideFlags.HideAndDontSave;
}
//Add Image Component to the hidden panel
Image pnlImg = hiddenPanel.AddComponent<Image>();
pnlImg.sprite = null;
pnlImg.color = new Color(1, 1, 1, 0); //Invisible
pnlImg.material = null;
pnlImg.raycastTarget = true;
//Make it child of HiddenCanvas GameObject
hiddenPanel.transform.SetParent(hiddenCanvas.transform);
return hiddenPanel;
}
//Set Canvas width and height,to matach screen width and height then set anchor points to the corner of canvas.
private void stretchImageAndConfigAnchor(GameObject panel)
{
Image pnlImg = panel.GetComponent<Image>();
//Reset postion to middle of the screen
pnlImg.rectTransform.anchoredPosition3D = new Vector3(0, 0, 0);
//Stretch the Image so that the whole screen is totally covered
pnlImg.rectTransform.anchorMin = new Vector2(0, 0);
pnlImg.rectTransform.anchorMax = new Vector2(1, 1);
pnlImg.rectTransform.pivot = new Vector2(0.5f, 0.5f);
}
//Adds EventForwarder script to the Hidden Panel GameObject
private void addEventForwarder(GameObject panel)
{
EventForwarder evnfwdr = panel.AddComponent<EventForwarder>();
}
//Adds EventUnBlocker script to the Hidden Panel GameObject
private void addEventRouter(GameObject panel)
{
EventUnBlocker evtrtr = panel.AddComponent<EventUnBlocker>();
eventRouter = evtrtr;
}
//Add EventSystem
private void addEventSystemAndInputModule()
{
//Check if EventSystem exist. If it does not create and add it
EventSystem eventSys = FindObjectOfType<EventSystem>();
if (eventSys == null)
{
GameObject evObj = new GameObject("EventSystem");
EventSystem evs = evObj.AddComponent<EventSystem>();
evs.firstSelectedGameObject = null;
evs.sendNavigationEvents = true;
evs.pixelDragThreshold = 5;
eventSys = evs;
}
//Check if StandaloneInputModule exist. If it does not create and add it
StandaloneInputModule sdlIpModl = FindObjectOfType<StandaloneInputModule>();
if (sdlIpModl == null)
{
sdlIpModl = eventSys.gameObject.AddComponent<StandaloneInputModule>();
sdlIpModl.horizontalAxis = "Horizontal";
sdlIpModl.verticalAxis = "Vertical";
sdlIpModl.submitButton = "Submit";
sdlIpModl.cancelButton = "Cancel";
sdlIpModl.inputActionsPerSecond = 10f;
sdlIpModl.repeatDelay = 0.5f;
sdlIpModl.forceModuleActive = false;
}
}
/*
Forwards Handler Event to every GameObject that implements IDragHandler, IPointerDownHandler, IPointerUpHandler interface
*/
public void forwardDragEvent(PointerEventData eventData)
{
//Route and send the event to UI and Colliders
for (int i = 0; i < registeredGameobjects.Count; i++)
{
ExecuteEvents.Execute<IDragHandler>(registeredGameobjects[i],
eventData,
ExecuteEvents.dragHandler);
}
//Route and send the event to UI and Colliders
eventRouter.routeDragEvent(eventData);
}
public void forwardPointerDownEvent(PointerEventData eventData)
{
//Send the event to all subscribed scripts
for (int i = 0; i < registeredGameobjects.Count; i++)
{
ExecuteEvents.Execute<IPointerDownHandler>(registeredGameobjects[i],
eventData,
ExecuteEvents.pointerDownHandler);
}
//Route and send the event to UI and Colliders
eventRouter.routePointerDownEvent(eventData);
}
public void forwardPointerUpEvent(PointerEventData eventData)
{
//Send the event to all subscribed scripts
for (int i = 0; i < registeredGameobjects.Count; i++)
{
ExecuteEvents.Execute<IPointerUpHandler>(registeredGameobjects[i],
eventData,
ExecuteEvents.pointerUpHandler);
}
//Route and send the event to UI and Colliders
eventRouter.routePointerUpEvent(eventData);
}
}
2 . EventForwarder.cs
: simplemente recibe cualquier evento de la Image
oculta y lo pasa al script WholeScreenPointer.cs
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
: EventUnBlocker.cs
los rayos que bloquea la Image
oculta al enviar un evento falso a cualquier objeto que se encuentre por encima de él. Ya sea UI, 2D o colisionador 3D.
public class EventUnBlocker : MonoBehaviour
{
List<GraphicRaycaster> grRayCast = new List<GraphicRaycaster>(); //UI
List<Physics2DRaycaster> phy2dRayCast = new List<Physics2DRaycaster>(); //Collider 2D (Sprite Renderer)
List<PhysicsRaycaster> phyRayCast = new List<PhysicsRaycaster>(); //Normal Collider(3D/Mesh Renderer)
List<RaycastResult> resultList = new List<RaycastResult>();
//For Detecting button click and sending fake Button Click to UI Buttons
Dictionary<int, GameObject> pointerIdToGameObject = new Dictionary<int, GameObject>();
// 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<IDragHandler>(target, fakeEventData,
ExecuteEvents.dragHandler);
break;
case PointerEventType.Down:
//Send/Simulate Fake OnPointerDown event
ExecuteEvents.Execute<IPointerDownHandler>(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<Button>();
//If pointerId is not in the dictionary add it
if (buttonFound != null)
{
if (!dictContains(eventData.pointerId))
{
dictAdd(eventData.pointerId, target);
}
}
//Bug in Unity with GraphicRaycaster and Toggle. Have to use a hack below
//Toggle Toggle component
Toggle toggle = null;
if ((target.name == "Checkmark" || target.name == "Label") && toggle == null)
{
toggle = target.GetComponentInParent<Toggle>();
}
if (toggle != null)
{
//Debug.LogWarning("Toggled!: " + target.name);
toggle.isOn = !toggle.isOn;
//Destroy(toggle.gameObject);
}
break;
case PointerEventType.Up:
//Send/Simulate Fake OnPointerUp event
ExecuteEvents.Execute<IPointerUpHandler>(target,
fakeEventData,
ExecuteEvents.pointerUpHandler);
//Code Below is for UI. break out of case if this is not UI
if (!isUI)
{
break;
}
//Send Fake Button Click if requirement is met
Button buttonPress = target.GetComponent<Button>();
/*If pointerId is in the dictionary, check
*/
if (buttonPress != null)
{
if (dictContains(eventData.pointerId))
{
//Check if GameObject matches too. If so then this is a valid Click
for (int i = 0; i < resultList.Count; i++)
{
GameObject tempButton = resultList[i].gameObject;
if (tempButton != this.gameObject && dictContains(eventData.pointerId, tempButton))
{
foundKeyAndValue = true;
//Debug.Log("Button ID and GameObject Match! Sending Click Event");
//Send/Simulate Fake Click event to the Button
ExecuteEvents.Execute<IPointerClickHandler>(tempButton,
new PointerEventData(EventSystem.current),
ExecuteEvents.pointerClickHandler);
}
}
}
}
break;
}
//Remove pointerId since it exist
if (foundKeyAndValue)
{
dictRemove(eventData.pointerId);
}
}
void routeOption(PointerEventData eventData, PointerEventType evType)
{
UpdateRaycaster();
if (WholeScreenPointer.Instance.simulateUIEvent)
{
//Loop Through All GraphicRaycaster(UI) and throw Raycast to each one
for (int i = 0; i < grRayCast.Count; i++)
{
//Throw Raycast to all UI elements in the position(eventData)
grRayCast[i].Raycast(eventData, resultList);
sendArtificialUIEvent(grRayCast[i], eventData, evType);
}
//Reset Result
resultList.Clear();
}
if (WholeScreenPointer.Instance.simulateCollider2DEvent)
{
//Loop Through All Collider 2D (Sprite Renderer) and throw Raycast to each one
for (int i = 0; i < phy2dRayCast.Count; i++)
{
//Throw Raycast to all UI elements in the position(eventData)
phy2dRayCast[i].Raycast(eventData, resultList);
sendArtificialUIEvent(phy2dRayCast[i], eventData, evType);
}
//Reset Result
resultList.Clear();
}
if (WholeScreenPointer.Instance.simulateColliderEvent)
{
//Loop Through All Normal Collider(3D/Mesh Renderer) and throw Raycast to each one
for (int i = 0; i < phyRayCast.Count; i++)
{
//Throw Raycast to all UI elements in the position(eventData)
phyRayCast[i].Raycast(eventData, resultList);
sendArtificialUIEvent(phyRayCast[i], eventData, evType);
}
//Reset Result
resultList.Clear();
}
}
public void routeDragEvent(PointerEventData eventData)
{
routeOption(eventData, PointerEventType.Drag);
}
public void routePointerDownEvent(PointerEventData eventData)
{
routeOption(eventData, PointerEventType.Down);
}
public void routePointerUpEvent(PointerEventData eventData)
{
routeOption(eventData, PointerEventType.Up);
}
public void UpdateRaycaster()
{
convertToList(FindObjectsOfType<GraphicRaycaster>(), grRayCast);
convertToList(FindObjectsOfType<Physics2DRaycaster>(), phy2dRayCast);
convertToList(FindObjectsOfType<PhysicsRaycaster>(), phyRayCast);
}
//To avoid ToList() function
void convertToList(GraphicRaycaster[] fromComponent, List<GraphicRaycaster> toComponent)
{
//Clear and copy new Data
toComponent.Clear();
for (int i = 0; i < fromComponent.Length; i++)
{
toComponent.Add(fromComponent[i]);
}
}
//To avoid ToList() function
void convertToList(Physics2DRaycaster[] fromComponent, List<Physics2DRaycaster> toComponent)
{
//Clear and copy new Data
toComponent.Clear();
for (int i = 0; i < fromComponent.Length; i++)
{
toComponent.Add(fromComponent[i]);
}
}
//To avoid ToList() function
void convertToList(PhysicsRaycaster[] fromComponent, List<PhysicsRaycaster> toComponent)
{
//Clear and copy new Data
toComponent.Clear();
for (int i = 0; i < fromComponent.Length; i++)
{
toComponent.Add(fromComponent[i]);
}
}
//Checks if object is in the dictionary
private bool dictContains(GameObject obj)
{
return pointerIdToGameObject.ContainsValue(obj);
}
//Checks if int is in the dictionary
private bool dictContains(int pointerId)
{
return pointerIdToGameObject.ContainsKey(pointerId);
}
//Checks if int and object is in the dictionary
private bool dictContains(int pointerId, GameObject obj)
{
return (pointerIdToGameObject.ContainsKey(pointerId) && pointerIdToGameObject.ContainsValue(obj));
}
//Adds pointerId and its value to dictionary
private void dictAdd(int pointerId, GameObject obj)
{
pointerIdToGameObject.Add(pointerId, obj);
}
//Removes pointerId and its value from dictionary
private void dictRemove(int pointerId)
{
pointerIdToGameObject.Remove(pointerId);
}
public enum PointerEventType
{
Drag, Down, Up
}
}
Uso
1. Conecte la WholeScreenPointer
comandos WholeScreenPointer
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 y luego llame a WholeScreenPointer.Instance.registerGameObject(this.gameObject);
una vez. Cualquier evento de la pantalla se enviará a ese script. No olvide OnDisable()
el registro en la función OnDisable()
.
Por ejemplo, adjunte Test
a cualquier GameObject que quiera 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 necesita llamar a WholeScreenPointer.Instance.registerGameObject(this.gameObject);
Si desea recibir eventos en cualquier lugar de la pantalla. Si solo desea recibir un evento del Objeto actual, entonces no tiene que llamar a esto. Si lo haces, recibirás múltiples eventos.
Otras funciones importantes:
Habilitar el evento WholeScreen - WholeScreenPointer.Instance.enablewholeScreenPointer(true);
Deshabilitar el evento WholeScreen - WholeScreenPointer.Instance.enablewholeScreenPointer(false);
Finalmente, esto se puede mejorar más.
La pregunta y la respuesta que voy a publicar parecen bastante basadas en la opinión. 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 frustum de la cámara. Sin embargo, hay una forma nativa de hacer esto en Unity, utilizando un objeto de UI 2D que cubre toda la pantalla. La pantalla se puede representar mejor mediante un objeto 2D. Para mí, esto parece una forma natural de hacerlo.
Yo uso un código genérico para este propósito:
public class Screen : MonoSingleton<Screen>, 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 { };
/// <summary> Dont use delta data as it will be wrong. If you are going to use delta, use OnDrag instead. </summary>
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);
}
}
}
La 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 ordenan en consecuencia. Esto también mantiene intacta la única responsabilidad.
Lo usaría para agregarlo a un objeto que representa el llamado vidrio (superficie de la pantalla). Si crees que los botones de la interfaz de usuario están saliendo de la pantalla, el cristal estaría debajo de ellos. Para ello, el cristal tiene que ser el primer hijo del Canvas
. Por supuesto, el Canvas
debe representarse en el espacio de la pantalla para que tenga sentido.
Un truco aquí, que no tiene sentido es agregar un componente de Image
invisible al vidrio, para que reciba eventos. Esto actúa como el objetivo de raycast del vidrio.
También puede usar Input
( Input.touches
etc.) para implementar este objeto de vidrio. Funcionaría como verificación si la entrada cambiara en cada llamada de Update
. Esto me parece un enfoque basado en encuestas, mientras que el anterior es un enfoque basado en eventos.
Su pregunta parece estar buscando una forma de justificar el uso de la clase de Input
. En mi humilde opinión, no te lo pongas difícil. Usa lo que funciona. Y acepta el hecho de que la Unidad no es perfecta.