【unity2D】场景光照随昼夜交替而变化-实现思路1

目标

在饥荒(Don't Starve)和泰拉瑞亚(Terraria)里,游戏场景的明暗会随着时间推移、昼夜交替而产生变化。今天试图初步实现这个机制


实现思路


思路

  1. 要模拟昼夜变化,先要实现“游戏内的时间系统”。基于Time.deltaTime,做一些变换即可模拟时间的推移。
  2. 模拟出了时间的推移,接下来,只需在特定时间段内,平滑地调整场景灯光的色彩、明暗即可。这里的灯光使用的是Light2D。

代码片段

与UnityEvent相结合的时间系统:

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; [System.Serializable] public class TimeEvent : UnityEvent <float> {} public class TimeTest : MonoBehaviour { public float timeInGame;//游戏内的时间,以秒为单位 private float timer; public int period;//周期,以秒为单位 [Header("Hours per second")]//游戏里每秒对应现实中多少小时 private float perSec; [Header("Seconds per hour")]//现实中每小时是游戏里多少秒 private float perHour; public float startTime;//最初的时间,以小时为单位 public List<float> timePoints = new List<float>(); public List<TimeEvent> timeEvents = new List<TimeEvent>(); public int currentPos = 0;//用于获取timePoints和timeEvents中的元素,本来是要写if-else的 void Start() { perSec = 24f / period ;//计算游戏里一秒对应现实里多少小时 perHour = period / 24f;//计算现实里一小时对应游戏里多少秒 timer = startTime * (period / 24f) ;//注意startTime以小时为单位 InitializeTimePoint(); } void Update() { TimeFlow(); SendTimeEvent(); } private void TimeFlow()//计时并循环 { timer += Time.deltaTime; if(timer < period)//为了避免timeInGame超出所设周期,在此做判断 { timeInGame = timer; } else//游戏内时间到了下一天 { timeInGame = 0f;//重置游戏内时间 timer= 0f;//重置计时器 currentPos = 0;//重置i } } private void SendTimeEvent() { if(currentPos < timePoints.Count && timeInGame > timePoints[currentPos]) { timeEvents[currentPos].Invoke(perHour); currentPos++; } } private void InitializeTimePoint()//要添加元素就在这里加 { timePoints.Add(5f * perHour);//传入时间 timePoints.Add(17f * perHour); } }

2D全局光照:这里的代码是从Light2D-学习记录3中的修改而来

using System.Collections.Generic; using UnityEngine; using UnityEngine.Experimental.Rendering.Universal; public class GlobalLightTest : MonoBehaviour { private Light2D light2D; private float timer = 0f; private List<Color> myColors = new List<Color>();//定义一个泛型集合,存储线性插值经过的端点的值 public List<Color> colors = new List<Color>();//在方法里对这个集合进行操作,以避免更改myColors中的元素 private bool boolSwitch; private float flowTime;//用于保存从Event中传入的过渡时间 int currentPos = 0; void Start() { light2D = GetComponent<Light2D>(); AddPoint(); InitializeColor(); } void Update() { if(boolSwitch == true) { ColorFlow(flowTime); } } private float Timer(float _second)//传入过渡时间 { float _mult = 1 / _second; if(timer <= 1f) { timer += Time.deltaTime * _mult; } return timer; } public void ColorFlow(float _second)//按照colors内的元素顺序变化 { flowTime = _second; if(boolSwitch == false)//在调用一次这个方法后,该方法就能在update里持续进行 boolSwitch = true; if(timer <= 1f && currentPos < colors.Count - 1 )//这里判断i是因为:在i超过长度后,要使插值停止工作 { light2D.color = Color.Lerp(colors[currentPos] ,colors[currentPos+1] , Timer(_second)); } else if(currentPos < colors.Count - 1)//如果还有得变 { currentPos++;//继续往后变 timer = 0f;//重置计时器 } else { currentPos = 0;//重置i timer = 0f;//重置计时器 RearrangeColor(); boolSwitch = false;//结束“持续调用这个方法” } } private void RearrangeColor()//颠倒colors里的元素顺序 { List<Color> _tempColors = new List<Color>(); for(int i = 0 ;i < myColors.Count ;i++) { _tempColors.Add(colors[myColors.Count - 1 - i]); } colors.Clear(); for(int i = 0 ;i < myColors.Count ;i++) { colors.Add(_tempColors[i]); } } private void InitializeColor() { for(int i = 0 ;i < myColors.Count ;i++) { colors.Add(myColors[i]); } } private void AddPoint()//在此函数内添加端点 { myColors.Add(new Color(70f/255 , 50f/255 , 20f/255)); myColors.Add(new Color(1f , 170f/255 , 70f/255)); myColors.Add(new Color(1f , 1f , 1f)); } }

在Inspector窗口里手动注册事件。


不足

这段代码是为了快速实现2D光照变化,很多其他细节没有考虑进去。如果有更复杂的需求,则需要改动。


补充

  1. 将UnityEvent和“游戏内的时间系统”相结合,优点是:几乎任何“到了特定时间即调用”的方法,都可以看作是“订阅者”,把“游戏内的时间系统”看作是“发布者”。
  2. 然而缺点也很明显:当委托的数量越来越多时,难以管理且运行效率不高。(UnityEvent的运行效率比C#Event的运行效率慢一些)

最终效果

为了方便测试,我把游戏内的一天定为30秒,即这个动图有30秒。
image

如果人物有“手电筒”一类的装备,则效果如下图:
image


__EOF__

本文作者OtusScops
本文链接https://www.cnblogs.com/OtusScops/p/14727854.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   AshScops  阅读(1090)  评论(1编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示