昼夜交替系统(切换灯光)
升级URP
通过切换URP的全局光的效果来实现昼夜切换,然后通过定义一个专门的数据结构来记录光源在不同时刻的光照强度,根据不同的时刻来触发切换数据即可。
从Package Manager
中寻找URP包,然后进行安装,安装完成后,直接在项目新建一个URP的渲染管线Rendering->URP Asset(with 2d Render)/ 2DRender
, 由于是2.5D游戏,因此需要将渲染模式修改为Custom Axis
然后再项目设置里修改默认的渲染管线为当前管线
然后将项目的素材升级到URPWindow->Rendering->Render Pipeline Convert
,
先勾选所有材质然后初始化Initialize Convert
,然后进行转化
直接在Hierarchy
中新建需要的光源Light->Global Light
,其中Target Sorting Layer
可以选择该光源能够照亮的内容; Instensity
光照强度
升级了URP以后,粒子系统也需要进行更新,在粒子系统的Render
中,将材质从Default-ParticleSystem
修改为Sprite-lit-default
(需要取消隐藏才能显示),这样粒子系统才能在全局光照下(夜间颜色才统一);
添加点光源的流程也是一样的。
通过一个SO文件来控制所有灯光的具体数值,然后每个灯光有一个管理的脚本LightController
,用于切换所有灯光数值内容,然后一个管理整个场景的灯光的脚本LightManager
,用于在切换场景后更新所有场景中的光源
灯光具体数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
[CreateAssetMenu(fileName = "LightPattenList_SO", menuName = "Light/LightPatten")]
public class LightPattenList_SO : ScriptableObject
{
public List<LightDetails> lightDetailsList;
/// <summary>
/// 通过当前所处的季节和时间段,得到当前灯光的具体数据
/// </summary>
/// <param name="season"></param>
/// <param name="lightShift"></param>
/// <returns></returns>
public LightDetails GetLightDetails(Season season, LightShift lightShift)
{
return lightDetailsList.Find(l => season == l.season && lightShift == l.lightShift);
}
}
[System.Serializable]
public class LightDetails
{
public Color lightColor;
public float lightIntensity;
public Season season;
public LightShift lightShift;
}
|
控制灯光组件
灯光组件Light2D的组件所处于using UnityEngine.Rendering.Universal
;组件中
控制日出灯光
设定日出和日落对应的时间,在Settings.cs
中,使用TimeSpan
来设定一个时间戳记录日出和日落时间,然后在TimeManager
中每度过一分钟,计算一次当前的灯光。同时为了避免灯光突然切换,设置一个灯光切换的持续时间。
1
2
3
4
5
|
// Settings中
/* 昼夜系统相关内容 */
public static TimeSpan morningTime = new TimeSpan(5, 0, 0);
public static TimeSpan nightTime = new TimeSpan(19, 0, 0);
public const float LightChangeDuration = 15f; /* 灯光的切换时间(现实时间) */
|
然后在TimeManager
中设置昼夜切换的事件,根据游戏时间获得当前的切换状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
private LightShift GetCurrentLightShift()
{
if ((GameTime >= Settings.morningTime && GameTime <= Settings.nightTime))
{
_timeDifference = (float)(GameTime - Settings.morningTime).TotalMinutes;
return LightShift.Morning;
}
else
{
_timeDifference = Mathf.Abs((float)((GameTime - Settings.nightTime)).TotalMinutes);
return LightShift.Night;
}
return LightShift.Morning;
}
|
并且在游戏一开始,以及每一分钟都呼叫一次昼夜切换相关的事件
1
|
EventHandler.CallLightShiftChangeEvent(_currentSeason, GetCurrentLightShift(), _timeDifference);
|
对于LightManager
来说,只需要保证每次被呼叫昼夜切换事件和切换场景的时候,更新当前场景的所有内容即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
private void OnLightShiftChangeEvent(Season currentSeason, LightShift currentLightShift, float timeDifference)
{
_currentSeason = currentSeason;
_currentLightShift = currentLightShift;
_timeDifferent = timeDifference;
foreach (LightController lightController in _sceneLightControllers)
{
/* 依次调用所有灯光的切换算法 */
lightController.ChangeLightShift(_currentSeason, _currentLightShift, _timeDifferent);
}
}
private void OnAfterSceneLoadEvent()
{
_sceneLightControllers = FindObjectsOfType<LightController>();
foreach (LightController lightController in _sceneLightControllers)
{
/* 依次调用所有灯光的切换算法 */
lightController.ChangeLightShift(_currentSeason, _currentLightShift, _timeDifferent);
}
}
|
在LightController.cs
中具体执行灯光的切换方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
/* 改变灯光的方法 */
public void ChangeLightShift(Season currentSeason, LightShift currentLightShift, float timeDifference)
{
_targetLightDetails = currentLightData.GetLightDetails(currentSeason, currentLightShift);
/* 设定光照数值 */
float gameTimeDuration = Settings.LightChangeDuration * (Settings.GameTimeThreshold * 60);
if (timeDifference < Settings.LightChangeDuration)
{
// 此时timeDifference是日出或日落时间与当前时间的差距分钟数
Color colorOffset = (_targetLightDetails.lightColor - _currentLight2D.color) /
Settings.LightChangeDuration * timeDifference; /* 计算从日出日落标准时间和目前的差值 */
_currentLight2D.color += colorOffset; /* 获得当前时间差下对应的初始天色 */
/* 使用DOTween插件缓慢切换灯光 */
DOTween.To(() => _currentLight2D.color, c => _currentLight2D.color = c, _targetLightDetails.lightColor,
gameTimeDuration - timeDifference);
DOTween.To(() => _currentLight2D.intensity, i => _currentLight2D.intensity = i, _targetLightDetails.lightIntensity,
gameTimeDuration - timeDifference);
}
else
{
_currentLight2D.color = _targetLightDetails.lightColor;
_currentLight2D.intensity = _targetLightDetails.lightIntensity;
}
}
|
1
|
float gameTimeDuration = Settings.LightChangeDuration * (Settings.GameTimeThreshold * 60);
|
由于DOTween
的切换速度是根据现实时间来设置的,而游戏时间流速更快,此处代码是为了将我们预计的游戏中昼夜切换时间转化为现实的具体时间