Unity3D,让 3D 和 UI Raycaster 同时响应触摸?
Unity3D, have BOTH 3D and UI Raycaster simultaneously respond to touch?
最近在 Unity 中,您可以使用光线投射器轻松检测到物体上的触摸...
public class DragThreeDee:MonoBehaviour,IPointerDownHandler
IPointerUpHandler, IBeginDragHandler,
IDragHandler, IEndDragHandler
{
public void OnPointerDown (PointerEventData data)
{ etc
因此,关于您的 UI 层,它正确地忽略了 UI 层上的触摸等等。
如果您希望发生以下情况怎么办,
您想收集(同时,在同一帧中,两者)在 3D 对象和 UI 项目上的触摸?
(假设你在屏幕上有几个透明的 UI 按钮;周围还有普通的 3D 机器人 运行。用户点击屏幕上有 UI 的点=28=] 按钮,和 "underneath",一个机器人。机器人的脚本和按钮都应该响应。)
你会怎么做?我广泛尝试使用单独的相机、单独的 EventSystem
、调整图层、遮挡蒙版等。我找不到办法。
如何在 Unity 中执行此操作?
看起来很复杂,但可以做到。
1。将来自 PointerEventData
的数据路由到您想要解锁 3D GameObjects 光线投射的所有 UI 组件中。
2。用FindObjectsOfType<PhysicsRaycaster>()
获取PhysicsRaycaster
的所有实例。就性能而言,缓存它是有意义的。
3。使用 PhysicsRaycaster.Raycast
执行光线投射,这将 return 所有带有 Collider 的游戏对象。
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。附加 RaycastForwarder
到一个空的游戏对象。
B。将 RayCastRouter
附加到任何 UI 组件,例如您不想阻止 3D 游戏对象的图像。而已。任何附加了 RayCastRouter
的 UI 组件将能够允许其后面的 3D 游戏对象接收光线投射。
一个事件现在将被发送到 3D 对象,该对象具有一个脚本,该脚本实现了来自 IPointerDownHandler
、IPointerUpHandler
和 IDragHandler
接口的功能。
不要忘记将 Physics Raycaster
附加到相机。
最近在 Unity 中,您可以使用光线投射器轻松检测到物体上的触摸...
public class DragThreeDee:MonoBehaviour,IPointerDownHandler
IPointerUpHandler, IBeginDragHandler,
IDragHandler, IEndDragHandler
{
public void OnPointerDown (PointerEventData data)
{ etc
因此,关于您的 UI 层,它正确地忽略了 UI 层上的触摸等等。
如果您希望发生以下情况怎么办,
您想收集(同时,在同一帧中,两者)在 3D 对象和 UI 项目上的触摸?
(假设你在屏幕上有几个透明的 UI 按钮;周围还有普通的 3D 机器人 运行。用户点击屏幕上有 UI 的点=28=] 按钮,和 "underneath",一个机器人。机器人的脚本和按钮都应该响应。)
你会怎么做?我广泛尝试使用单独的相机、单独的 EventSystem
、调整图层、遮挡蒙版等。我找不到办法。
如何在 Unity 中执行此操作?
看起来很复杂,但可以做到。
1。将来自 PointerEventData
的数据路由到您想要解锁 3D GameObjects 光线投射的所有 UI 组件中。
2。用FindObjectsOfType<PhysicsRaycaster>()
获取PhysicsRaycaster
的所有实例。就性能而言,缓存它是有意义的。
3。使用 PhysicsRaycaster.Raycast
执行光线投射,这将 return 所有带有 Collider 的游戏对象。
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。附加 RaycastForwarder
到一个空的游戏对象。
B。将 RayCastRouter
附加到任何 UI 组件,例如您不想阻止 3D 游戏对象的图像。而已。任何附加了 RayCastRouter
的 UI 组件将能够允许其后面的 3D 游戏对象接收光线投射。
一个事件现在将被发送到 3D 对象,该对象具有一个脚本,该脚本实现了来自 IPointerDownHandler
、IPointerUpHandler
和 IDragHandler
接口的功能。
不要忘记将 Physics Raycaster
附加到相机。