using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif
[AddComponentMenu("UI/Raycast Zone")]
public class RaycastZone : MaskableGraphic
{
#if UNITY_EDITOR
[SerializeField] private bool m_EnableDebugDraw = false;
[SerializeField] private Color m_DebugColor = new Color(1f, 0f, 0f, 0.2f);
#endif
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
#if UNITY_EDITOR
if (!m_EnableDebugDraw)
return;
var rect = GetPixelAdjustedRect();
UIVertex vertex = UIVertex.simpleVert;
vertex.color = m_DebugColor;
vertex.position = new Vector2(rect.xMin, rect.yMin);
vh.AddVert(vertex);
vertex.position = new Vector2(rect.xMin, rect.yMax);
vh.AddVert(vertex);
vertex.position = new Vector2(rect.xMax, rect.yMax);
vh.AddVert(vertex);
vertex.position = new Vector2(rect.xMax, rect.yMin);
vh.AddVert(vertex);
vh.AddTriangle(0, 1, 2);
vh.AddTriangle(2, 3, 0);
#endif
}
protected override void OnPopulateMesh(Mesh mesh)
{
mesh.Clear();
}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
SetAllDirty();
}
public void SetDebugVisible(bool visible)
{
m_EnableDebugDraw = visible;
SetAllDirty();
}
public void SetDebugColor(Color color)
{
m_DebugColor = color;
SetAllDirty();
}
#endif
}
📘 正文:
✨ 为什么要用 RaycastZone?
在移动 UI 或自定义输入中,常常需要一个看不见但能响应点击的区域。例如:
- 虚拟摇杆
- 全屏点击遮罩
- 引导指引层的“非透明点击区域”
很多开发者习惯用 Image
组件设置透明来处理,但这样仍然会:
✖ 产生 draw call ✖ 增加 fillrate 开销 ✖ 导致 GPU 负担
于是,就有了这类经典实现:继承 MaskableGraphic,但不绘制图像
。
🧠 RaycastZone 原理
- MaskableGraphic 本身具有射线检测(RaycastTarget)能力
- 重写 OnPopulateMesh 清空顶点数据,不渲染图像
- 保留 raycastTarget,可以照常接收点击事件
✅ 真机无逻辑开销的优化
我们通过 #if UNITY_EDITOR
宏包装调试部分:
- 编辑器中可开启红色调试绘制
- 构建到 iOS/Android/WebGL 后调试逻辑完全剔除,连 if 判断都没有
🧪 使用建议
- 添加 UI 空物体,附加 RaycastZone.cs
- 设置尺寸/锚点,作为你想监听的点击区域
- 不要添加 Sprite/Image
- 如需调试:勾选 Enable Debug Draw,可设置颜色透明度
🚀 性能对比
方法 | 是否渲染 | 可点击 | 真机性能 |
---|---|---|---|
Image + Alpha 0 | ✅ 有渲染 | ✅ | 🟡 有额外 fillrate |
RaycastZone | ❌ 不渲染 | ✅ | 🟢 零开销,最推荐 |