Set DirectionalLight Rotation for Day/Night per Time

I have two directional lights - ie. Sun & Moon - and depending on two main variables I need to set their initial rotations: the length of time for a game day (in real world minutes) and the time of day (gameDay) that the scene is started at. I can’t figure out how to set the initial rotation to Sun/Moon (directional light/lens flare really) to coordinate with the time of gameDay. There are only two Rotates in my code, the first in Update does this:

sunmoon [cnt].Rotate (new Vector3 (-degreeRotation, 0, 0) * Time.deltaTime);

degreeRotation is determined by: 360 / fullGameDayInRLSeconds;
While testing, I set a very short fullGameDay of 2 (RL minutes) so fullGameDayinRLSeconds is 120, so my degreeRotation is 3. I think this part is ok as once the lights are put into rotation they move constantly at this rate. It’s the setting of their intitial rotation based on the time of day that I’m stumbling over.

During testing, I set the StartHour @ 14 (2:00pm) for a RL 2 minute gameday. I take 120 (fullGameDayinRLSeconds) / 24 (hours per day) = 5; so every 5 seconds an hour passes in-game. So 2:00pm (or the 14th hour) is 70. Out of my 120 second day, I’m at mark 70. I take that and multiply it by my degreeRotation (3) and get 210 and set that

sunmoon [0].Rotate (new Vector3 (startRot, 0, 0))

But right away I’m a little confused as I was thinking 0 to 180 would be “day”, and the “Sun” is coming over the horizon when I hit play so my math/logic is off.

My terrain is at y 0, rotation 0,0,0
The two directional lights are also at y 0

Ok, let’s get some math going…

let’s implement the case where sunset and dusk is cut out from the nighttime, so we get longer days like in minecraft. here’s a sketch:

day/night cycle

For ease of calculation we keep TimeOfDay between 0.0 and 1.0 - 0.0 is the start of day when the sun is directly at the horizont, 0.25 is midday, at 0.5 the sun touches the horizont again and the other half of the day begins - the half that includes dusk/nighttime/sunset.

And while we’re at it, let’s also calculate a sky color by blending between colors assigned to daytime, midday, nighttime.

I’m also creating a moon and sun object, a floor and the lights in this example code, so you can drop it onto a Cube representing the player in an empty scene to test it.
The radius is the distance of the sun/moon from the player and is kept small here, so the positions can be inspected while testing without the need for a first person controller or the like to look around.

Users of Unity Free will only see one light/shadow while testing this script - they need to disable one of the lights to see the other take effect.

I’m attaching the lights to the moon/sun, which is not really needed as the position of directional lights don’t matter.

using UnityEngine;
using System.Collections;

public class DayNight : MonoBehaviour {

	void Start()
	{
		// Creating everything needed to demonstrate this from a single cube
		player = this.transform;
		GameObject floor = GameObject.CreatePrimitive(PrimitiveType.Plane);
		floor.transform.position = player.position + 0.5f*Vector3.down;
		sun = GameObject.CreatePrimitive(PrimitiveType.Sphere);
		sun.name = "sun";
		sun.renderer.material.color=Color.yellow;
		sun.AddComponent<Light>().type = LightType.Directional;
		sun.light.shadows = LightShadows.Hard;
		sun.light.color = new Color(1,1,0.5f);
		sun.renderer.castShadows = false;
		moon = GameObject.CreatePrimitive(PrimitiveType.Sphere);
		moon.name = "moon";
		moon.renderer.material.color=new Color(0.75f,0.75f,0.75f);
		moon.AddComponent<Light>().type = LightType.Directional;
		moon.light.shadows = LightShadows.Hard;
		moon.light.color = new Color(0.5f,0.5f,0.5f);
		moon.light.intensity = 0.1f;
		moon.renderer.castShadows = false;
	}

	public Transform player;

	public GameObject sun;
	public GameObject moon;

	public float radius = 6;

	public Color daytimeSkyColor = new Color(0.31f, 0.88f, 1f);
	public Color middaySkyColor = new Color(0.58f, 0.88f, 1f);
	public Color nighttimeSkyColor = new Color(0.04f, 0.19f, 0.27f);

	// implementing minecraft PC defaults
	public const float daytimeRLSeconds   = 10.0f * 60;
	public const float duskRLSeconds      =  1.5f * 60;
	public const float nighttimeRLSeconds =  7.0f * 60;
	public const float sunsetRLSeconds    =  1.5f * 60;
	public const float gameDayRLSeconds = daytimeRLSeconds + duskRLSeconds + nighttimeRLSeconds + sunsetRLSeconds;
	
	public const float startOfDaytime = 0;
	public const float startOfDusk = daytimeRLSeconds / gameDayRLSeconds;
	public const float startOfNighttime = startOfDusk + duskRLSeconds / gameDayRLSeconds;
	public const float startOfSunset = startOfNighttime + nighttimeRLSeconds / gameDayRLSeconds;


	private float timeRT = 0;
	public float TimeOfDay // game time 0 .. 1
	{
		get { return timeRT/gameDayRLSeconds; }
		set { timeRT = value*gameDayRLSeconds; }
	}
	
	void Update () {
		timeRT = (timeRT+Time.deltaTime) % gameDayRLSeconds;
		Camera.main.backgroundColor = CalculateSkyColor();
		float sunangle = TimeOfDay * 360;
		float moonangle = TimeOfDay * 360 + 180;
		Vector3 midpoint = player.position; midpoint.y -= 0.5f; //midpoint = playerposition at floor height
		sun.transform.position = midpoint + Quaternion.Euler(0,0,sunangle)*(radius*Vector3.right);
		sun.transform.LookAt(midpoint);
		moon.transform.position = midpoint + Quaternion.Euler(0,0,moonangle)*(radius*Vector3.right);
		moon.transform.LookAt(midpoint);
	}

	Color CalculateSkyColor()
	{
		float time = TimeOfDay;
		if (time <= 0.25f)
			return Color.Lerp(daytimeSkyColor, middaySkyColor, time/0.25f);
		if (time <= 0.5f)
			return Color.Lerp(middaySkyColor, daytimeSkyColor, (time-0.25f)/0.25f);
		if (time <= startOfNighttime)
			return Color.Lerp(daytimeSkyColor, nighttimeSkyColor, (time-startOfDusk)/(startOfNighttime-startOfDusk));
		if (time <= startOfSunset) return nighttimeSkyColor;
		return Color.Lerp(nighttimeSkyColor, daytimeSkyColor, (time-startOfSunset)/(1.0f-startOfSunset));
	}

	void OnGUI()
	{
		Rect rect = new Rect(10, 10, 120, 20);
		GUI.Label(rect, "time: " + TimeOfDay); rect.y+=20;
		GUI.Label(rect, "timeRT: " + timeRT);
		rect = new Rect(120, 10, 200, 10);
		TimeOfDay = GUI.HorizontalSlider(rect, TimeOfDay, 0, 1);
	}
}

Hello,

I know this answer is really old now, but I’m wondering if I could get help with this script.

I tried this script and think the sun rotation and such works really good. However, even if I set different lengths for Day, dusk, night, sunset it stills splits night (or well dusk, night and sunset) into half of the whole time.

I tried doing this

    public const float daytimeRLSeconds = 1.0f * 60;
    public const float duskRLSeconds = 0.1f * 60;
    public const float nighttimeRLSeconds = 0.3f * 60;
    public const float sunsetRLSeconds = 0.1f * 60;
    public const float gameDayRLSeconds = daytimeRLSeconds + duskRLSeconds + nighttimeRLSeconds + sunsetRLSeconds;

Which from what I count should mean that day is 60 seconds long and the SUN should therefor be visible for 60 seconds and then the sun I assume should move faster during dusk, night and sunset since those together is 30 seconds. However, at the moment the script just splits the time in half and make day be 45 seconds and dusk, night and sunset 45 seconds.

I did remove the player thing since I assumed that was just for if I had no player controller in the level to see how the script would work.

This is my Update

    private void Update()
    {
        timeRT = (timeRT + Time.deltaTime) % gameDayRLSeconds;
        float sunangle = TimeOfDay * 360;
        float moonangle = TimeOfDay * 360 + 180;
        Vector3 midpoint = new Vector3(0, 0, 0);
        _lSunLight.transform.position = midpoint + Quaternion.Euler(0, 0, sunangle) * (fRadius * Vector3.right);
        _lSunLight.transform.LookAt(midpoint);
    }

I also added a short film to show what happens for me.

https://www.dropbox.com/s/bovu5bv80agdxhr/TimeOfDayProblem.avi?dl=0

Does anyone know how to fix this issue so I could have longer days then nights?