Unity之新手引导shader遮罩事件穿透
效果图
设计思路
1.新手引导我们期待开发内容不影响正常的功能模块,意思就是分层,新手引导在正常功能之上
2.新手引导层级用一层深色bg显示遮住正常功能层级,在需要引导的位置留出高亮区域
3.在这个新手引导层做事件渗透,给指定的对象或UI做事件渗透,如果点击交互位置处于指定UI或对象范围内,让事件渗透新手引导层,到正常功能层。
场景搭建
搭建两个层级
制作新手引导的预制体
代码
这里有两个比较重要的内容,一个是遮罩层是深色的,并要镂空指定区域做高亮,这里使用shader去制作效果图。二是在合适时机做指定区域的事件渗透。
GuideMask.cs
/// <summary>
/// 创建圆形点击区域
/// </summary>
/// <param name="pos">圆心的屏幕位置</param>
/// <param name="rad">圆的半径</param>
/// <param name="CallBack">点击的回调</param>
public void CreateCircleMask(Vector3 pos, float rad, GameObject target)
{
ShowGuideMask(()=> {
ShowTween = true;
ev.SetTargetimage(target);
_rectTrans.sizeDelta = Vector2.zero;
_materia.SetFloat("_MaskType", 0f);
CurRadNum = rad;
_materia.SetVector("_Origin", new Vector4(pos.x, pos.y, rad + 1000, 20));
});
}
public void ShowGuideMask(Action callback)
{
ShowTween = false;
if (_rectTrans == null)
{
ResMgr.Instance.Load("GuideSystem", (obj) => {
guide = Instantiate((GameObject)obj, ISceneManager.Instance.GuideHolder);
_rectTrans = guide.GetComponent<RectTransform>();
_rawImage = guide.GetComponent<RawImage>();
_rawImage.color = new Color(1, 1, 1, 1);
_materia = _rawImage.material;
ev = guide.GetComponent<EventPenetrate>();
callback();
});
}
else
{
callback();
}
}
GuideMask.shader
Shader "UI/GuideMask"
{
Properties
{
[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
_Color("Tint", Color) = (1,1,1,1)
_Blur("边缘虚化的范围", Range(1,1000)) = 100
_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
//中心
_Origin("Circle",Vector) = (0,0,0,0)
//裁剪方式 0圆形 1圆形
_MaskType("Type",Float) = 0
}
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
{
Name "Default"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
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 _Blur;
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
{
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
if (_MaskType == 0) {
//if (distance(IN.worldPosition.xy, _Origin.xy) <= _Origin.z)
//{
// color.a = 0;
//}
float dis = distance(IN.worldPosition.xy, _Origin.xy);
//过滤掉距离小于(半径-过渡范围)的片元
clip(dis - (_Origin.z - _Blur));
//优化if条件判断,如果距离小于半径则执行下一步,等于if(dis < _Radius)
fixed tmp = step(dis, _Origin.z);
//计算过渡范围内的alpha值
color.a *= (1 - tmp) + tmp * (dis - (_Origin.z - _Blur)) / _Blur;
}
else if (_MaskType == 1) {
//UnityGet2DClipping这个函数实现了判断2D空间中的一点是否在一个矩形区域中
if (UnityGet2DClipping(IN.worldPosition.xy, _Origin))
{
color.a = 0;
}
}
else if (_MaskType == 2)
{
if (UnityGet2DClipping(IN.worldPosition.xy, _Origin))
{
color.a = 0;
#ifdef UNITY_UI_CLIP_RECT
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _Origin);
#endif
}
}
return color;
}
ENDCG
}
}
}
调用
GuideMask.Instance.CreateCircleMaskoffset(button2.gameObject, 0, null);
GuideMask.Instance.CloseGuideMask();