Unity3d+Newbie guide引導:讀CSV表驅(qū)動,屏蔽不可點擊區(qū)域,UI鏤空矩形區(qū)域Shader
主要功能
- 表驅(qū)動,引導到哪步查找ui面板下路徑
- 屏蔽不可點擊區(qū)域,點擊屏蔽,UImask鏤空
- 具有點擊該按鈕驅(qū)動下一步,或者點擊新手引導的下一步驅(qū)動
數(shù)據(jù)結(jié)構(gòu)
//新手引導UI箭頭出現(xiàn)的方向
public enum EnGuideDir
{
up = 0,
down = 1,
left = 2,
right = 3
}
//出現(xiàn)引導如何跳轉(zhuǎn)下一步
public enum EnGuideClick
{
NoClickCloseSelf = 0, //點擊空白處關閉當前ui面板
Click = 1, //點擊要引導的按個按鈕
NoClickNoClose = 2, //點擊空白處,只關閉引導mask,不關閉UI面板
ClickNeedNext = 3, // 可以點擊但是要通過點擊 "下一步"按鈕 驅(qū)動 ,針對輸入框
}
[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; // 背景路徑,新手引導的收縮至此,即這個區(qū)域是可點擊區(qū)域,其他區(qū)域半透明黑色,屏蔽點擊
public int isAutoNext = 1; // 是否自動開始下步引導 0:不自動 1:自動
public string param = ""; // 傳入?yún)?shù)
}
策劃用數(shù)據(jù)表

設置引導到第幾步,開啟引導遮罩
/// <summary>
/// 查找當前界面 是否 是當前的新手引導的第n步,如果找到了,執(zhí)行引導遮罩
/// </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>(); //如果引導在滾動層上,加屏蔽滾動
//目標本身可點,擊且點擊后能驅(qū)動到下一步引導,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;
};
}
//目標本身可點擊,點擊后不能驅(qū)動下一步,新手引導暫停
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++;
}
- 協(xié)程中一直查找當前步驟要引導的界面,目標(界面上的按鈕或者圖,用UImask 包裹的區(qū)域,能驅(qū)動下一步)
- 目標本身可點,擊且點擊后能驅(qū)動到下一步引導,m_curIdx+1,并接著引導
- 目標本身可點擊,點擊后不能驅(qū)動下一步,新手引導暫停
- 如果引導目標是在滾動層上,需要加上禁止?jié)L動腳本,防止?jié)L沒了目標
設置遮罩
遮罩繼承ICanvasRaycastFilter
該元素可以過濾射線投射。如果頂級元素被命中,它還能進一步“檢查”該位置是否有效。
- 傳入shader的參數(shù):目標的中心點
- 傳入shader的參數(shù):目標的離中心點最大長度的一半
- 傳入shader的參數(shù):目標的離中心點最大高度的一半
void SetNewTargetImage()
{
//獲取畫布
//Canvas m_canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
//Canvas m_canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
//獲取高亮區(qū)域四個頂點的世界坐標
m_bgTarget.rectTransform.GetWorldCorners(_corners);
//計算高亮顯示區(qū)域咋畫布中的范圍
_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;
//計算高亮顯示區(qū)域的中心
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)
{
//獲取畫布區(qū)域的四個頂點
canvasRectTransform.GetWorldCorners(_corners);
//它從左下開始,到左上, 然后到右上,最后到右下-->左下角開始逆時針旋轉(zhuǎn)
//求偏移初始值
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);
}
}
//設置遮罩材質(zhì)中當前偏移的變量
_material.SetFloat("_SliderX", _currentOffsetX);//設置離中心點最大的x距離
_material.SetFloat("_SliderY", _currentOffsetY);//設置離中心點最大的y距離
m_isSetOk = true;
}
遮罩裁剪shader
// 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高度內(nèi),color.a = 0,達到鏤空效果
源碼
https://github.com/luoyikun/VirtualCity
TestNewGuide場景
NewGuideDemo.m_isNewGuideDemo這個參數(shù)要為true(單獨測試用引動用)
demo演示

浙公網(wǎng)安備 33010602011771號