Используя подход Unity3D IPointerDownHandler, но с "всем экраном",
В Unity говорят, что вам нужно обнаружить прикосновение пальца (рисунок пальцем) к чему-либо в сцене.
Единственный способ сделать это в современном Unity, очень прост:
Шаг 1 Поместите коллайдер на этот объект. ("Земля" или что бы то ни было.) 1
Шаг 2. На вашей камере на панели "Инспектор" щелкните, чтобы добавить Physics Raycaster (2D или 3D в зависимости от ситуации).
Шаг 3. Просто используйте код, как в примере A ниже.
(Совет - не забудьте убедиться в наличии EventSystem... иногда Unity добавляет ее автоматически, иногда нет!)
Фантастика, не может быть проще. Наконец, Unity правильно обрабатывает un/распространяется через уровень пользовательского интерфейса. Работает равномерно и безупречно на рабочем столе, устройствах, редакторе и т.д. И т.д. Ура Unity.
Все хорошо. Но что, если вы хотите рисовать просто "на экране"?
Таким образом, вы хотите, просто, проводите/касания/рисования от пользователя "на экране". (Например, просто для управления камерой на орбите, скажем.) Так же, как и в любой обычной 3D-игре, где камера бегает и движется.
Вы не хотите положение пальца на каком-либо объекте в мировом пространстве, вы просто хотите абстрактные "движения пальца" (то есть положение на стекле).
Какой коллайдер вы потом используете? Вы можете сделать это без коллайдера? Кажется глупым добавлять коллайдер только по этой причине.
Что мы делаем, это:
Я просто делаю плоский коллайдер и прикрепляю его к камере. Так что он просто сидит в усеченной камере и полностью закрывает экран.
![enter image description here]()
(Для кода тогда нет необходимости использовать ScreenToWorldPoint, поэтому просто используйте код, как в примере B - чрезвычайно просто, работает отлично.)
Мой вопрос, кажется немного странным, что я использую описанную мной "камеру под камерой", просто чтобы прикоснуться к стеклу.
Какое дело здесь?
(Примечание. Пожалуйста, не отвечайте на вопрос о древней системе Unity "Прикосновения", которая сегодня непригодна для реальных проектов, вы не можете игнорировать .UI, используя устаревший подход.)
Пример кода А - рисование на объекте сцены. Используйте 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...");
}
Пример кода B... вас интересует только то, что пользователь делает на стеклянном экране устройства. Еще проще здесь:
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 Если вы новичок в Unity: на этом шаге, скорее всего, сделайте слой с именем "Draw"; в настройках физики заставить Ничья взаимодействовать ни с чем; на втором шаге с Raycaster просто установите слой на "Draw".
Ответы
Ответ 1
Вопрос и ответ, который я собираюсь опубликовать, выглядят в значительной степени на основе мнения. Тем не менее я собираюсь ответить как можно лучше.
Если вы пытаетесь обнаружить события указателя на экране, нет ничего плохого в представлении экрана с объектом. В вашем случае вы используете 3D-коллайдер, чтобы покрыть всю усечку камеры. Тем не менее, есть собственный способ сделать это в Unity, используя 2D-объект UI, который охватывает весь экран. Экран может быть лучше всего представлен 2D-объектом. Для меня это кажется естественным способом сделать это.
Я использую общий код для этой цели:
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);
}
}
}
Screen
- одиночный, потому что в контексте игры есть только один экран. Объекты (например, камера) подписываются на события указателя и организуют их соответственно. Это также сохраняет целостность одной ответственности.
Вы использовали бы это как дополнение к объекту, который представляет собой так называемое стекло (поверхность экрана). Если вы думаете, что кнопки в пользовательском интерфейсе выходят из экрана, стекло будет под ними. Для этого стекло должно быть первым дочерним элементом Canvas
. Конечно, Canvas
должен отображаться в пространстве экрана, чтобы это имело смысл.
Один взломанный здесь, что не имеет смысла - добавить невидимый компонент Image
к стеклу, чтобы он получал события. Это действует как расистская мишень из стекла.
![Инспектор]()
Вы также можете использовать Input
(Input.touches
и т.д.) для реализации этого стеклянного объекта. Он будет работать, проверяя, изменился ли вход в каждом вызове Update
. Это похоже на подход, основанный на опросах, в то время как вышеупомянутый подход основан на событиях.
Ваш вопрос выглядит так, как будто он ищет способ оправдать использование класса Input
. ИМХО, Не делай это сложнее для себя. Используйте то, что работает. И соглашайтесь с тем, что Единство не идеально.
Ответ 2
Прежде всего, вы должны понимать, что есть только 3
способа обнаружить щелчок на объекте с OnPointerDown
функции OnPointerDown
:
1. Вам нужен компонент пользовательского интерфейса, чтобы обнаружить щелчок с OnPointerDown
функции OnPointerDown
. Это относится и к другим подобным событиям пользовательского интерфейса.
2. Другой метод обнаружения щелчка с OnPointerDown
функции OnPointerDown
в 2D/Sprite GameObject заключается в том, чтобы прикрепить Physics2DRaycaster
к камере, а затем при OnPointerDown
будет вызван OnPointerDown
. Обратите внимание, что 2D коллайдер должен быть прикреплен к нему.
3.Ел это 3D объект с коллайдером не 2D коллайдером, вы должны быть PhysicsRaycaster
прикреплен к камере для того, чтобы OnPointerDown
функции, которая будет вызван.
Делать это первым способом кажется более разумным, чем иметь большой коллайдер или 2D коллайдер, покрывающий экран. Все, что вам нужно сделать, - это создать Canvas
, Panel GameObject и прикрепить к нему компонент Image
, растянувшийся на весь экран.
Чувак, я просто не вижу использования .UI в качестве серьезного решения: представьте, что мы делаем большую игру, а вы возглавляете команду, которая выполняет весь пользовательский интерфейс (я имею в виду кнопки, меню и все). Я возглавляю команду, выполняющую ходячих роботов. Я неожиданно говорю вам: "О, кстати, я не могу справиться с прикосновением ("! "), Не могли бы вы зайти в интерфейс. Панель, не забудьте оставить его под всем, что вы делаете, о, и положить один на любом/всех холстах или камерах, между которыми ты меняешься - и передай мне эту информацию, ОК! " :) Я имею в виду это просто глупо. По сути, нельзя сказать: "О, Unity не справляется с прикосновением"
Не очень сложно, как ты это описал. Вы можете написать длинный код, который сможет создать Canvas
, Panel
и Image
. Измените изображение альфа на 0
. Все, что вам нужно сделать, это прикрепить этот код к камере или пустому GameObject, и он автоматически выполнит все это в режиме игры.
Сделайте так, чтобы каждый GameObject, который хочет получить событие на экране, подписался на него, затем используйте ExecuteEvents.Execute
чтобы отправить событие всем интерфейсам в сценарии, прикрепленном к этому GameObject.
Например, приведенный ниже пример кода отправит событие OnPointerDown
в GameObject с именем target.
ExecuteEvents.Execute<IPointerDownHandler>(target,
eventData,
ExecuteEvents.pointerDownHandler);
Проблема, с которой вы столкнетесь:
Компонент скрытого Image
заблокирует другой пользовательский интерфейс или GameObject от получения raycast. Это самая большая проблема здесь.
Решение:
Так как это вызовет некоторые проблемы с блокировкой, лучше сделать так, чтобы холст изображения был поверх всего. Это обеспечит 100 блокировку всех других UI/GameObject. Canvas.sortingOrder = 12;
должен помочь нам сделать это.
Каждый раз, когда мы обнаруживаем событие, такое как OnPointerDown
из изображения, мы вручную отправляем OnPointerDown
события OnPointerDown
всем остальным OnPointerDown
пользовательского интерфейса /GameObject под Image
.
Прежде всего, мы генерируем raycast с помощью GraphicRaycaster
(UI), Physics2DRaycaster
(2D коллайдер), PhysicsRaycaster
(3D Collider) и сохраняем результат в List
.
Теперь мы перебираем результат в Списке и повторно отправляем полученное событие, отправляя искусственное событие в результаты с:
ExecuteEvents.Execute<IPointerDownHandler>(currentListLoop,
eventData,
ExecuteEvents.pointerDownHandler);
Другие проблемы, с которыми вы столкнетесь:
Вы не сможете отправлять события эмуляции на компонент Toggle
с помощью GraphicRaycaster
. Это ошибка в Unity. Мне потребовалось 2 дня, чтобы понять это.
Также не удалось отправить ложное событие перемещения ползунка в компонент Slider
. Я не могу сказать, если это ошибка или нет.
Помимо этих проблем, упомянутых выше, я смог реализовать это. Он состоит из 3 частей. Просто создайте папку и поместите в нее все скрипты.
СЦЕНАРИИ:
1 WholeScreenPointer.cs
- основная часть скрипта, которая создает Canvas
, GameObject и скрытое Image
. Это делает все сложные вещи, чтобы убедиться, что Image
всегда покрывает экран. Он также отправляет событие всем подписавшимся 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
- он просто получает любое событие из скрытого Image
и передает его в сценарий WholeScreenPointer.cs
для обработки.
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
- разблокирует лучи, которые скрывает скрытое Image
, отправив поддельное событие любому объекту над ним. Будь то пользовательский интерфейс, 2D или 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
}
}
Использование:
1. Прикрепите скрипт WholeScreenPointer
к пустому GameObject или камере.
2. Чтобы получить любое событие в сцене, просто IDragHandler
, IPointerDownHandler
, IPointerUpHandler
в любом скрипте, затем вызовите WholeScreenPointer.Instance.registerGameObject(this.gameObject);
один раз. Любое событие с экрана теперь будет отправлено в этот скрипт. Не забудьте OnDisable()
регистрацию в функции OnDisable()
.
Например, прикрепите Test
к любому GameObject, для которого вы хотите получать сенсорные события:
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);
}
}
ПРИМЕЧАНИЕ:
Вам нужно только вызвать WholeScreenPointer.Instance.registerGameObject(this.gameObject);
если вы хотите получить событие в любом месте на экране. Если вы просто хотите получить событие от текущего объекта, вам не нужно вызывать это. Если вы это сделаете, вы получите несколько событий.
Другие важные функции:
Включить событие WholeScreen - WholeScreenPointer.Instance.enablewholeScreenPointer(true);
Отключить событие WholeScreen - WholeScreenPointer.Instance.enablewholeScreenPointer(false);
Наконец, это может быть улучшено больше.