主要功能
- 表驱动,引导到哪步查找ui面板下路径
- 屏蔽不可点击区域,点击屏蔽,UImask镂空
- 具有点击该按钮驱动下一步,或者点击新手引导的下一步驱动
数据结构
代码语言:javascript复制//新手引导UI箭头出现的方向
public enum EnGuideDir
{
up = 0,
down = 1,
left = 2,
right = 3
}
//出现引导如何跳转下一步
public enum EnGuideClick
{
NoClickCloseSelf = 0, //点击空白处关闭当前ui面板
Click = 1, //点击要引导的按个按钮
NoClickNoClose = 2, //点击空白处,只关闭引导mask,不关闭UI面板
ClickNeedNext = 3, // 可以点击但是要通过点击 "下一步"按钮 驱动 ,针对输入框
}
[System.Serializable]
public class NewGuideItem
{
public string panelName; //面板的名字
public string imgPath; //目标的路径
public string text;//提示的字
public EnGuideClick isCanClick = EnGuideClick.Click; // 目标按钮可点击 1:可点 0:不可点,并关闭自己 2:不可点,不关闭自己
public int isTextShowDir = -1; // 文本显示按钮的位置 -1 为下, 1为上 , 2
public int belongCanvas = 0; // 属于哪个ui canvas下 0:screen 1:top
public string bgPath; // 背景路径,新手引导的收缩至此,即这个区域是可点击区域,其他区域半透明黑色,屏蔽点击
public int isAutoNext = 1; // 是否自动开始下步引导 0:不自动 1:自动
public string param = ""; // 传入参数
}
策划用数据表
设置引导到第几步,开启引导遮罩
代码语言:javascript复制/// <summary>
/// 查找当前界面 是否 是当前的新手引导的第n步,如果找到了,执行引导遮罩
/// </summary>
/// <param name="delay"></param>
/// <returns></returns>
IEnumerator YieldDoNextNewGuide(int delay)
{
yield return delay;
//DoNextNewGuide();
NewGuideItem item = m_listGuide[m_curIdx];
Transform transCanvas = null;
if (item.belongCanvas == 0)
{
transCanvas = m_canvasScreen;
Debug.Log("transCanvas:" transCanvas.name);
}
else if (item.belongCanvas == 1)
{
transCanvas = m_canvasTop;
Debug.Log("transCanvas:" transCanvas.name);
}
Transform trans = null;
while (trans == null)
{
//Debug.Log("接着找");
yield return new WaitForSeconds(1);
try
{
Debug.Log("新手引导查找:" item.panelName "/" item.imgPath);
trans = transCanvas.Find(item.panelName "/" item.imgPath);
}
catch
{
Debug.Log("新手引导找不到:" item.panelName "/" item.imgPath);
}
}
UGUIPanel panel = transCanvas.Find(item.panelName).GetComponent<UGUIPanel>();
while (panel.m_isOpen == false)
{
yield return null;
}
trans.gameObject.AddComponent<DontDrag>(); //如果引导在滚动层上,加屏蔽滚动
//目标本身可点,击且点击后能驱动到下一步引导,m_curIdx 1,并接着引导
if (item.isCanClick == EnGuideClick.Click && item.isAutoNext == 1)
{
while (trans.gameObject.GetComponent<ClickListener>() == null)
{
yield return null;
}
trans.gameObject.GetComponent<ClickListener>().onNewGuideClick = (obj) =>
{
StartOneNewGuide();
trans.gameObject.GetComponent<ClickListener>().onNewGuideClick = null;
};
}
//目标本身可点击,点击后不能驱动下一步,新手引导暂停
if (item.isCanClick == EnGuideClick.Click && item.isAutoNext == 0)
{
while (trans.gameObject.GetComponent<ClickListener>() == null)
{
yield return null;
}
trans.gameObject.GetComponent<ClickListener>().onNewGuideClick = (obj) =>
{
newguidepanel.Instance.PauseGuide();
trans.gameObject.GetComponent<ClickListener>().onNewGuideClick = null;
};
}
m_lastTarget = trans;
Image img = trans.GetComponent<Image>();
Transform transBg = transCanvas.Find(item.panelName "/" item.bgPath);
Image imgBg = transBg.GetComponent<Image>();
newguidepanel.Instance.Open(img,imgBg,item);
ReqUpdateOtherDataMessage req = new ReqUpdateOtherDataMessage();
req.fieldName = "newStep";
req.value = m_curIdx.ToString();
HallSocket.Instance.SendMsgProto(MsgIdDefine.ReqUpdateOtherDataMessage, req);
m_curIdx ;
}
- 协程中一直查找当前步骤要引导的界面,目标(界面上的按钮或者图,用UImask 包裹的区域,能驱动下一步)
- 目标本身可点,击且点击后能驱动到下一步引导,m_curIdx 1,并接着引导
- 目标本身可点击,点击后不能驱动下一步,新手引导暂停
- 如果引导目标是在滚动层上,需要加上禁止滚动脚本,防止滚没了目标
设置遮罩
遮罩继承ICanvasRaycastFilter 该元素可以过滤射线投射。如果顶级元素被命中,它还能进一步“检查”该位置是否有效。
- 传入shader的参数:目标的中心点
- 传入shader的参数:目标的离中心点最大长度的一半
- 传入shader的参数:目标的离中心点最大高度的一半
void SetNewTargetImage()
{
//获取画布
//Canvas m_canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
//Canvas m_canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
//获取高亮区域四个顶点的世界坐标
m_bgTarget.rectTransform.GetWorldCorners(_corners);
//计算高亮显示区域咋画布中的范围
_targetOffsetX = Vector2.Distance(WorldToCanvasPos(m_canvas, _corners[0]), WorldToCanvasPos(m_canvas, _corners[3])) / 2f;
_targetOffsetY = Vector2.Distance(WorldToCanvasPos(m_canvas, _corners[0]), WorldToCanvasPos(m_canvas, _corners[1])) / 2f;
//计算高亮显示区域的中心
float x = _corners[0].x ((_corners[3].x - _corners[0].x) / 2f);
float y = _corners[0].y ((_corners[1].y - _corners[0].y) / 2f);
Vector3 centerWorld = new Vector3(x, y, 0);
Vector2 center = WorldToCanvasPos(m_canvas, centerWorld);
//设置遮罩材料中中心变量
Vector4 centerMat = new Vector4(center.x, center.y, 0, 0);
_material = GetComponent<Image>().material;
_material.SetVector("_Center", centerMat); //传入要镂空矩形的中心点
//计算当前偏移的初始值
RectTransform canvasRectTransform = (m_canvas.transform as RectTransform);
if (canvasRectTransform != null)
{
//获取画布区域的四个顶点
canvasRectTransform.GetWorldCorners(_corners);
//它从左下开始,到左上, 然后到右上,最后到右下-->左下角开始逆时针旋转
//求偏移初始值
for (int i = 0; i < _corners.Length; i )
{
if (i % 2 == 0)
_currentOffsetX = Mathf.Max(Vector3.Distance(WorldToCanvasPos(m_canvas, _corners[i]), center), _currentOffsetX);
else
_currentOffsetY = Mathf.Max(Vector3.Distance(WorldToCanvasPos(m_canvas, _corners[i]), center), _currentOffsetY);
}
}
//设置遮罩材质中当前偏移的变量
_material.SetFloat("_SliderX", _currentOffsetX);//设置离中心点最大的x距离
_material.SetFloat("_SliderY", _currentOffsetY);//设置离中心点最大的y距离
m_isSetOk = true;
}
遮罩裁剪shader
代码语言:javascript复制// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
Shader "UI/BeginnerGuidance/Rect"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,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
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
_Center("Center",vector) = (0,0,0,0)
_SliderX("SliderX",Range(0,1500)) = 1500
_SliderY("SliderY",Range(0,1500)) = 1500
}
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"
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
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;
float2 _Center;
float _SliderX;
float _SliderY;
v2f vert(appdata_t v)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.worldPosition = v.vertex;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.texcoord = v.texcoord;
OUT.color = v.color * _Color;
return OUT;
}
sampler2D _MainTex;
fixed4 frag(v2f IN) : SV_Target
{
half4 color = (tex2D(_MainTex, IN.texcoord) _TextureSampleAdd) * IN.color;
#ifdef UNITY_UI_CLIP_RECT
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#endif
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
float2 dis = IN.worldPosition.xy - _Center.xy;
color.a *= (abs(dis.x) > _SliderX) || (abs(dis.y) > _SliderY);
color.rgb *= color.a;
return color;
}
ENDCG
}
}
}
float2 dis = IN.worldPosition.xy - _Center.xy; color.a *= (abs(dis.x) > _SliderX) || (abs(dis.y) > _SliderY); 以中心点_Center.xy,最大_SliderX的宽度,和最大_SliderY高度内,color.a = 0,达到镂空效果
源码
https://github.com/luoyikun/VirtualCity TestNewGuide场景 NewGuideDemo.m_isNewGuideDemo这个参数要为true(单独测试用引动用)