Unity3d UGUI图片按钮只有非透明区域(透明阈值)可以点击功能实现(含源码)
前言
在Unity有小地图相关功能场景下,经常有需要点击范围检测的功能,特别是游戏地图界面的精确交互往往是决定玩家体验的关键因素。传统矩形按钮的碰撞检测在复杂地形场景中暴露了显著缺陷:当玩家点击河流、山脉边缘或迷雾区域时,本应无效的地形区域却意外触发了交互会导致地形穿透、视觉误导等问题。
透明阈值点击检测正是解决地图交互的好方案。实现通过像素级Alpha通道分析,将地图覆盖的可点击区域镂空,即可屏蔽不可点击区域。通过设置 Image.alphaHitTestMinimumThreshold 属性,使按钮只响应图像中不透明度高于设定阈值的区域(忽略透明部分)。
实现
核心思想就是UGUI图片组件的alphaHitTestMinimumThreshold 属性控制各区域的点击。
地图UI
这里以地图UI为例,以以下的游戏地图为小地图:
随意创建一个图层覆盖其之上,将可以点击区域镂空:
两个地图图层叠加效果如下:
并将覆盖层图片添加Button,只要输出点击状态即可。
图片需要
图片的 Read/Write Enabled 必须开启(在纹理导入设置中)
纹理格式需支持透明度(如 PNG)
如果不启用会报错:
Using alphaHitTestMinimumThreshold greater than 0 on Image whose sprite texture cannot be read. Texture ‘***_cover’ is not readable, the texture memory can not be accessed from scripts. You can make the texture readable in the Texture Import Settings. Also make sure to
disable sprite packing for this sprite.
编码
直接设置属性代码:
using UnityEngine;
using UnityEngine.UI;[RequireComponent(typeof(Image))]
public class AlphaButton : MonoBehaviour
{[Header("透明度阈值设置")][Tooltip("值越小越敏感,推荐0.05-0.3")][Range(0f, 1f)]public float alphaThreshold = 0.1f;void Start(){ApplyAlphaThreshold();}void OnValidate(){// 在编辑器模式下实时预览效果if (Application.isPlaying){ApplyAlphaThreshold();}}private void ApplyAlphaThreshold(){var image = GetComponent<Image>();if (image != null){image.alphaHitTestMinimumThreshold = alphaThreshold;}}
}
将脚本挂载到 UI Button 对象上,在 Inspector 中调整 alphaThreshold(推荐 0.05-0.3)
确保按钮图片的纹理设置正确(Read/Write Enabled = true)否则会报错。
这里应该是完成了功能,不过测试发现有时候是失效的,最后发现需要设置Mesh Type 为 Full Rect。
所以修改了代码,加入检测图片的设置并提醒:
using UnityEngine;
using UnityEngine.UI;[RequireComponent(typeof(Image))]
public class AlphaButton : MonoBehaviour
{[Header("透明度阈值设置")][Tooltip("值越小越敏感,推荐0.05-0.3")][Range(0f, 1f)]public float alphaThreshold = 0.1f;void Start(){ApplyAlphaThreshold();}void OnValidate(){// 在编辑器模式下实时预览效果if (Application.isPlaying){ApplyAlphaThreshold();}}private void ApplyAlphaThreshold(){var image = GetComponent<Image>();if (image == null) return;// 检测精灵是否存在if (image.sprite == null){Debug.LogError("AlphaButton: 未找到精灵图片!", this);return;}// 检测Mesh Type是否为Full Rectif (image.sprite.meshType != SpriteMeshType.FullRect){Debug.LogError($"AlphaButton: 精灵 '{image.sprite.name}' 的Mesh Type应为Full Rect! " +$"当前类型: {image.sprite.meshType}", this);return;}// 检测纹理是否可读if (!image.sprite.texture.isReadable){Debug.LogError($"AlphaButton: 纹理 '{image.sprite.texture.name}' 未开启Read/Write Enabled! " +"请在导入设置中启用", this);return;}// 应用透明度阈值image.alphaHitTestMinimumThreshold = alphaThreshold;}
}
这里有一种说法是网格类型(如 Tight)会使用简化网格碰撞体,简化网格会破坏透明度检测的准确性。
其他
性能影响:透明度检测会增加计算开销,避免在大量按钮上使用。