c# - Использование подхода Unity3D IPointerDownHandler, но с «целым экраном»




touch (2)

Прежде всего, вы должны понимать, что есть только 3 способа обнаружить щелчок на объекте с OnPointerDown функции OnPointerDown :

1. Вам нужен компонент пользовательского интерфейса, чтобы обнаружить щелчок с OnPointerDown функции OnPointerDown . Это относится и к другим подобным событиям пользовательского интерфейса.

2. Другой метод обнаружения щелчка с OnPointerDown функции OnPointerDown в 2D / Sprite GameObject заключается в том, чтобы прикрепить Physics2DRaycaster к камере, а затем при OnPointerDown будет вызван OnPointerDown . Обратите внимание, что 2D коллайдер должен быть прикреплен к нему.

3. Если это 3D-объект с коллайдером, а не с 2D-коллайдером , то для OnPointerDown функции OnPointerDown к камере необходимо 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 . Это bug в 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); Наконец, это может быть улучшено больше.

В Unity говорят, что вам нужно обнаружить прикосновение пальца (рисунок пальцем) к чему-либо в сцене.

Единственный способ сделать это в современном Unity, очень прост :

Шаг 1 Поместите коллайдер на этот объект. («Земля» или что бы то ни было.) 1

Шаг 2. На вашей камере на панели «Инспектор» щелкните, чтобы добавить Physics Raycaster (2D или 3D в зависимости от ситуации).

Шаг 3. Просто используйте код, как в примере A ниже.

(Совет - не забудьте убедиться, что есть EventSystem ... иногда Unity добавляет ее автоматически, иногда нет!)

Фантастика, не может быть проще. Наконец, Unity правильно обрабатывает un / распространяется через уровень пользовательского интерфейса. Работает равномерно и безупречно на рабочем столе, устройствах, редакторе и т. Д. И т. Д. Ура Unity.

Все хорошо. Но что, если вы хотите рисовать просто «на экране» ?

Таким образом, вы хотите, просто, проводите / касания / рисования от пользователя "на экране". (Например, просто для управления камерой на орбите, скажем.) Так же, как и в любой обычной 3D-игре, где камера бегает и движется.

Вы не хотите положение пальца на каком-либо объекте в мировом пространстве, вы просто хотите абстрактные «движения пальца» (то есть положение на стекле).

Какой коллайдер вы потом используете? Вы можете сделать это без коллайдера? Кажется глупым добавлять коллайдер только по этой причине.

Что мы делаем, это :

Я просто делаю плоский коллайдер и прикрепляю его к камере . Так что он просто сидит в усеченной камере и полностью закрывает экран.

(Для кода тогда нет необходимости использовать 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».


Вопрос и ответ, который я собираюсь опубликовать, в значительной степени основаны на мнении. Тем не менее, я собираюсь ответить как можно лучше.

Если вы пытаетесь обнаружить события указателя на экране, нет ничего плохого в том, чтобы представить экран объектом. В вашем случае вы используете 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 . ИМХО, не усложняй себе. Используйте то, что работает. И принять тот факт, что Единство не является идеальным.





touch