前言:每日记录自己学习unity的心得和体会,小弟才疏学浅,如有错误的地方,欢迎大佬们指正,感谢~
如下图 想要实现精确的点击不发生遮挡的情况
UGUI 想要只点击表盘有反应的效果 (点击箭头处没有反应)
原理:精灵像素检测
UGUI在处理控件是否被点击的时候,主要是根据IsRaycastLocationValid这个方法的返回值来进行判断的,而这个方法用到的基本原理则是判断指定点对应像素的RGBA数值中的Alpha是否大于某个指定临界值。
例如,我们知道半透明通常是指Alpha=0.5,而对一个后缀名为png格式的图片来说半透明或者完全透明的区域理论上不应该被响应的,所以根据这个原理,我们只需要设定一个透明度的临界值,然后对当前鼠标位置对应的像素进行判断就可以了
重要的一点
下面是重写 IsRaycastLocationValid 的方法 直接挂在按钮上即可
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 不规则按钮点击 核心代码
/// </summary>
[RequireComponent(typeof(Image))]
public class BuGuiZhe : MonoBehaviour, ICanvasRaycastFilter
{
private Image image_;
private Sprite sprite_;
[Tooltip("设定Sprite响应的Alpha阈值")] //工具功能提示
[Range(0, 0.5f)]
public float alpahThreshold = 0.5f;
void Start()
{
image_ = GetComponent<Image>();
}
/// 重写IsRaycastLocationValid接口
public bool IsRaycastLocationValid(Vector2 vtor2, Camera main_Camera)
{
sprite_ = image_.sprite;
var rectTransform = (RectTransform)transform;
Vector2 localPositionPivotRelative;
RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)transform, vtor2, main_Camera, out localPositionPivotRelative); // 转换为以屏幕左下角为原点的坐标系
var localPosition = new Vector2(localPositionPivotRelative.x + rectTransform.pivot.x * rectTransform.rect.width,
localPositionPivotRelative.y + rectTransform.pivot.y * rectTransform.rect.height);
var spriteRect = sprite_.textureRect;
var maskRect = rectTransform.rect;
var x = 0;
var y = 0; // 转换为纹理空间坐标
switch (image_.type)
{
case Image.Type.Sliced:
{
var border = sprite_.border; // x 轴裁剪
if (localPosition.x < border.x)
{
x = Mathf.FloorToInt(spriteRect.x + localPosition.x);
}
else if (localPosition.x > maskRect.width - border.z)
{
x = Mathf.FloorToInt(spriteRect.x + spriteRect.width - (maskRect.width - localPosition.x));
}
else
{
x = Mathf.FloorToInt(spriteRect.x + border.x +
((localPosition.x - border.x) /
(maskRect.width - border.x - border.z)) *
(spriteRect.width - border.x - border.z));
} // y 轴裁剪
if (localPosition.y < border.y)
{
y = Mathf.FloorToInt(spriteRect.y + localPosition.y);
}
else if (localPosition.y > maskRect.height - border.w)
{
y = Mathf.FloorToInt(spriteRect.y + spriteRect.height - (maskRect.height - localPosition.y));
}
else
{
y = Mathf.FloorToInt(spriteRect.y + border.y +
((localPosition.y - border.y) /
(maskRect.height - border.y - border.w)) *
(spriteRect.height - border.y - border.w));
}
}
break;
case Image.Type.Simple:
default:
{ // 转换为统一UV空间
x = Mathf.FloorToInt(spriteRect.x + spriteRect.width * localPosition.x / maskRect.width);
y = Mathf.FloorToInt(spriteRect.y + spriteRect.height * localPosition.y / maskRect.height);
}
break;
}
try
{
return sprite_.texture.GetPixel(x, y).a > alpahThreshold;
}
catch (UnityException e)
{
Debug.LogError("请检查图片设置是不是已经勾选: Advanced/Read/Write Enabled'" + e.Message);
// 如果texture导入过程报错,则删除组件
//Destroy(this);
return false;
}
}
}
然后在按钮上绑定方法进行测试 (测试脚本如下)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Btn_coner : MonoBehaviour
{
private Button btn;
int sdsd = 0;
// Start is called before the first frame update
void Start()
{
btn = this.GetComponent<Button>();
btn.onClick.AddListener(delegate {
sdsd++;
Debug.Log(string.Format("点击了{0}次", sdsd));
});
}
}
如果感觉还是不太准确,可能还有那么一点点偏移(偏下)
在设置一下就好,一般来说不设置的话也感觉不出来的(反正我是没有感觉出来,哈哈哈)