Unity工具——LightTransition(光照过渡)

news2024/11/19 9:24:58

需求描述

        在游戏中,开发者为了让玩家更直接地看到待拾取的物品从而为其添加一种闪烁效果,或者模拟现实中闪烁的灯光效果,我能够想到的一种方案则是通过控制光照强度来实现,那么本篇文章我们就尝试通过这个方案来实现一下,看看是否能够满足这类需求,这也能够作为我们个人资源库的一部分,以后如果在开发中有这方面需求,就可以直接把这个方案搬过来用,虽然这可能只是一个小小的功能,但这是一个积少成多的过程,只要我们能够完整地实现并使之通过测试,那么就算是一种成功,也能够从中获得收获。

功能描述

        如果大家了解过LightLight2D组件,那么就应该知道其中的intensity属性用于控制光照强度,本质上闪烁就是通过改变光照强度的值来控制的,从暗到明,则对应intensity的值从小到大。        

        首先我们需要两个属性——光照强度起始值beginValue光照强度最终值endValue,还有从beginValue到endValue的光照强度变化的步长值stepValue,同时还要确定等待时间的属性timeSpan。为了使得这个组件应用更广泛,我们不公开beginValue和endValue,而是让使用者明确光照强度的最小值minValue以及光照强度最大值maxValue,所以beginValue既可以是minValue也可以是maxValue,这取决于使用者的需求。

        其次就是需要明确从beginValue到endValue的过渡方式了,我首先能够想到的是是否需要循环闪烁,假定属性Loop用于明确是否循环闪烁,如果不循环闪烁,那么只要从beginValue过渡到endValue就结束,若需要循环闪烁则需要明确循环的方式,如果每次循环都是从beginValue过渡到endValue则可以假定这种方式叫BeginToEnd,除此之外还可以是先从beginValue过渡到endValue然后再过渡到beginValue,类似一个钟摆一样,假定这种方式叫Pendulum

        最后我们还可以规定beginValue的取值,我们规定它要么是minValue,要么是maxValue,只要确定了beginValue,就可以确定endValue,所以我们假定属性beginValueType用于明确光照强度起始值的类型。

属性解释
LightUnity的Light组件
Light2DUniversal RP中的Light2D组件
intensity

Light/Light2D组件的光照强度属性

beginValue光照强度起始值
endValue光照强度最终值
stepValue光照强度变化的步长值
timeSpan光照强度变化的时间间隔
minValue光照强度的最小值
maxValue光照强度的最大值
Loop是否循环闪烁
BeginToEnd每次循环都是从beginValue过渡到endValue
Pendulum先从beginValue过渡到endValue然后再过渡到beginValue,类似一个钟摆
beginValueType光照强度起始值的类型

