Unity新手引导(圆形指引、矩形指引)
Unity新手引导(圆形指引、矩形指引)声明:中心镂空为圆形或矩形,增加指引动画,基于UGUI一、Shader建立两个shader,命名为RectGuide、CircleGuide。RectGuideShader "UI/RectGuide"{Properties{[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}_Color
·
Unity新手引导(圆形指引、矩形指引)
声明:中心镂空为圆形或矩形,增加指引动画,基于UGUI
一、Shader
建立两个shader,命名为RectGuide、CircleGuide。
- RectGuide
Shader "UI/RectGuide"
{
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
};
sampler2D _MainTex;
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
float4 _MainTex_ST;
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 = TRANSFORM_TEX(v.texcoord, _MainTex);
OUT.color = v.color * _Color;
return OUT;
}
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
}
}
}
- CircleGuide
Shader "UI/CircleGuide"
{
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)
_Slider("Slider",Range(0,2500)) = 2500
}
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
};
sampler2D _MainTex;
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
float4 _MainTex_ST;
float2 _Center;
float _Slider;
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 = TRANSFORM_TEX(v.texcoord, _MainTex);
OUT.color = v.color * _Color;
return OUT;
}
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
color.a *= (distance(IN.worldPosition.xy,_Center.xy) > _Slider);
color.rgb *= color.a;
return color;
}
ENDCG
}
}
}
二、前期准备
- 建立两个材质球,命名为rectMat、circleMat,将对应shader赋值给材质。
- 创建UI-Image,将Image填充满屏幕,将其中一个材质赋值给Image上的material,修改Image的透明度,一般为纯黑半透明。
三、脚本实现
- 创建基类GuideBase脚本,用来管理两个shader,因为中心镂空的效果,除了半 径或者长宽的计算方式不一样,其他的都一样。
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(Image))]
public class GuideBase : MonoBehaviour
{
protected Material material;//材质
protected Vector3 center;//镂空中心
protected RectTransform target;//被引导的目标对象
protected Vector3[] targetCorners = new Vector3[4];//引导目标的边界
protected float timer;//计时器,来达到动画匀速播放
protected float time;//整体动画时间
protected bool isScaling;//是否正在缩放
//虚方法,子类可以去重写,里面用来判断动画是否播放,如果播放,就按照既定的时间匀速完成
protected virtual void Update()
{
if (isScaling)
{
timer += Time.deltaTime * 1 / time;
if (timer >= 1)
{
timer = 0;
isScaling = false;
}
}
}
//这里是来获取目标物体的四个点来计算中心点,因为对于矩形或者圆形效果,他们面对的中心点是确定的
public virtual void Guide(Canvas canvas, RectTransform target)
{
material = GetComponent<Image>().material;
this.target = target;
//获取四个点的世界坐标
target.GetWorldCorners(targetCorners);
//世界坐标转屏幕坐标
for (int i = 0; i < targetCorners.Length; i++)
{
targetCorners[i] = WorldToScreenPoints(canvas, targetCorners[i]);
}
//计算中心点
center.x = targetCorners[0].x + (targetCorners[3].x - targetCorners[0].x) / 2;
center.y = targetCorners[0].y + (targetCorners[1].y - targetCorners[0].y) / 2;
//设置中心点
material.SetVector("_Center", center);
}
//为了让子类继承的时候直接重写就可以,因为矩形和圆形的动画方式不一样,跟长宽或者半径有关
public virtual void Guide(Canvas canvas, RectTransform target,float scale,float time)
{
}
//坐标的转换
public Vector2 WorldToScreenPoints(Canvas canvas, Vector3 world)
{
//把世界转屏幕
Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, world);
Vector2 localPoint;
//屏幕转局部坐标
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(), screenPoint, canvas.worldCamera, out localPoint);
return localPoint;
}
}
- 创建CircleGuide和RectGuide脚本来重写基类GuideBase脚本里的通用方法。
- CircleGuide脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CircleGuide : GuideBase
{
private float r;//镂空半径
private float scaleR;//变化之后的半径大小
//继承GuideBase基类,重写他的获取目标位置同时修改半径的方法
public override void Guide(Canvas canvas, RectTransform target)
{
base.Guide(canvas, target);//继承基类里面获取中心点的计算
//计算半径
float width = (targetCorners[3].x - targetCorners[0].x) / 2;
float height = (targetCorners[1].y - targetCorners[0].y) / 2;
r=Mathf.Sqrt(width*width+height*height);
this.material.SetFloat("_Slider", r);
}
//重写基类动画方法,获取半径值来达到动画效果
public override void Guide(Canvas canvas, RectTransform target, float scale, float time)
{
this.Guide(canvas, target);//需要中心点,所以直接调用上一个方法
scaleR = r * scale;
this.material.SetFloat("_Slider", scaleR);
this.time = time;
isScaling = true;
timer = 0;
}
protected override void Update()
{
base.Update();
if (isScaling)
{
this.material.SetFloat("_Slider", Mathf.Lerp(scaleR,r,timer));
}
}
}
- RectGuide脚本
using UnityEngine;
using UnityEngine.UI;
public class RectGuide : GuideBase
{
protected float width;//镂空宽
protected float height;//镂空高
float scalewidth;
float scaleheight;
public override void Guide(Canvas canvas,RectTransform target)
{
base.Guide(canvas, target);
//计算宽高
width = (targetCorners[3].x - targetCorners[0].x) / 2;
height = (targetCorners[1].y - targetCorners[0].y) / 2;
material.SetFloat("_SliderX", width);
material.SetFloat("_SliderY", height);
}
public override void Guide(Canvas canvas, RectTransform target,float scale,float time)
{
this.Guide(canvas, target);
scalewidth = width * scale;
scaleheight = height * scale;
material.SetFloat("_SliderX", scalewidth);
material.SetFloat("_SliderY", scaleheight);
this.time = time;
isScaling = true;
timer = 0;
}
protected override void Update()
{
base.Update();
if (isScaling)
{
this.material.SetFloat("_SliderX", Mathf.Lerp(scalewidth, width, timer));
this.material.SetFloat("_SliderY", Mathf.Lerp(scaleheight, height, timer));
}
}
}
- 创建GuideController脚本来实现方法被调用,从而达到相应效果。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public enum GuideType
{
Rect,
Circle,
}
[RequireComponent(typeof(CircleGuide))]
[RequireComponent(typeof(RectGuide))]
public class GuideController : MonoBehaviour,ICanvasRaycastFilter
{
private CircleGuide circleGuide;
private RectGuide rectGuide;
public Material rectMat;
public Material circleMat;
private Image mask;//就是本身,纯黑半透明
private RectTransform target;
private void Awake()
{
mask = transform.GetComponent<Image>();
if (mask == null) { throw new System.Exception("mask初始化失败"); }
if (rectMat == null || circleMat == null) { throw new System.Exception("材质未赋值"); }
rectGuide = transform.GetComponent<RectGuide>();
circleGuide = transform.GetComponent<CircleGuide>();
}
public void Guide(Canvas canvas,RectTransform target,GuideType guideType)
{
this.target = target;
switch (guideType)
{
case GuideType.Rect:
mask.material = rectMat;
rectGuide.Guide(canvas, target);
break;
case GuideType.Circle:
mask.material = circleMat;
circleGuide.Guide(canvas, target);
break;
}
}
public void Guide(Canvas canvas, RectTransform target, GuideType guideType,float scale,float time)
{
this.target = target;
switch (guideType)
{
case GuideType.Rect:
mask.material = rectMat;
rectGuide.Guide(canvas, target, scale,time);
break;
case GuideType.Circle:
mask.material = circleMat;
circleGuide.Guide(canvas, target, scale, time);
break;
}
}
//这里的方法代表是否镂空内容可被点击,返回false则可以,true则不可以
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
if (target==null) { return true; }
return !RectTransformUtility.RectangleContainsScreenPoint(target, sp);
}
}
四、测试
- 在Image上挂载RectGuide、CircleGuide、GuideController脚本,同时创建测试脚本GuidePanel并挂载到Image身上。
- GuidePanel的实现
using UnityEngine;
public class GuidePanel : MonoBehaviour
{
GuideController guideController;
Canvas canvas;
private void Start()
{
canvas = transform.GetComponentInParent<Canvas>();
guideController = transform.GetComponent<GuideController>();
//这句代码的参数代表(哪个画布,哪个对象需要被镂空引导,镂空的类型,镂空缩放动画前的比例,镂空缩放动画的时长)
guideController.Guide(canvas, GameObject.Find("GuideUI").GetComponent<RectTransform>(), GuideType.Circle,2,0.5f);
}
}
更多推荐
已为社区贡献1条内容
所有评论(0)