挖坑和高亮
这里总结了一些常用的方式,不全面归纳如下:
1.将目标操作对象(Button/或重载自写的交互事件)区域提取到引导灰色层之上
处理方式: A. 在对象上加Canvs控制层级
B. 直接把对象的父节点设置到引导层上
优点:a.即达到了高亮又解决了唯一可点击的问题
b.点击的是实际的按键,省去了再处理交互事件的麻烦
缺点:a.如果这个对象的可操作区域和对应显示区域大小不一样时会比较尴尬
b.把层级提上来之后,之后再放下去,对于一些比较复杂的界面来说很麻烦(比如这个对象上还有一些层级不一样的特效 粒子,还需要单独去设置这些粒子的层级)
c.如果是B这种方法实现的话,目标对象如果是在Layout列表里的话,处理起来就麻烦
2.在引导Mask上挖坑,区域为可操作区域(真挖坑)
处理方式:写个Shader挖形状挂在Raycast关闭的Image上,再用四张Image做遮罩动态计算修改空点击范围
优点:a.点击的是实际的按键,省去了再处理交互事件的麻烦
b.高亮区域可控,也比较方便
c.不改变原来对象的层级和节点结构,省去了维护这个的麻烦
缺点:a.Shader没法挖出想要的形状,特别是有些UI形状古怪的
b.实际可操作区域永远是大小可变的矩形,没有圆形(基本能满足需求)
3.在Mask上挖坑,实际并没有挖出操作区域
处理方式:写Shader挖坑,并打开Image的Raycast,判断操作区域时,通过判断这个Image上的alph = 0 来知道点击的是可操作区域
优点:a.不改变原来对象的层级和节点结构
缺点:a.高亮区域很难挖出想要的形状
b.需要另外想办法获取对象上的交互事件
4.复制一个待操作对象FakeObj到引导Mask上
处理方式:mask全遮住,点击后判断是点在FakeObj上则把交互传递下去
以上几种方法还能衍生出很多组合,看实际项目需求吧。
我的做法是,上面分具体情况利用了上面的两种方法:挖坑/赋值假对象(想怎么样通过配置确定,实际上写了4种类型来应对不同的需求),因为操作区域可能需要:长按 / 拖拽 / 操作后需要等待服务器回调 / 。。。
下面是挖坑的Shader代码:
可同时挖两个坑(圆+矩形 / 圆+圆),能力有限,矩形的边缘虚化还没有想到更好的办法
Shader "UICustom/GuideMask"
{
Properties
{
[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
_Color("Tint", Color) = (0.573,0.573,0.573,1)
_StencilComp("Stencil Comparison", Float) = 8
_Stencil("Stencil ID", Float) = 0
_StencilOp("Stencil Operation", Float) = 0
_StencilWriteMask("Stencil Write Mask", Float) = 255
_StencilReadMask("Stencil Read Mask", Float) = 255
_ColorMask("Color Mask", Float) = 15
//中心 - x/y/宽/高
_Origin("圆心",Vector) = (0,0,0,0)
//中心2 - x/y/宽/高
_Origin2("圆心2",Vector) = (0,0,0,0)
//裁剪方式 0圆形 1矩形
_MaskType("Type",Float) = 0
//屏幕高宽
_ScreenY("屏幕高",Float) = 1920
_ScreenX("屏幕宽",Float) = 1080
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0
[PowerSlider(1)] _DimRange("DimRange", Range(0.0, 1.0)) = 0.066
[PowerSlider(1)] _DimRectRange("DimRectRange", Range(0.0, 1.0)) = 0.006
}
SubShader
{
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas" = "True"
}
Stencil
{
Ref[_Stencil]
Comp[_StencilComp]
Pass[_StencilOp]
ReadMask[_StencilReadMask]
WriteMask[_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest[unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask[_ColorMask]
//pass 1
Pass
{
Name "GDMask"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
float4 _Origin;
float _MaskType;
float _ScreenX;
float _ScreenY;
float _DimRange;
float _DimRectRange;
float4 _Origin2;
v2f vert(appdata_t IN)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.worldPosition = IN.vertex;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
return OUT;
}
sampler2D _MainTex;
fixed4 frag(v2f IN) : SV_Target
{
half2 uv = IN.texcoord;
half4 col = IN.color;
//开始裁剪
//外部直接给坐标 宽 高 GPU计算比率
half posX = (_Origin.x + _ScreenX / 2) / _ScreenX;
half posY = (_Origin.y + _ScreenY / 2) / _ScreenY;
half2 pos = half2(posX, posY);
if (_MaskType == 0) {
posX = posX * _ScreenX / _ScreenY;
pos = half2(posX, posY);
half rid = _Origin.z / _ScreenY / 2;
uv.x = uv.x * _ScreenX / _ScreenY;
half2 nor = uv - pos;
half len = length(nor);
if (len < rid + _DimRange)
{
if (len < rid)
{
col.a = 0;
}
else {
//向内虚化18.2.5Sam
if (len >= rid)
col.a = col.a*((len - rid) / _DimRange);
}
}
}
//矩形剪裁
else {
//矩形剪裁 + 四角模糊
float w = _Origin.z / _ScreenX / 2;
float h = _Origin.w / _ScreenY / 2;
float dr_w = _Origin.z / _ScreenX / 2 + _DimRectRange;
float dr_h = _Origin.w / _ScreenY / 2 + _DimRectRange;
//向内虚化18.2.5Sam
if (uv.x > pos.x - dr_w && uv.x<pos.x + dr_w && uv.y>pos.y - dr_h && uv.y < pos.y + dr_h)
{
if (uv.x > pos.x - w && uv.x < pos.x + w
&& uv.y > pos.y - h && uv.y < pos.y + h)
{
col.a = 0;
}
if ((uv.x <= pos.x - w && uv.y <= pos.y - h)
|| (uv.x <= pos.x - w && uv.y >= pos.y + h)
|| (uv.x >= pos.x + w && uv.y <= pos.y - h)
|| (uv.x >= pos.x + w && uv.y >= pos.y + h)
)
{
上下左右
//half4 a = tex2D(_MainTex, uv + float2(_Offset, 0));
//half4 b = tex2D(_MainTex, uv + float2(-_Offset, 0));
//half4 c = tex2D(_MainTex, uv + float2(0, _Offset));
//half4 d = tex2D(_MainTex, uv + float2(0, -_Offset));
//偏45度上下左右
float _Offset = 0.01;
half4 e = tex2D(_MainTex, uv + float2(_Offset / 2, _Offset / 2));
half4 f = tex2D(_MainTex, uv + float2(-_Offset / 2, _Offset / 2));
half4 g = tex2D(_MainTex, uv + float2(_Offset / 2, -_Offset / 2));
half4 h = tex2D(_MainTex, uv + float2(-_Offset / 2, -_Offset / 2));
//+ e + f + g + h
half4 texCol = tex2D(_MainTex, uv);
texCol = (texCol + (e + f + g + h)) * 0.14 *_Color;//;
return texCol;
}
//-----------上下透明度过渡--------------------------------------------
if (uv.x <= pos.x - w || uv.x >= pos.x + w)
{
if (uv.x <= pos.x - w)
{
col.a = col.a*((pos.x - w - uv.x) / _DimRectRange);
}
else {
col.a = col.a*((uv.x - (pos.x + w)) / _DimRectRange);
}
}
else if (uv.y <= pos.y - h || uv.y >= pos.y + h)
{
if (uv.y <= pos.y - h)
{
col.a = col.a*((pos.y - h - uv.y) / _DimRectRange);
}
else
{
col.a = col.a*((uv.y - (pos.y + h)) / _DimRectRange);
}
}
//-----------------------------------------------------------------
}
}
//---------圆2--------------------------------------
if (_Origin2.z > 0)
{
half posX2 = (_Origin2.x + _ScreenX / 2) / _ScreenX;
half posY2 = (_Origin2.y + _ScreenY / 2) / _ScreenY;
half2 pos2 = half2(posX2, posY2);
posX2 = posX2 * _ScreenX / _ScreenY;
pos2 = half2(posX2, posY2);
half rid2 = _Origin2.z / _ScreenY / 2;
if (_MaskType != 0)
uv.x = uv.x * _ScreenX / _ScreenY;
half2 nor2 = uv - pos2;
half len2 = length(nor2);
if (len2 < rid2 + _DimRange)
{
if (len2 < rid2 && col.a >0)
{
col.a = 0;
}
else {
//向内虚化18.2.5Sam
if (len2 >= rid2)
col.a = col.a*((len2 - rid2) / _DimRange);
}
}
}
//-----------------------------------------------------
half4 color = (tex2D(_MainTex, uv) + _TextureSampleAdd) * col;
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
clip(col.a);
#ifdef UNITY_UI_ALPHACLIP
clip(color.a - 0.001);
#endif
return color;
}
ENDCG
}//end pass 1
}
}
效果如下
Vect4 类型 x y 表示屏幕坐标的位置, z w 分别表示宽和高(圆形的话,只用z表示半径)
参数中 DimRange 和 DimRectRange 调整好后基本可以不动了,
只需调整 _Origin _Origin2 _MaskType来控制大小和形状即可,具体在脚本里怎么获取在说就多余了。
shader是借鉴别人的的基础上改的,有点糙,但是基本满足了我的需求,请多指教。
最后,不得不说这些方法能做到的都是有限的,对于不同项目出现的不同需求,还是需要自己灵活使用。