策划要求做一个新手引导的遮罩,之前一直没做过新手引导,记得之前游戏都是方框的可以点击区域,其他地方鼠标不能穿透点击。网上找了一下还没找到相关现成的资源,花了一些功夫研究了下。
思路如下:
遮挡点击可以利用Image组件的RaycastTarget.只有透明部分让鼠标可以穿透,就可以复写Image的IsRaycastLocationValid方法.计算是否点击在椭圆内。
制作如下:
因为遮罩是椭圆或者圆形的可点击区域。随便找一个绘制椭圆的shader,改一改
改的一个从椭圆遮罩变为反向椭圆遮罩,椭圆部分alpha为0,可以实现新手引导使用,增加了一点边缘alpha过渡,看起来没毛刺。
_LengthRate用来调整椭圆大小.
_LengthRateTransition是我增加用来增加一点alpha过渡,这样显示出来的圆没毛刺。不过偷懒了,椭圆远的地方和近的地方过渡长度不一样,后面可以优化下。
//对图片的mask处理,只有椭圆部分
Shader "Unlit/Transparent Colored Mask UnRound"
{
Properties
{
_MainTex("Base (RGB), Alpha (A)", 2D) = "black" {}
_CenterX("CenterX", float) = 0.5
_CenterY("CenterY", float) = 0.5
_Width("Width", float) = 0.5
_Height("Height", float) = 0.15
_LengthRate("LengthRate", float) = 0.1
_LengthRateTransition("LengthRateLerp", float) = 0.01
}
SubShader
{
LOD 200
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
Pass
{
Cull Off
Lighting Off
ZWrite Off
Fog{ Mode Off }
Offset -1,-1
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_ST;
half _CenterX;
half _CenterY;
half _Width;
half _Height;
half _LengthRate;
half _LengthRateTransition;
struct appdata_t
{
half4 vertex : POSITION;
half2 texcoord : TEXCOORD0;
fixed4 color : COLOR;
};
struct v2f
{
half4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
fixed4 color : COLOR;
};
v2f o;
v2f vert(appdata_t v)
{
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = v.texcoord;
o.color = v.color;
return o;
}
fixed4 frag(v2f IN) : COLOR
{
half2 pt = IN.texcoord - half2(_CenterX,(1 - _CenterY));
half h = (pt.x*pt.x) / (_Width*_Width) + (pt.y*pt.y) / (_Height*_Height);
half4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
half sub = _LengthRate - h;
if (h < _LengthRate && sub < _LengthRateTransition)
{
c.a = c.a - sub / _LengthRateTransition ;
return c;
}
else if (h < _LengthRate)
{
discard;
}
return c;
}
ENDCG
}
}
SubShader
{
LOD 100
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
Pass
{
Cull Off
Lighting Off
ZWrite Off
Fog{ Mode Off }
Offset -1,-1
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
ColorMaterial AmbientAndDiffuse
SetTexture[_MainTex]
{
Combine Texture * Primary
}
}
}
}
配合Image组件使用,令透明区域可以穿透点击,其他地方阻挡。修改IsRaycastLocationValid方法,计算鼠标点击位置。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CGuideImage : Image
{
[SerializeField]
protected Vector2 _Center = new Vector2(0.5f, 0.5f);
[SerializeField]
protected Vector2 _Size = new Vector2(0.5f, 0.15f);
[SerializeField]
protected float radius = 0.1f;
public bool LocationCanClick = false;
//BoxCollider2D coll;
//protected override void Start()
//{
// base.Start();
// coll = gameObject.GetComponent<BoxCollider2D>();
//}
public void changePlace(Vector2 pos, Vector2 sz, float r)
{
_Center = pos;
_Size = sz;
radius = r;
material.SetFloat("_CenterX", pos.x);
material.SetFloat("_CenterY", pos.y);
material.SetFloat("_Width", sz.x);
material.SetFloat("_Height", sz.y);
material.SetFloat("_LengthRate", r);
}
Vector2 local;
//是否是有效点击位置,返回true表示不能穿透.
public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
{
if (alphaHitTestMinimumThreshold <= 0)
{
LocationCanClick = false;
return true;
}
if (alphaHitTestMinimumThreshold > 1)
{
LocationCanClick = true;
return false;
}
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out local))
{
LocationCanClick = true;
return false;
}
//Debug.Log("local" + local.ToString());
Rect rect = GetPixelAdjustedRect();
// Convert to have lower left corner as reference point.
local.x += rectTransform.pivot.x * rect.width;
local.y += rectTransform.pivot.y * rect.height;
//Debug.Log("local2" + local.ToString());
Vector2 at = new Vector2(local.x / rectTransform.sizeDelta.x, 1f - local.y / rectTransform.sizeDelta.y);
Vector2 pt = at - _Center;
float h = (pt.x * pt.x) / (_Size.x * _Size.x) + (pt.y * pt.y) / (_Size.y * _Size.y);
//Debug.Log("to:" + at.x.ToString() + "," + at.y.ToString() + " h :" + h.ToString());
if (h < radius)
{
LocationCanClick = true;
return false;
}
LocationCanClick = false;
return true;
}
}
这样就实现了鼠标穿透了,如果用了Input的鼠标点击方法,还可以取LocationCanClick变量来判断是否在区域内。
材质:
IsRaycastLocationValid方法当时想法是获取alpha来判断是否椭圆区域,但是发现根本没办法拿到,因为走的材质球的,渲染都在显卡,除非是用相机加一个RenderTexture来获取再给Image的图片附加,感觉复杂了,没有进一步研究。
方法有点麻烦,欢迎探讨,相互交流。