uGUI学习篇: 新手引导镂空遮罩与事件透传

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/linxinfa/article/details/89310579

前言

新手引导我们经常需要制作这样的效果:全屏有个中间镂空的遮罩,引导玩家点击遮罩镂空的部位的按钮。
在Unity中如何实现这个效果呢,以uGUI的方式为例,跟着下面的步骤开始吧

第一步:shader脚本

创建一个GuideMask.shader,实现镂空遮罩效果,代码如下:

Shader "UI/GuideMask"
{
	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
 
 
	//-------------------add----------------------
      _Center("Center", vector) = (0, 0, 0, 0)
      _Silder ("_Silder", Range (0,1000)) = 1000 // sliders
    //-------------------add----------------------
	}
 
	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_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;
			//-------------------add----------------------
            float _Silder;
            float2 _Center;
            //-------------------add----------------------
			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;
				
				color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
				
				#ifdef UNITY_UI_ALPHACLIP
				clip (color.a - 0.001);
				#endif
				//-------------------add----------------------
               	color.a*=(distance(IN.worldPosition.xy,_Center.xy) > _Silder);
               	color.rgb*= color.a;
               	//-------------------add----------------------
				return color;
			
			}
		ENDCG
		}
	}
}

第二步:创建材质球

新建一个材质球,命名为GuideMask.material,并使用上面的shader

第三步:C#脚本

创建一个Guide.cs脚本,实现新手引导的镂空遮罩动画,代码如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 新手引导动画
/// </summary>
[RequireComponent(type(EventPermeate))]
public class Guide : MonoBehaviour 
{  
	public Image target;

	private Material material;
	private float diameter; // 直径
	private float current = 0f;
 
	Vector3[] corners = new Vector3[4]; 
 
	void Awake () 
	{
 
        // 设置事件透传对象
        gameObject.GetComponent<EventPermeate>.target = target.gameObject;
        
		Canvas canvas = GameObject.Find ("Canvas").GetComponent<Canvas> ();
		target.rectTransform.GetWorldCorners (corners);
		diameter = Vector2.Distance (WordToCanvasPos(canvas,corners [0]), WordToCanvasPos(canvas,corners [2])) / 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 center = new Vector3 (x, y, 0f);
		Vector2 position = Vector2.zero;
		RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, center, canvas.GetComponent<Camera>(), out position);
 
		center = new Vector4 (position.x,position.y,0f,0f);
		material = GetComponent<Image>().material;
		material.SetVector ("_Center", center);
	
		(canvas.transform as RectTransform).GetWorldCorners (corners);
		for (int i = 0; i < corners.Length; i++) 
		{
			current = Mathf.Max(Vector3.Distance (WordToCanvasPos(canvas,corners [i]), center),current);
		}
 
		material.SetFloat ("_Silder", current);
	}

	float yVelocity = 0f;
	void Update () 
	{
		float value = Mathf.SmoothDamp(current, diameter, ref yVelocity, 0.3f);
		if (!Mathf.Approximately (value, current)) {
			current = value;
			material.SetFloat ("_Silder", current);
		}
	}
 
	void OnGUI()
	{
		if(GUILayout.Button("Test"))
		{
			Awake ();
		}
	}
 
	Vector2 WordToCanvasPos(Canvas canvas,Vector3 world)
	{
		Vector2 position = Vector2.zero;
		RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, world, canvas.GetComponent<Camera>(), out position);
		return position;
	}
}

创建一个EventPermeate.cs脚本,实现事件穿透,代码如下:

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System.Collections.Generic;
 
public class EventPermeate: MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler
{ 
	// 事件穿透对象
	[HideInInspector]
	public GameObject target;
	
    // 监听按下
    public void OnPointerDown(PointerEventData eventData)
    {
        PassEvent(eventData,ExecuteEvents.pointerDownHandler);
    }
 
    // 监听抬起
    public void OnPointerUp(PointerEventData eventData)
    {
        PassEvent(eventData,ExecuteEvents.pointerUpHandler);
    }
 
    // 监听点击
    public void OnPointerClick(PointerEventData eventData)
    {
        PassEvent(eventData,ExecuteEvents.submitHandler);
        PassEvent(eventData,ExecuteEvents.pointerClickHandler);
    }

    // 把事件透下去
    public void  PassEvent<T>(PointerEventData data,ExecuteEvents.EventFunction<T> function)
        where T : IEventSystemHandler
    {
        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(data, results); 
        GameObject current = data.pointerCurrentRaycast.gameObject ;
        for(int i =0; i< results.Count;i++)
        {
            if(target == results[i].gameObject)
            {
            	// 如果是目标物体,则把事件透传下去,然后break
                ExecuteEvents.Execute(results[i].gameObject, data,function);
                break;
            }
        }
    }
}

第四步:创建ui进行测试

1 在Canvas创建两个按钮Button1和Button2
2 创建一个Image,命名为guide_mask,设置Width和Height使其可以遮住全屏,为其添加Guide组件,此时会自动添加EventPermeat组件
3 设置guide_mask的Image组件的Color为半透明黑色,设置Material属性为我们上面制作的材质球GuideMask.mat
4 把Button1对象赋值给guide_mask的Guide组件的Target属性
在这里插入图片描述
运行效果
在这里插入图片描述
想要引导点击哪个按钮,就把Guide组件的Target属性设置为那个按钮即可


猜你喜欢

转载自blog.csdn.net/linxinfa/article/details/89310579
今日推荐