【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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM