I’d like to keep using EventSystems click interfaces, they’re convenient but for touch interface they’re so so because when you press on a small screen with them fat fingers the ray misses the monsters.
We used to raycast 8 rays around the touch point which would hit various things, we’d then prioritize. For example if most rays touched the background and only one touch an enemy, the enemy would get the event.
I read that the Event system is fairly open but I haven’t dug into it. Has anyone made such “fat finger physics raycaster”?
At a cursory glance it looks like you simply need to inherit from BaseRaycaster and overide Raycast. You won’t be able to use your current system verbatim, but it will be pretty close.
I turned the PhysicsRaycaster that’s on github into a multi SphereCaster.
If anyone needs PhatFingerRaycaster, here it is
using System.Collections.Generic;
namespace UnityEngine.EventSystems
{
/// <summary>
/// Simple event system using physics raycasts.
/// </summary>
[AddComponentMenu("Event/Phat Finger Raycaster")]
[RequireComponent(typeof(Camera))]
public class PhatFingerRaycaster : BaseRaycaster
{
/// <summary>
/// Const to use for clarity when no event mask is set
/// </summary>
protected const int kNoEventMaskSet = -1;
protected Camera m_EventCamera;
/// <summary>
/// Layer mask used to filter events. Always combined with the camera's culling mask if a camera is used.
/// </summary>
[SerializeField]
protected LayerMask m_EventMaskHighPriority = kNoEventMaskSet;
[SerializeField]
protected LayerMask m_EventMaskLowPriority = kNoEventMaskSet;
protected PhatFingerRaycaster()
{}
public override Camera eventCamera
{
get
{
if (m_EventCamera == null)
m_EventCamera = GetComponent<Camera>();
return m_EventCamera ?? Camera.main;
}
}
/// <summary>
/// Depth used to determine the order of event processing.
/// </summary>
public virtual int depth
{
get { return (eventCamera != null) ? (int)eventCamera.depth : 0xFFFFFF; }
}
/// <summary>
/// Event mask used to determine which objects will receive events.
/// </summary>
public int finalEventMaskLowPriority
{
get { return (eventCamera != null) ? eventCamera.cullingMask & m_EventMaskLowPriority : kNoEventMaskSet; }
}
public int finalEventMaskHighPriority
{
get { return (eventCamera != null) ? eventCamera.cullingMask & m_EventMaskHighPriority : kNoEventMaskSet; }
}
/// <summary>
/// Layer mask used to filter events. Always combined with the camera's culling mask if a camera is used.
/// </summary>
public LayerMask eventMask
{
get { return m_EventMaskLowPriority; }
set { m_EventMaskLowPriority = value; }
}
[SerializeField] float radius=2;
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
{
if (eventCamera == null)
return;
var ray = eventCamera.ScreenPointToRay(eventData.position);
float dist = eventCamera.farClipPlane - eventCamera.nearClipPlane;
var hits = Physics.SphereCastAll ( ray, radius, dist, finalEventMaskHighPriority);
// no high priority hits, so we spherecast low priority layer
if (hits.Length==0)
hits = Physics.SphereCastAll ( ray, radius, dist, finalEventMaskLowPriority);
//var hits = Physics.RaycastAll(ray, dist, finalEventMask);
if (hits.Length > 1)
System.Array.Sort(hits, (r1, r2) => r1.distance.CompareTo(r2.distance));
if (hits.Length != 0)
{
for (int b = 0, bmax = hits.Length; b < bmax; ++b)
{
var result = new RaycastResult
{
gameObject = hits[b].collider.gameObject,
module = this,
distance = hits[b].distance,
worldPosition = hits[b].point,
worldNormal = hits[b].normal,
screenPosition = eventData.position,
index = resultAppendList.Count,
sortingLayer = 0,
sortingOrder = 0
};
resultAppendList.Add(result);
}
}
}
}
}