需求描述
在游戏中,开发者为了让玩家更直接地看到待拾取的物品从而为其添加一种闪烁效果,或者模拟现实中闪烁的灯光效果,我能够想到的一种方案则是通过控制光照强度来实现,那么本篇文章我们就尝试通过这个方案来实现一下,看看是否能够满足这类需求,这也能够作为我们个人资源库的一部分,以后如果在开发中有这方面需求,就可以直接把这个方案搬过来用,虽然这可能只是一个小小的功能,但这是一个积少成多的过程,只要我们能够完整地实现并使之通过测试,那么就算是一种成功,也能够从中获得收获。
功能描述
如果大家了解过Light和Light2D组件,那么就应该知道其中的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用于明确光照强度起始值的类型。
属性 | 解释 |
Light | Unity的Light组件 |
Light2D | Universal 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组件的简单演示效果
LightTransition2D - Demo
资源下载
GitHub_LightTransition 百度网盘
如果这篇文章对你有帮助,请给作者点个赞吧!