Day/Night Cycle
Configurable day/night cycle with directional light rotation, color gradient, and time-of-day event triggers.
How to Use
1
Add a Directional Light to your scene (it acts as the sun)
2
Attach DayNightCycle to an empty manager object
3
Assign the directional light
4
Set day length in real-time minutes (default: 10)
5
Hook OnDawn/OnDusk events for NPC schedules, streetlights, etc.
6
Display time: dayNight.FormattedTime gives 'HH:MM'
7
Control: SetTime(6f) to skip to dawn, SetPaused(true) to freeze
Source Code
C#
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// Day/night cycle controller. Rotates directional light,
/// adjusts ambient color, and fires time-of-day events.
/// </summary>
public class DayNightCycle : MonoBehaviour
{
[Header("Time")]
[SerializeField] [Range(0f, 24f)] private float timeOfDay = 12f;
[SerializeField] private float dayLengthMinutes = 10f;
[SerializeField] private bool isPaused = false;
[Header("Sun")]
[SerializeField] private Light directionalLight;
[SerializeField] private Gradient sunColor;
[SerializeField] [Range(0f, 2f)] private float maxIntensity = 1.2f;
[Header("Ambient")]
[SerializeField] private Gradient ambientColor;
[Header("Events")]
public UnityEvent OnDawn; // 6:00
public UnityEvent OnNoon; // 12:00
public UnityEvent OnDusk; // 18:00
public UnityEvent OnMidnight; // 0:00
/// <summary>Current time of day (0-24).</summary>
public float CurrentTime => timeOfDay;
/// <summary>Normalized time (0-1).</summary>
public float NormalizedTime => timeOfDay / 24f;
/// <summary>Is it currently daytime (6-18)?</summary>
public bool IsDaytime => timeOfDay >= 6f && timeOfDay < 18f;
/// <summary>Formatted time string (HH:MM).</summary>
public string FormattedTime
{
get
{
int hours = Mathf.FloorToInt(timeOfDay);
int minutes = Mathf.FloorToInt((timeOfDay % 1f) * 60f);
return string.Format("{0:00}:{1:00}", hours, minutes);
}
}
private float lastEventHour = -1f;
private void Start()
{
if (directionalLight == null)
directionalLight = RenderSettings.sun;
SetupDefaultGradients();
}
private void Update()
{
if (isPaused) return;
// Advance time
float hoursPerSecond = 24f / (dayLengthMinutes * 60f);
timeOfDay += hoursPerSecond * Time.deltaTime;
if (timeOfDay >= 24f)
timeOfDay -= 24f;
UpdateLighting();
CheckTimeEvents();
}
private void UpdateLighting()
{
float t = NormalizedTime;
// Rotate sun
if (directionalLight != null)
{
float sunAngle = (t * 360f) - 90f;
directionalLight.transform.rotation = Quaternion.Euler(sunAngle, 170f, 0f);
// Sun color and intensity
directionalLight.color = sunColor.Evaluate(t);
float intensity = 0f;
if (timeOfDay >= 6f && timeOfDay <= 18f)
{
float dayProgress = (timeOfDay - 6f) / 12f;
intensity = Mathf.Sin(dayProgress * Mathf.PI) * maxIntensity;
}
directionalLight.intensity = intensity;
}
// Ambient color
RenderSettings.ambientLight = ambientColor.Evaluate(t);
}
private void CheckTimeEvents()
{
int currentHour = Mathf.FloorToInt(timeOfDay);
if (currentHour == lastEventHour) return;
lastEventHour = currentHour;
switch (currentHour)
{
case 0: OnMidnight?.Invoke(); break;
case 6: OnDawn?.Invoke(); break;
case 12: OnNoon?.Invoke(); break;
case 18: OnDusk?.Invoke(); break;
}
}
private void SetupDefaultGradients()
{
if (sunColor.colorKeys.Length <= 1)
{
sunColor = new Gradient();
GradientColorKey[] keys = new GradientColorKey[4];
keys[0] = new GradientColorKey(new Color(0.1f, 0.1f, 0.3f), 0f);
keys[1] = new GradientColorKey(new Color(1f, 0.7f, 0.4f), 0.25f);
keys[2] = new GradientColorKey(new Color(1f, 0.95f, 0.9f), 0.5f);
keys[3] = new GradientColorKey(new Color(1f, 0.5f, 0.3f), 0.75f);
sunColor.SetKeys(keys, new GradientAlphaKey[] {
new GradientAlphaKey(1f, 0f), new GradientAlphaKey(1f, 1f)
});
}
}
/// <summary>Set time of day directly (0-24).</summary>
public void SetTime(float hours)
{
timeOfDay = Mathf.Repeat(hours, 24f);
UpdateLighting();
}
/// <summary>Pause/unpause the cycle.</summary>
public void SetPaused(bool paused) => isPaused = paused;
/// <summary>Set day length in real-time minutes.</summary>
public void SetDayLength(float minutes) => dayLengthMinutes = minutes;
}