Unity3D, ОБА 3D и UI Raycaster одновременно реагируют на прикосновения?

В эти дни в Unity вы очень легко обнаруживаете прикосновение к объектам, используя raycaster...

public class DragThreeDee:MonoBehaviour,IPointerDownHandler
       IPointerUpHandler, IBeginDragHandler,
       IDragHandler, IEndDragHandler
       {
       public void OnPointerDown (PointerEventData data)
          { etc

Итак, что касается вашего уровня пользовательского интерфейса, он правильно игнорирует касание уровня пользовательского интерфейса и так далее.

Что если вы хотите, чтобы произошло следующее,

Вы хотите собрать (одновременно, в одном и том же кадре) касание как над трехмерным объектом, так и над элементом пользовательского интерфейса?

(Представьте, что у вас есть, скажем, несколько прозрачных кнопок пользовательского интерфейса на экране; у вас также бегают обычные 3D-роботы. Пользователь нажимает на точку на экране, где есть кнопка пользовательского интерфейса, и "под" роботом. ОБА робот скрипт, как указано выше, и кнопка, должны ответить.)

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

Как это сделать в Unity?

1 ответ

Решение

Выглядит сложно, но можно сделать.

1. Направьте данные из PointerEventData во всех компонентах пользовательского интерфейса вы хотите разблокировать 3D-трансляцию GameObjects.

2. Получить все экземпляр PhysicsRaycaster с FindObjectsOfType<PhysicsRaycaster>(), С точки зрения производительности, имеет смысл кешировать это.

3. Выполните Raycast с PhysicsRaycaster.Raycast который вернет все объекты GameObject с прикрепленным к нему коллайдером.

4. Использование ExecuteEvents.Execute отправить соответствующее событие в результат, сохраненный в RaycastResult,

RaycastForwarder сценарий:

public class RaycastForwarder : MonoBehaviour
{
    List<PhysicsRaycaster> rayCast3D = new List<PhysicsRaycaster>();
    List<RaycastResult> rayCast3DResult = new List<RaycastResult>();

    private static RaycastForwarder localInstance;
    public static RaycastForwarder Instance { get { return localInstance; } }


    private void Awake()
    {
        if (localInstance != null && localInstance != this)
        {
            Destroy(this.gameObject);
        }
        else
        {
            localInstance = this;
        }
    }

    public void notifyPointerDown(PointerEventData eventData)
    {
        findColliders(eventData, PointerEventType.Down);
    }

    public void notifyPointerUp(PointerEventData eventData)
    {
        findColliders(eventData, PointerEventType.Up);
    }

    public void notifyPointerDrag(PointerEventData eventData)
    {
        findColliders(eventData, PointerEventType.Drag);
    }

    private void findColliders(PointerEventData eventData, PointerEventType evType)
    {
        UpdateRaycaster();

        //Loop Through All Normal Collider(3D/Mesh Renderer) and throw Raycast to each one
        for (int i = 0; i < rayCast3D.Count; i++)
        {
            //Send Raycast to all GameObject with 3D Collider
            rayCast3D[i].Raycast(eventData, rayCast3DResult);
            sendRayCast(eventData, evType);
        }
        //Reset Result
        rayCast3DResult.Clear();
    }

    private void sendRayCast(PointerEventData eventData, PointerEventType evType)
    {
        //Loop over the RaycastResult and simulate the pointer event
        for (int i = 0; i < rayCast3DResult.Count; i++)
        {
            GameObject target = rayCast3DResult[i].gameObject;
            PointerEventData evData = createEventData(rayCast3DResult[i]);

            if (evType == PointerEventType.Drag)
            {
                ExecuteEvents.Execute<IDragHandler>(target,
                                        evData,
                                        ExecuteEvents.dragHandler);
            }

            if (evType == PointerEventType.Down)
            {
                ExecuteEvents.Execute<IPointerDownHandler>(target,
                               evData,
                               ExecuteEvents.pointerDownHandler);
            }

            if (evType == PointerEventType.Up)
            {
                ExecuteEvents.Execute<IPointerUpHandler>(target,
                    evData,
                    ExecuteEvents.pointerUpHandler);
            }
        }
    }

    private PointerEventData createEventData(RaycastResult rayResult)
    {
        PointerEventData evData = new PointerEventData(EventSystem.current);
        evData.pointerCurrentRaycast = rayResult;
        return evData;
    }

    //Get all PhysicsRaycaster in the scene
    private void UpdateRaycaster()
    {
        convertToList(FindObjectsOfType<PhysicsRaycaster>(), rayCast3D);
    }

    private 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]);
        }
    }

    public enum PointerEventType
    {
        Drag, Down, Up
    }
}

RayCastRouter сценарий:

public class RayCastRouter : MonoBehaviour, IPointerDownHandler,
       IPointerUpHandler,
       IDragHandler
{
    public void OnDrag(PointerEventData eventData)
    {
        RaycastForwarder.Instance.notifyPointerDrag(eventData);
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        RaycastForwarder.Instance.notifyPointerDown(eventData);
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        RaycastForwarder.Instance.notifyPointerUp(eventData);
    }
}

Использование:

A.Attach RaycastForwarder в пустой GameObject.

Б. Прикрепление RayCastRouter к любому компоненту пользовательского интерфейса, например Image, который вы не хотите блокировать 3D GameObject. Вот и все. Любой компонент пользовательского интерфейса с RayCastRouter прикрепленный к нему сможет позволить трехмерному объекту GameObject позади него получать raycast.

Теперь событие будет отправлено в 3D-объект, который имеет скрипт, который реализует функции из IPointerDownHandler, IPointerUpHandler а также IDragHandler интерфейс.

Не забудьте прикрепить Physics Raycaster на камеру.

Другие вопросы по тегам