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
на камеру.