intermediate Utilities

Day/Night Cycle

Configurable day/night cycle with directional light rotation, color gradient, and time-of-day event triggers.

Unity 2022.3+ · 2.6 KB · DayNightCycle.cs

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

DayNightCycle.cs
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;
}