自定义Unity组件——LightTransition(光照过渡)

需求描述

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

功能描述

        如果大家了解过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组件的简单演示效果

自定义Unity组件LightTransition(2D)

LightTransition组件的简单演示效果 

自定义Unity组件LightTransition(3D)

资源下载

GitHub_LightTransition     百度网盘

免责声明:由于本文内容未经过正规和严格的测试,可能存在错误,因此造成的损失均由使用者自行承担,对本文内容复制、下载、参考等引用行为即默认悉知并同意该声明。 

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值