【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 {}

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 timePoints = new List();
public List timeEvents = new List();
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 myColors = new List();//定义一个泛型集合,存储线性插值经过的端点的值

public List colors = new List();//在方法里对这个集合进行操作,以避免更改myColors中的元素

private bool boolSwitch;

private float flowTime;//用于保存从Event中传入的过渡时间

int currentPos = 0;

void Start()
{
light2D = GetComponent();

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 _tempColors = new List();

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

posted @ 2021-05-04 11:00  AshScops  阅读(1435)  评论(1)    收藏  举报