代码展示(C#)

CommonLightTransition.cs

using System.Collections;
using UnityEngine;

namespace Tools
{
    public class CommonLightTransition : MonoBehaviour
    {
        [Header("必要属性")]
        [Tooltip("光照强度变化平均值,默认值为0.1(值>0)")]
        [SerializeField] private float IntensityStepValue = 0.1f;
        [Tooltip("光照强度最小值,默认值为1(值属于[0,IntensityMaxValue))")]
        [SerializeField] private float IntensityMinValue = 1;
        [Tooltip("光照强度最大值,默认值为2(值>IntensityMinValue)")]
        [SerializeField] private float IntensityMaxValue = 2;
        [Tooltip("过渡时间间隔,默认值为0.05,单位s(值>0)")]
        [SerializeField] private float TransitionTimeSpan = 0.05f;
        [Tooltip("是否开启光照过渡循环,默认值为true")]
        [SerializeField] private bool Loop = true;
        [Tooltip("光照过渡循环方式,需要勾选Loop该选项才有效,BeginToEnd是每次循环从光照过渡起始值开始,Pendulum是从光照过渡起始值至光照过渡最终值再过渡至光照过渡起始值(钟摆式过渡)")]
        [SerializeField] private LoopMode TheLoopMode;
        [Tooltip("光照过渡起始值类型,Min代表起始值从IntensityMinValue开始,Max同理")]
        [SerializeField] private BeginValueType TransitionBeginValueType;

        protected bool toMax;
        protected bool lockUnLoopTransition;
        protected int index;
        protected bool _isInit { get => isInit; }
        protected float[] _changedValues { get => changedValues; }
        protected BeginValueType _transitionBeginValueType { get => TransitionBeginValueType; }
        protected bool _loop { get => Loop; }
        protected LoopMode _loopMode { get => TheLoopMode; }
        protected float _intensityMinValue { get => IntensityMinValue; }
        protected float _intensityMaxValue { get => IntensityMaxValue; }

        private bool isInit, endTransition;
        private float[] changedValues;
        private IEnumerator coroutine;

        /// <summary>
        /// 启动过渡
        /// </summary>
        public void StartTransition()
        {
            if (isInit && endTransition)
            {
                StartCoroutine(coroutine);
                endTransition = false;
            }
        }

        /// <summary>
        /// 停止过渡
        /// </summary>
        public void StopTransition()
        {
            if (isInit && !endTransition)
            {
                StopCoroutine(coroutine);
                endTransition = true;
            }
        }

        /// <summary>
        /// 初始化游戏对象相关组件
        /// <para>返回值:初始化组件成功则返回true,否则返回false</para>
        /// </summary>
        protected virtual bool InitComponents() { return false; }

        /// <summary>
        /// 光照过渡类型处理
        /// </summary>
        protected virtual void TransitionTypeDeal() { }

        /// <summary>
        /// 非循环光照过渡
        /// </summary>
        protected virtual void UnLoopLightTransition() { }

        /// <summary>
        /// 循环光照过渡
        /// </summary>
        protected virtual void LoopLightTransition() { }

        /// <summary>
        /// 组件检测控制台提示方法
        /// <para>声明:该方法仅在Unity Editor模式下且当Inspector面板中组件属性发生更改时执行</para>
        /// </summary>
        protected virtual void ComponentLog() { }

        /// <summary>
        /// 光照过渡起始值类型
        /// </summary>
        protected enum BeginValueType
        {
            Min, Max
        }

        /// <summary>
        /// 循环光照过渡方式
        /// </summary>
        protected enum LoopMode
        {
            BeginToEnd, Pendulum
        }

        private void Start()
        {
            isInit = InitComponents() && InitParameters();
        }

        //初始化游戏对象相关参数
        private bool InitParameters()
        {
            if (IntensityStepValue <= 0 || IntensityMinValue < 0 || IntensityMaxValue <= 0) return false;
            if (IntensityMinValue >= IntensityMaxValue) return false;
            if (TransitionTimeSpan <= 0) return false;
            changedValues = NumberRange.FloatRange(IntensityMinValue, IntensityMaxValue, IntensityStepValue, true);
            if (changedValues == null && changedValues.Length == 0) return false;
            TransitionTypeDeal();
            coroutine = UnLoopTransition();
            if (Loop)
            {
                lockUnLoopTransition = true;
                coroutine = LoopTransition();
            }
            endTransition = true;
            return true;
        }

        private IEnumerator LoopTransition()
        {
            while (true)
            {
                LoopLightTransition();
                yield return new WaitForSeconds(TransitionTimeSpan);
            }
        }

        private IEnumerator UnLoopTransition()
        {
            while (!lockUnLoopTransition)
            {
                UnLoopLightTransition();
                yield return new WaitForSeconds(TransitionTimeSpan);
            }
            endTransition = true;
        }

#if UNITY_EDITOR
        private void OnValidate()
        {
            ComponentLog();
        }
#endif
    }
}

LightTransition2D.cs 

using UnityEngine.Experimental.Rendering.Universal;
using UnityEngine;

namespace Tools
{
    public class LightTransition2D : CommonLightTransition
    {
        [Header("必要组件(需要下载扩展包:Universal RP)")]
        [Tooltip("Light2D组件")]
        [SerializeField] private Light2D Light2D;

        private bool isLight2DLog;

        //初始化游戏对象相关组件
        protected sealed override bool InitComponents()
        {
            if (Light2D == null) return false;
            return true;
        }

        //光照过渡类型处理
        protected sealed override void TransitionTypeDeal()
        {
            if (_transitionBeginValueType == BeginValueType.Min)
            {
                Light2D.intensity = _intensityMinValue;
                index = 0;
                toMax = true;
            }
            else
            {
                Light2D.intensity = _intensityMaxValue;
                index = _changedValues.Length - 1;
                toMax = false;
            }
        }

        //非循环光照过渡
        protected sealed override void UnLoopLightTransition()
        {
            if (_isInit && !lockUnLoopTransition)
            {
                if (_transitionBeginValueType == BeginValueType.Min)
                {
                    if (index >= _changedValues.Length)
                    {
                        lockUnLoopTransition = true;
                        return;
                    }
                    Light2D.intensity = _changedValues[index++];
                }
                else
                {
                    if (index < 0)
                    {
                        lockUnLoopTransition = true;
                        return;
                    }
                    Light2D.intensity = _changedValues[index--];
                }
            }
        }

        //循环光照过渡
        protected sealed override void LoopLightTransition()
        {
            if (_isInit && _loop)
            {
                if (_loopMode == LoopMode.Pendulum)
                {
                    if (index <= 0) toMax = true;
                    else if (index >= _changedValues.Length - 1) toMax = false;
                }
                else if (index < 0 || index > _changedValues.Length - 1) TransitionTypeDeal();
                if (toMax) Light2D.intensity = _changedValues[index++];
                else Light2D.intensity = _changedValues[index--];
            }
        }

        //组件检测控制台提示方法
        protected sealed override void ComponentLog()
        {
            if (Light2D == null)
            {
                if (!isLight2DLog)
                {
                    Debug.LogWarning("The component \"<color=orange><b>Light2D</b></color>\" is null.");
                    isLight2DLog = true;
                }
            }
            else isLight2DLog = false;
        }
    }
}

LightTransition.cs 

using UnityEngine;

namespace Tools
{
    public class LightTransition : CommonLightTransition
    {
        [Header("必要组件")]
        [Tooltip("Light组件")]
        [SerializeField] private Light Light;

        private bool isLightLog;

        //初始化游戏对象相关组件
        protected sealed override bool InitComponents()
        {
            if (Light == null) return false;
            return true;
        }

        //光照过渡类型处理
        protected sealed override void TransitionTypeDeal()
        {
            if (_transitionBeginValueType == BeginValueType.Min)
            {
                Light.intensity = _intensityMinValue;
                index = 0;
                toMax = true;
            }
            else
            {
                Light.intensity = _intensityMaxValue;
                index = _changedValues.Length - 1;
                toMax = false;
            }
        }

        //非循环光照过渡
        protected sealed override void UnLoopLightTransition()
        {
            if (_isInit && !lockUnLoopTransition)
            {
                if (_transitionBeginValueType == BeginValueType.Min)
                {
                    if (index >= _changedValues.Length)
                    {
                        lockUnLoopTransition = true;
                        return;
                    }
                    Light.intensity = _changedValues[index++];
                }
                else
                {
                    if (index < 0)
                    {
                        lockUnLoopTransition = true;
                        return;
                    }
                    Light.intensity = _changedValues[index--];
                }
            }
        }

        //循环光照过渡
        protected sealed override void LoopLightTransition()
        {
            if (_isInit && _loop)
            {
                if (_loopMode == LoopMode.Pendulum)
                {
                    if (index <= 0) toMax = true;
                    else if (index >= _changedValues.Length - 1) toMax = false;
                }
                else if (index < 0 || index > _changedValues.Length - 1) TransitionTypeDeal();
                if (toMax) Light.intensity = _changedValues[index++];
                else Light.intensity = _changedValues[index--];
            }
        }

        //组件检测控制台提示方法
        protected sealed override void ComponentLog()
        {
            if (Light == null)
            {
                if (!isLightLog)
                {
                    Debug.LogWarning("The component \"<color=orange><b>Light</b></color>\" is null.");
                    isLightLog = true;
                }
            }
            else isLightLog = false;
        }
    }
}

NumberRange.cs 

using System.Collections.Generic;
using System.Linq;

namespace Tools
{
    /// <summary>
    /// 数值范围数组工具类
    /// </summary>
    public static class NumberRange
    {
        /// <summary>
        /// 获取指定范围内指定步长的Float数值数组
        /// <para>p_start:起始值</para>
        /// <para>p_end:终点值</para>
        /// <para>p_step:步长值</para>
        /// <para>[ContainsEnd]:是否包括终点值,默认为false</para>
        /// <para>返回值:Float[]</para>
        /// </summary>
        public static float[] FloatRange(float p_start, float p_end, float p_step, bool ContainsEnd = false)
        {
            if (!ContainsEnd) return DoFloatRange(p_start, p_end, p_step).ToArray();
            else
            {
                List<float> result = DoFloatRange(p_start, p_end, p_step).ToList();
                result.Add(p_end);
                return result.ToArray();
            }
        }

        static IEnumerable<float> DoFloatRange(float p_start, float p_end, float p_step)
        {
            for (float i = p_start; i <= p_end; i += p_step)
            {
                yield return i;
            }
        }
    }
}

界面展示

LightTransition2D

LightTransition

部分演示效果

LightTransition2D组件的简单演示效果

LightTransition2D - Demo

资源下载

GitHub_LightTransition     百度网盘

 如果这篇文章对你有帮助,请给作者点个赞吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1011414.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【面试题精讲】你了解String.intern方法吗

有的时候博客内容会有变动&#xff0c;首发博客是最新的&#xff0c;其他博客地址可能会未同步,认准https://blog.zysicyj.top 首发博客地址 系列文章地址 String.intern 方法是 Java 中的一个方法&#xff0c;「它用于将字符串对象添加到字符串常量池中&#xff0c;并返回常量…

浅谈C++|STL之算法函数篇

一.遍历常用算法 1.1for_each 在 C 中&#xff0c;for_each 是一个算法函数&#xff0c;位于 <algorithm> 头文件中。它接受一个范围&#xff08;容器或迭代器对&#xff09;以及一个函数对象&#xff08;函数指针、函数、lambda 表达式等&#xff09;&#xff0c;用于…

C#,《小白学程序》第二十四课:大数的阶乘(BigInteger Factorial)算法与源程序

1 文本格式 /// <summary> /// 《小白学程序》第二十四课&#xff1a;大数&#xff08;BigInteger&#xff09;的阶乘 /// 用于大数的阶乘算法&#xff08;原始算法&#xff09; /// </summary> /// <param name"a"></param> /// <retur…

【算法挨揍日记】day05——209. 长度最小的子数组、3. 无重复字符的最长子串

209. 长度最小的子数组 209. 长度最小的子数组 题目描述&#xff1a; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果不存在…

2023-9-14 最长上升子序列

题目链接&#xff1a;最长上升子序列 #include <iostream> #include <algorithm>using namespace std;const int N 1010;int n; int a[N]; int f[N];int main() {cin >> n;for(int i 1; i < n; i ) cin >> a[i];for(int i 1; i < n; i ){f[i]…

【物联网】简要介绍最小二乘法—C语言实现

最小二乘法是一种常用的数学方法&#xff0c;用于拟合数据和寻找最佳拟合曲线。它的目标是找到一个函数&#xff0c;使其在数据点上的误差平方和最小化。 文章目录 基本原理最小二乘法的求解应用举例使用C语言实现最小二乘法总结 基本原理 假设我们有一组数据点 ( x 1 , y 1 …

使用Jconsole监控JMX

使用Jconsole监控 Jconsole启动 直接本地启动jdk工具 本地连接 本地启动java应用直接点击就可以连接 本地远程连接 idea启动服务连接 配置运行配置 配置远程参数 -Djava.rmi.server.hostname127.0.0.1 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxrem…

【无公网IP内网穿透】 搭建Emby媒体库服务器并远程访问「家庭私人影院」

目录 1.前言 2. Emby网站搭建 2.1. Emby下载和安装 2.2 Emby网页测试 3. 本地网页发布 3.1 注册并安装cpolar内网穿透 3.2 Cpolar云端设置 3.3 Cpolar内网穿透本地设置 4.公网访问测试 5.结语 1.前言 在现代五花八门的网络应用场景中&#xff0c;观看视频绝对是主力…

多元函数微分学

目录 多元函数的极限 多元函数的连续性 连续的概念 连续函数的性质 多元函数微分学是微分学中的重要组成部分&#xff0c;也是解决许多实际问题的关键工具之一。它可以研究多个自变量和因变量之间的关系&#xff0c;以及这些关系的数学性质。 多元函数微分学的研究对象是多…

Vulkan入门——编译Shaderc

编译 Vulkan-Samples时&#xff0c;遇到了如下shaderc编译报错。 ninja: error: /Users/xiaxl/Library/Android/sdk/ndk/21.1.6352462/sources/third_party/shaderc/libs/c_static/armeabi-v7a/libshaderc.a, needed by ../../../../build/intermediates/cmake/debug/obj/arme…

酷开系统壁纸模式,用一幅名画打开艺术之门

中国文化博大精深&#xff0c;在源远流长的数千年里&#xff0c;沉淀下了太多的经典之作。特别是中国名画&#xff0c;历史悠久&#xff0c;源远流长。哪怕一个朝代&#xff0c;一历年间&#xff0c;一个画家&#xff0c;甚至一幅画&#xff0c;就能道出万万千千的姿态。它们记…

CRM系统如何帮助外贸企业发展

外贸企业竞争激烈&#xff0c;提高自身竞争力&#xff0c;扩大海外业务市场&#xff0c;是每个外贸企业的目标。为了实现这一目标&#xff0c;不少外贸企业借助CRM系统&#xff0c;优化业务流程&#xff0c;管理维护客户&#xff0c;从而实现可持续发展。那么&#xff0c;外贸企…

Stability AI推出Stable Audio;ChatGPT:推荐系统的颠覆者

&#x1f989; AI新闻 &#x1f680; Stability AI推出Stable Audio&#xff0c;用户可以生成个性化音乐片段 摘要&#xff1a;Stability AI公司发布了一款名为Stable Audio的工具&#xff0c;用户可以根据自己的文本内容自动生成音乐或音频。免费版可生成最长20秒音乐片段&a…

2023年8月知识复习

物联网总括 物联网即通过网络将客观事物进行智能化互联的网络系统。互联网主要是人与人、人与计算机之间的通信&#xff0c;而物联网主要是物联网设备之间的通信。 注意&#xff1a;数据上传到云端不是物联网的必要条件。物联网可以仅在本地网络和局域网中进行通信和数据处理…

27、Flink 的SQL之SELECT (SQL Hints 和 Joins)介绍及详细示例(2-1)

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

小型水库雨水情测报和大坝安全监测解决方案

一、建设背景 我国小型水库数量众多&#xff0c;大多由农村集体经济组织管理&#xff0c;灌溉、供水、防洪、生 态效益突出&#xff0c;是农业生产、农民生活、农村发展和区域防洪的重要基础设施&#xff0c;实施乡 村振兴战略和生态文明建设的重要支撑保障。由于小型水库工程存…

【webrtc】时间戳reordered 重新排序、环绕的判断

inter_frame_delay_.CalculateDelay( ) 计算传输抖动值 webrtc源码分析(6)- jitter delay计算详解 大神对这块的使用,内涵外延,有深入细致的讲解。输入rtp时间戳、到达时间(当前系统时间?)-- 在rtp的时间戳的处理上,inter_frame_delay_.CalculateDelay( ) 计算传输抖动值…

机器学习_个人笔记_周志华(停更中......)

第1章 绪论 1.1 引言 形成优秀的心理表征&#xff0c;自然能成为领域内的专家。 系统1 & 系统2。 机器学习&#xff1a;致力于研究如何通过计算的手段&#xff0c;利用经验来改善系统自身的性能。主要研究计算机从数据中产生model的算法&#xff0c;即“learning algori…

chatyoutube:AI快速整理影片内容

【产品介绍】 名称 chatyoutube 具体描述 Chat YouTube是一款基于ChatGPT与OpenAI的在线服务&#xff0c;其操作非常简单&#xff1a;只需提供一个YouTube视频链接&#xff0c;它就能根据视频内容回答您的问题、总结视频要点&#xff0c;甚至帮助您理解外语视频内容&#xff0c…

嵌入式学习笔记(32)S5PV210的向量中断控制器

6.6.1异常处理的2个阶段 可以将异常处理分为2个阶段来理解。第一个阶段是异常向量表跳转&#xff1b;第二个阶段是进入了真正的异常处理程序irq_handler之后的部分。 6.6.2回顾&#xff1a;中断处理的第一个阶段&#xff08;异常向量表跳转阶段&#xff09;处理 &#xff08;…