效果如图所示:固定大小模式,边缘大小可以调节
效果如图所示:动画缩放模式,边缘大小可以调节
结构如图所示:
Test:测试脚本,用于调用类
using UnityEngine;
public class Test : MonoBehaviour
{
GuideController guideController;
Canvas canvas;
public GameObject game;
private void Start()
{
canvas = transform.GetComponentInParent
GuideController:总控制脚本
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();
if (mask == null) { throw new System.Exception("mask初始化失败"); }
if (rectMat == null || circleMat == null) { throw new System.Exception("材质未赋值"); }
rectGuide = transform.GetComponent();
circleGuide = transform.GetComponent();
}
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;
}
}
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
if (target==null) { return true; }
return !RectTransformUtility.RectangleContainsScreenPoint(target, sp);
}
}
RectGuide:矩形控制脚本
using UnityEngine;
public class RectGuide : GuideBase
{
protected float width;
protected float height;
float scalewidth;
float scaleheight;
public float widths, heights;//设置宽高 余量
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 + widths);
material.SetFloat("_SliderY", height + heights);
}
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));
}
}
}
CircleGuide:圆形控制脚本
using UnityEngine;
public class CircleGuide : GuideBase
{
private float r;//镂空半径
private float scaleR;//变化之后的半径大小
public float radius;//半径 余量
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 + radius);
}
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));
}
}
}
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().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(), screenPoint, canvas.worldCamera, out localPoint);
return localPoint;
}
}
shader脚本:
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
}
}
}
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
}
}
}
如有疑问 可以再评论区留言