推荐阅读
大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。
一、前言
先来看一下效果图:
感觉这种效果超炫酷,就根据大佬的文章复现了一下。
并且详细补充了一下相机、Post、材质球和Shader的处理过程,接下俩就看一下如何实现吧。
二、正文
2-1、实现思路
将鼠标光标的位置传递给Shader中,根据当前像素到像素的距离去处理像素点的颜色值。
这个动作需要拆分分为背景 + 边框两部分使用 2 个 Pass 处理。
Background Pass 背景
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float dis = distance(_MousePos.xy, i.vertex.xy);
float4 addColor = _Color;
addColor.a = saturate(smoothstep(_ColorRadius, 0, dis) - _ColorOffset * sign(col.a-0.1));
addColor.a = addColor.a * col.a * _ColorPow;
return addColor;
}
Border Pass 边框
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_BorderTex, i.uvBorder);
float dis = distance(_MousePos.xy, i.vertexBorder.xy);
col = col + sign(col.a) * smoothstep(_ColorRadius, 0, dis) * _Color * _BorderPow;
return col;
}
2-2、Shader编写
在Unity中,右键新建一个Shader→Standard Surface Shader
:
Shader
的代码如下:
Shader "Custom/HoverBtn"
{
Properties
{
_MainTex ("MainTex", 2D) = "white" {}
_MousePos ("MousePos", Vector) = (0,0,0,0)
_ColorRedius ("ColorRedius", float) = 40
[HDR]_Color ("Color", Color) = (0,0,0,1)
_ColorOffset ("ColorOffset", Range(0, 1)) = 0
_ColorPow ("ColorPow", Range(0, 1)) = 0.5
_BorderTex ("BorderTex", 2D) = "white" {}
_BorderColorMul ("BorderColorPow", float) = 2
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
// MainTex
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
}
// MainColor
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
// 原图形状mask
float4 vert : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float4 vert : TEXCOORD1;
};
fixed4 _MousePos;
float4 _Color;
float _ColorRedius;
float _ColorOffset;
float _ColorPow;
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.vert = UnityObjectToClipPos(v.vert);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float dis = distance(_MousePos.xy, i.vertex.xy);
float4 addColor = _Color;
addColor.a = saturate(smoothstep(_ColorRedius, 0, dis) - _ColorOffset * sign(col.a-0.1));
addColor.a = addColor.a * col.a * _ColorPow;
return addColor;
}
ENDCG
}
// Border Color
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertexBorder : POSITION;
float2 uvBorder : TEXCOORD0;
};
struct v2f
{
float2 uvBorder : TEXCOORD0;
float4 vertexBorder : SV_POSITION;
};
sampler2D _BorderTex;
float4 _BorderTex_ST;
fixed4 _MousePos;
float _ColorRedius;
fixed4 _Color;
float _BorderColorMul;
v2f vert (appdata v)
{
v2f o;
o.vertexBorder = UnityObjectToClipPos(v.vertexBorder);
o.uvBorder = TRANSFORM_TEX(v.uvBorder, _BorderTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_BorderTex, i.uvBorder);
float dis = distance(_MousePos.xy, i.vertexBorder.xy);
col = col + sign(col.a) * smoothstep(_ColorRedius, 0, dis) * _Color * _BorderColorMul;
return col;
}
ENDCG
}
}
}
2-3、控制代码编写
新建个脚本,挂载在按钮上,响应鼠标的悬浮和点击事件:
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class HoverBtn : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
[SerializeField, ColorUsage(true, true)]
private Color color = Color.white;
[SerializeField]
private int radius = 100;
private float scaleFactor;
private Material material;
private Canvas canvas;
Coroutine coroutine_hide;
Coroutine coroutine_show;
void Start()
{
canvas = GetComponentInParent<Canvas>();
scaleFactor = canvas.scaleFactor;
var image = GetComponent<Image>();
material = new(image.material);
image.material = material;
material.SetColor("_Color", color);
material.SetFloat("_ColorRadius", radius);
material.SetFloat("_ScaleFactor", scaleFactor);
}
private void Update()
{
material.SetVector("_MousePos", Input.mousePosition);
if (scaleFactor != canvas.scaleFactor)
{
scaleFactor = canvas.scaleFactor;
material.SetFloat("_ScaleFactor", scaleFactor);
}
}
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
if (null != coroutine_show)
{
StopCoroutine(coroutine_show);
}
if (null != coroutine_hide)
{
StopCoroutine(coroutine_hide);
}
coroutine_show = StartCoroutine(ChangeInnerColorStateAsync(0.3f, true));
}
private IEnumerator ChangeInnerColorStateAsync(float duration, bool show)
{
#region Ease
static float inCubic(float t) => t * t * t;
static float outCubic(float t) => inCubic(t - 1f) + 1f;
#endregion
float time = 0;
float start = material.GetFloat("_ColorOffset");
float end = show ? 0 : 0.98f; // 当 _ColorOffset = 0 显示内部颜色
Func<float, float> ease = show ? outCubic : inCubic;
while (time < duration)
{
time += Time.deltaTime;
var p = ease(time / duration);
material.SetFloat("_ColorOffset", Mathf.Lerp(start, end, p));
yield return null;
}
}
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
{
if (null != coroutine_hide)
{
StopCoroutine(coroutine_hide);
}
coroutine_hide = StartCoroutine(HideInnerColorAsync());
}
private IEnumerator HideInnerColorAsync()
{
//等待ShowInnerColorAsync结束
if (null != coroutine_show)
{
yield return coroutine_show;
}
coroutine_hide = StartCoroutine(ChangeInnerColorStateAsync(0.1f, false));
}
}
2-4、材质球、材质、相机设置、UI设置、Post设置
(1)新建个材质球,使用HoverBtn这个Shader:
参数设置如下:
主要注意ColorRedius和Color两个参数。
(2)材质
右键另存为到本地,一个是背景一个是边框
(3)UI搭建
一个GridLayoutGroup父节点:
按钮设置:
可以修改按钮的颜色:
(4)Canvas渲染模式
(5)相机设置
相机增加Post Volume和Post Layer组件:
Post插件需要导入,设置Layer层,不做赘述。
整体搭建如下:
2-5、运行结果
三、参考链接
1、UI动效 01
2、UGUI 实现超赞 Win10 日历悬停效果
四、后记
总结一下就是:
(1)编写Shader
(2)编写C#脚本响应鼠标事件,传递鼠标位置
(3)其他效果设置
(4)运行起来
如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。
你的点赞就是对博主的支持,有问题记得留言:
博主主页有联系方式。
博主还有跟多宝藏文章等待你的发掘哦:
专栏 | 方向 | 简介 |
---|---|---|
Unity3D开发小游戏 | 小游戏开发教程 | 分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。 |
Unity3D从入门到进阶 | 入门 | 从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。 |
Unity3D之UGUI | UGUI | Unity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。 |
Unity3D之读取数据 | 文件读取 | 使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。 |
Unity3D之数据集合 | 数据集合 | 数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。 |
Unity3D之VR/AR(虚拟仿真)开发 | 虚拟仿真 | 总结博主工作常见的虚拟仿真需求进行案例讲解。 |
Unity3D之插件 | 插件 | 主要分享在Unity开发中用到的一些插件使用方法,插件介绍等 |
Unity3D之日常开发 | 日常记录 | 主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等 |
Unity3D之日常BUG | 日常记录 | 记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。 |