当前位置: 首页 > article >正文

【Unity3D小功能】Unity3D中实现超炫按钮悬停效果

推荐阅读

  • CSDN主页
  • GitHub开源地址
  • Unity3D插件分享
  • QQ群:398291828
  • 小红书
  • 小破站

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新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之UGUIUGUIUnity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据文件读取使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合数据集合数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发虚拟仿真总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件插件主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发日常记录主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG日常记录记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。

http://www.kler.cn/a/535937.html

相关文章:

  • 【分布式理论六】分布式调用(4):服务间的远程调用(RPC)
  • 本地化部署deepseek r1,包含web部署
  • C_位运算符及其在单片机寄存器的操作
  • 在Debian 12上安装VNC服务器
  • python3中错误与异常初识
  • PostgreSQL证书什么样子的?
  • Golang 并发机制-6:掌握优雅的错误处理艺术
  • SQL中Limit的用法详解
  • DeePseek结合PS!批量处理图片的方法教程
  • 【react】react面试题
  • JavaWeb开发学习笔记--MySQL
  • JavaScript的 switch 方法
  • 通过STM32实现外设控制应用案例
  • Postman简介
  • 【机器学习案列】糖尿病风险可视化及预测
  • Spring Boot中使用MockMvc测试PATCH请求控制器
  • ubuntu下迁移docker文件夹
  • 深入解析:Jsoup 库的多功能应用场景
  • Python分享20个Excel自动化脚本
  • 数据库------------
  • 开源 GPU 集群管理器 GPUStack 轻松拉起deepseek各版本模型
  • RabbitMQ深度探索:创建消息队列
  • 【梦想终会实现】Linux驱动学习4
  • Nginx与frp结合实现局域网和公网的双重https服务
  • 网站打开提示不安全
  • 深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换(二)