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

目标

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

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

实现思路

思路

要模拟昼夜变化,先要实现“游戏内的时间系统”。基于Time.deltaTime,做一些变换即可模拟时间的推移。
模拟出了时间的推移,接下来,只需在特定时间段内,平滑地调整场景灯光的色彩、明暗即可。这里的灯光使用的是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光照变化,很多其他细节没有考虑进去。如果有更复杂的需求,则需要改动。

补充

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

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值