Real Sun Path | Realistic Sun Movement and Longer/Shorter Days!

Wanna enjoy truly long summer days, or short, dark winters? Real Sun Path will bring its beauty!

• Introduction
Here’s a quick astronomy lesson! We all know how the days are longer in the summer, and shorter in the winter. This is because the Earth is tilted 23.4 degrees. As the Earth orbits the Sun, its tilt direction remains the same relative to the stars, but faces either away or toward the Sun during the course of the orbit, causing variations in the amount of sunlight different parts of the Earth receive.

The result is a gradual lengthening and shortening of days, with the most significant changes occurring during the solstices and equinoxes. Furthermore, the path and the angle the Sun rises varies with latitude.

The daylength variation becomes increasingly pronounced at higher latitudes, eventually making some parts of the year receive 24 hours of Sun and 24 hours of darkness, which amazingly is what this script allows you to do! Below is an illustration of how the daylength is different at different times of the year.

By default, Roblox’s latitude is essentially strictly set at the Equator, with the Sun rising/setting vertically, and with the default GeographicLatitude acting like an axial tilt setting, which does nothing to increase the variation of daylength.

Below is a rough sketch of how the Lighting service handles the Sun’s movement.

Real Sun Path is a script that calculates the Sun’s realistic position in the sky, based on the Time of Day, Day of Year, Latitude, and Axial Tilt. . It is calculated using known equations used in calculating the Sun’s position, and is expressed as (x, y, z) coordinates that would be obtained from :GetSunDirection(), and then converted into ClockTime and GeographicLatitude values in the Lighting property.

Here’s a demo on how it works!

*Please note that the Sunrise colour visuals seen in the video and the thumbnail are produced by 3D Atmosphere, a script that complements Real Sun Path!

3D Atmosphere Script

3D Atmosphere - Roblox

• How to implement
Its very easy to implement! Just open the model, and drag the main script to Workspace and that’s it!

There are other extra features included in the package, such as a Game Clock GUI, along with a Sun graph that shows the time the Sun rises and sets, its elevation, azimuth, and twilight phase, and a day/night cycle script that is specialized for this script!

IMPORTANT: If you are going to use a day/night cycle script, adjust the time, or change the latitude, please use the ClockTime property under this script!

The script uses the lighting’s ClockTime and GeographicLatitude as coordinates and should not be used! That will defeat the purpose of this script!

• Properties
You may wonder what these properties do, but here is an explanation of what each property does:

  • Analemma
    Sun’s figure-eight movement at a fixed TimeOfDay due to Earth’s elliptical orbit. (Refer to thumbnail)

  • Axial Tilt
    Determines how far north, or south the Sun will rise/set. Earth is 23.437 degrees.

  • ClockTime
    The time of day in the game, local solar time (Middle of ideal timezone)

  • DayOfYear
    The number of days from January 1st. Determines how long or short the days are.
    Vernal Equinox: 79 → Equal daylength as night.
    Summer Solstice: 171 → Longest day of the year.
    Autumnal Equinox: 266 → Equal daylength as night.
    Winter Solstice: 354 → Shortest day of the year.

  • GeographicLatitude
    The vertical location from Earth’s equator. Determines how extreme the daylength variation is, thought the year. Set 0 degrees for the Equator, 90 for the north pole, and -90 for the south pole.
    Any latitude above 66.563 or below -66.563 will experience 24 hours of Sun, or 24 hours of night at parts of the year, assuming AxialTilt is set at 23.437.

  • Refraction
    Due to this phenomenon, the atmosphere may cause the Sun to set a few minutes later than the actual sunset. Thats why you may notice at the equinoxes, the daylength is actually 12 hours and 7 minutes, instead of a solid 12 hours.
    KNOWN BUG: The Sun may jitter out of place during the Midnight Sun. If you’re at the poles, I suggest disabling this feature.

Extra Features

• Game Clock GUI
The game clock GUI provides you a clock that players can view in the game. To implement, you just drag it into StarterGui.

The time shown is based on ideal timezones, 24 even slices of the Earth that are 15 degrees latitude apart, therefore the time displayed will not always match the ClockTime property, especially when the DST setting is on, and advancing the display time by 1 hour.

You can manually offset the time, in hours, which is useful if your game takes place outside its ideal timezone such as Madrid, where it lies on the GMT timezone, but uses CET.

The GUI also features a Sun graph, along with information above it, for those who enjoy observing the Sun’s movement!

Below is the changing latitude, from -90 to 90 degrees, at Summer Solstice, at 10:00 AM, and Real Sun Path in action!
RobloxRealSunPathSolargraph

• Day/Night Cycle Script
Included is a specialized day/night cycle script, which is completely different from the ones you will find out there, or using the :SetMinutesAfterMidnight() method, or changing the ClockTime in the Lighting service.

To reiterate, the script uses the lighting’s ClockTime and GeographicLatitude as COORDINATES and should not be used! That will defeat the purpose of this script!

This script instead changes the ClockTime value under the Real Sun Path script, which fires every change in the property. To set up, leave in Workspace!

There are three properties under the script.

  • AdvanceDayOfYear: Makes the day of the year change every 24 hours elapsed, which is how it should be! Set it to false if you want the game to be solstice, or a specific day forever!

  • Enabled: If true, the script will cycle through day and night as it should, and if not, it will disable the day/night cycle.

  • Speed: Sets the speed of the day/night cycle. Example: 60 means 60 seconds elapsed per REAL seconds, or 60x real time. A day per second would mean a value of 86,400, and an hour per second would mean a value of 3600.

If you have any questions, concerns, or feedback, feel free to share below!

163 Likes

this is what happens when people have too much free time

( jk that system looks really cool and good job for making it

7 Likes

This looks really cool, and could actually be very useful for developers trying to realistically create changing seasons

3 Likes

This looks amazing! I’ve always wanted a script that can make sunrises & sunsets much more realistic!

1 Like

Converted into a module script if anyone is interested

MODULE SCRIPT:

--[[

	REAL SUN PATH: ENJOY LONGER (OR SHORTER) DAYS ON ROBLOX!

	This is a script that calculates the real position of the Sun based on
	latitude and the time of the year.

	** DO NOT USE THE LIGHTING CLOCKTIME AND GEOGRAPHIC LATITUDE! **

	(Use the settings under this script as the new ClockTime and GeographicLatitude)

	The script uses these as "coordinates" of the Sun. Why? The Roblox
	GeographicLatitude is more of an axial tilt, than a latitude, which in
	reality, the latitude is ALWAYS fixed at the equator. You might've
	noticed how the Sun rises and sets vertically, which at latitude, is 
	absolutely not the case.

	To set up, just leave this script in Workspace and that's it!

	If you wish to have the time set client-sidedly, simply copy the contents of this
	script into a LocalScript, including the settings under this script, and place the
	LocalScript in StarterPlayerScripts or ReplicatedFirst. This is great for integrating
	with 3D Atmosphere, another script written by me. You can look into it by searching up
	"3D Atmosphere".

	------------------------------------------------------------------------------------------------------------
	** Settings: YOU MUST SET THEM ON THE SERVER, WHEN THE GAME RUNS! **

	- Analemma: Set this to true to account for the phenomenon known as analemma.
				If you were to capture the Sun at the same time everyday in a year, the Sun would trace
				a figure-8 in the sky.
	- Axial tilt: The tilt of the planet. (Earth is 23.44 degrees by default.)
				  The higher the tilt, the more variation of day length over the course of the year
	- ClockTime: The current time of day (solar time), in hours.
	- DayOfYear: The current day number of the year. (Eg: Summer solstice is on June 20 which is day 171.)
	- GeographicLatitude: The latitude of your game. Higher latitudes mean more extreme day lengths.
	- Refraction: Makes the Sun rise earlier than it supposed to be (or later than it actually sets)
				  by 2 minutes. (Known bug: This may cause the Sun to 'skip' during the midnight Sun.)

	For more accurate results, please set the Sun's apparent diameter to  1.44 on a skybox. Yes the Sun
	might look 'unrealistically' small to you, but it is as real as it looks.

	Also, if you want to add a day/night cycle, on another script, you will need to while loop over
	the ClockTime value under this script, NOT the game.Lighting.ClockTime property.


	Made by BangoutBoy a.k.a. Pulsarnova
	Modified by Hff321

]]

local module = {
	Analemma = true,
	AxialTilt = 23.437,
	ClockTime = 7,
	DayOfYear = 171.121,
	GeographicLatitude = 43.653,
	Refraction = true
}


local function getTime(x, y, z, L, tx, ty)
	-- Determines where in the day a particular Sun Direction is, due
	-- to the periodic nature of trigonometry.
	if x > 0 and y < 0 then -- Wee hours
		if ty ~= ty then
			return math.abs(tx), "Wee hours"
		end
		return math.abs(ty), "Wee hours"
	elseif (math.round(x) == 0 and math.round(y) == 0 and x > 0
		and y > 0 and tx ~= tx and math.round(ty) == -6) then -- Sunrise
		return 6, "Sunrise"
	elseif x > 0 and y > 0 then -- Morning
		if ty ~= ty then
			return 12 + tx, L, "Morning"
		end
		return math.abs(ty), "Morning"
	elseif x < 0 and y > 0 then -- Afternoon
		local outputTime = 24 + ty
		if outputTime == outputTime then
			return outputTime, "Afternoon"
		end
		-- Noon
		return 12, "Noon"
	elseif (math.round(x) == 0 and math.round(y) == 0 and x < 0
		and y < 0 and math.round(tx) == 6 and math.round(ty) == -6) then -- Sunset
		return 18, "Sunset"
	elseif x < 0 and y < 0 then  -- Evening
		local outputTime = 24 + ty
		if outputTime ~= outputTime then
			return 24 - tx, "Evening"
		end
		return outputTime, "Evening"
	elseif math.round(tx) == 0 and ty ~= ty then -- Noon or Midnight
		if math.round(x) == 0 and y < 0 then -- Midnight
			return 0, "Midnight"
		elseif math.round(x) == 0 and y > 0 then -- Noon
			return 12, "Noon"
		end
	end
	return 0, "Midnight"
end


-- Warning: Math
local function ConvertSunDirectionInToClockTimeAndGeographicLatitude(sunDir)
	-- Basically the reverse of game.Lighting:GetSunDirection()
	-- Converts a Sun Direction into lighting ClockTime and GeographicLatitude

	-- Known bugs: The converter loses a lot of accuracy at latitudes closer at
	-- -90 to -89 degrees or 89 degrees to 90 degrees.

	local x = sunDir.X
	local y = sunDir.Y
	local z = sunDir.Z
	local L = math.asin(z)
	local tx = ((12 * math.acos(x / math.cos(L))) / math.pi) - 6
	local ty = ((12 * math.acos(y / math.cos(L))) / math.pi) - 12

	local resultTime, timePeriod = getTime(x, y, z, L, tx, ty)

	return resultTime, L
end


local function setSunPosition()
	local dayOfYear = module.DayOfYear
	local yearLength = 365.242181
	local earthEccentricity = 0.01671022
	local axialTilt = math.clamp(module.AxialTilt, -90, 90)

	-- Equation of Time (minutes): Over the course of the year, the Sun can be either ahead of
	-- the solar noon, or behind. This offset creates the analemma effect. (If set to true.)
	local B = 2 * math.pi * (dayOfYear - 1) / 365.242181
	local equationOfTime = (229.18 * (0.000075 + 0.001868 * math.cos(B) - 0.032077 * math.sin(B) 
		- 0.014615 * math.cos(2*B) - 0.040849 * math.sin(2*B))) / 60

	local K = math.pi / 12
	local latitude = math.clamp(module.GeographicLatitude, -90, 90)
	local season = -math.cos(((2 * math.pi) / yearLength) * (dayOfYear + 11.5))
	local seasonAxial = math.cos(math.rad(axialTilt * season))
	local seasonAxialSine = math.sin(math.rad(axialTilt * season))
	local seasonLatitude = math.cos(math.rad(latitude))
	local seasonLatitudeReversed = math.cos(math.rad(90 - latitude))
	local lateralOffset = (math.sqrt(2) * math.sin(math.rad(axialTilt))
		* math.sin((math.pi / 180) * (latitude - 45)))

	if module.Analemma == false then
		equationOfTime = 0
	end

	local t0 = (module.ClockTime + equationOfTime) % 24
	local x = -seasonAxial * math.sin(K * t0)
	local y = (-(seasonAxial * seasonLatitude) * math.cos(K * t0)
		+ (seasonAxialSine * seasonLatitude) + (season * lateralOffset))
	local z = ((seasonAxial * seasonLatitudeReversed) * math.cos(K * t0)
		+ (seasonAxialSine * seasonLatitudeReversed) - (season * lateralOffset))

	if module.Refraction then
		-- Atmospheric refraction, the Sun either rose 2 minutes earlier than it did, or
		-- 2 minutes later than it actually set.

		local sunElevation = math.deg(math.atan(y / math.sqrt(x ^ 2 + z ^ 2)))
		local sunRefraction = math.clamp((1.02 / (3600 * math.tan(
			math.rad(sunElevation + (10.3 / (sunElevation + 5.11)))))), 0, 0.05)
		y += sunRefraction * math.cos(math.rad(sunElevation))
	end

	local t, L = ConvertSunDirectionInToClockTimeAndGeographicLatitude(Vector3.new(x, y, z))

	game.Lighting:SetMinutesAfterMidnight(1440 - t * 60)
	game.Lighting.GeographicLatitude = -math.deg(L) + 23.44
end



-- Set position of the Sun at the start of the game.
setSunPosition()


-- Create a proxy table that will track modifications
local proxyTable = newproxy(true)
local proxyMetatable = getmetatable(proxyTable)

-- Define the __index metamethod to access the return table
proxyMetatable.__index = module

-- Define the __newindex metamethod to detect modifications to the return table
proxyMetatable.__newindex = function(t, key, value)
	module[key] = value
	setSunPosition()
end

-- Return the proxy table as the module's return value
return proxyTable

SERVER SCRIPT:

local MyModule = require(script["Real Sun Path"])

local Enabled = true
local Speed = 1
local AdvanceDayOfYear = true


spawn(function()
	while true do
		local waitTime = task.wait()
		if Enabled then
			MyModule.ClockTime += (Speed / 3600) * waitTime
			if AdvanceDayOfYear then
				MyModule.DayOfYear += (Speed / 86400) * waitTime
			end
		end
	end
end)
8 Likes

Amazing mod! I would probably try to maintain the customizability but its amazing how now people don’t have to copy the formulae again and again for different scripts. Also hope you enjoy the script as a whole!

1 Like

How can i make it so the day of year changes in my game automatically?

for example:
Its July 14 and the day of year changes in game to 195(July 14)
And When the next day comes it changes to 196(July 15) automatically

1 Like

You will wanna use the given Day/Night cycle script and check the AdvanceDayOfYear value to be true, which should be checked by default.

1 Like

i have AdvanceDayOfYear value true but it always starts in june 20 when i join the game. i want the DayOfYear to change automatic depending on the day of year we are in like in Space Sailors.

Change the NumberValue called DayOfYear under the main script, the Real Sun Path script.

1 Like

Nevermind. Sorry for asking but what is the name of the music in space sailors When you land on the moon?

Hi! I’ve created a very cool radio communicator that has support for modular apps and such and decided to adapt the real sun path clock GUI to add support for your module.

However, I’m not able to write a function that returns the degrees from 0 to 360 from sun height and azimuth for the daily sun path you see in the picture, I want the sun to be at 0 degrees for sunrise and 180 for sunset, do you know how I would be able to accomplish that?

I was also wondering if you are going to update your module to provide values for the moon too, as that would be very cool :slight_smile:

3 Likes

This looks cool but does it have weather/climate system?

1 Like

I don’t clearly understand fully what you’re trying to do, but I do know that the Sun doesn’t rise at 0 (North), and set at 180 (South). It should rise at more or less 90 degrees (Depending on the day of year), and set at more or less 270 degrees, assuming the day of the year is set at the equinoxes. I assume you’re trying to offset the display degrees, which you can simply do simple addition to the current azimuth value.

And for the Sun height, -90 is directly below horizon (nadir), 0 if its at the horizon, and +90 if directly overhead (zenith). It doesn’t go all the way to 360.

For the Moon, Roblox currently has a fixed Moon phase of a Full Moon, so the best way is to make one separate for the Moon but you will need to mimic the positioning system of the Sun for an artificial BasePart Moon, then apply the Real Sun Path formulae with some modifications. It will be a lot of work given the current Roblox features.

Also nice GUI work you made! Can’t wait to see that astronomy app you’re making!

1 Like

Hi! It’s a work in progress, but you can already try the whole device here:

At the end of the app list, there should be an app called “Suntimes”, that’s what I’m working on now!
I am currently trying to make an accurate sun path on the front page (here highlighted in red)
immagine
The sun icon in the position above is rotated by 0 degrees (sunrise), 90 degrees would be overhead/12pm and 180° would be sunset, do you know how I could achieve this based on variables used in the sungraph script? :slight_smile:

You’re free to try out everything I made up to this point, I am still sorting out the chart a bit, you may have seen the Sensors app, that uses some of your formulas along with others (humidity, etc. and EMF, which is fictional and calculated based on near parts with scripts)

Everything’s open source, and you can find more details here: 🌡️ Sensors | Indirecta

1 Like

God help my soul this looks like seal team 6 briefing.

A simple 180 degree diagram should be relatively easy so basically lets say you’re living at a high enough latitude where you get about 8 hours of daylight in the winter, and 16 hours in the summer. For every hour that passes, you move the Sun on the diagram about 180 deg / 8 hours = 22.5 deg / hour. And for the summer, you do the same thing: 180 deg / 16 hours = 11.25 deg / hour. So 180 degrees will be the constant, and you’re treating the length of day on the denominator as the variable.

Also looks like you wanna keep 12pm as solar noon, so you will have to shift when you want the diagram changes occur. So in my example, you begin moving the diagram at 8am, and for the summer, you start at 4am.

2 Likes

Thank you! I was able to get it working with this function

function calculate_sundial_rotation(daylength, t)
			-- Convert time t to be within the range of the day (0 to daylength)
			local rotation;
			t = t % daylength

			-- Calculate sundial rotation
			if t < daylength / 2 then
				rotation = (t / (daylength / 2)) * 90  -- Rotates from 0 to 90 degrees
			else
				rotation = 90 + ((t - daylength / 2) / (daylength / 2)) * 90  -- Rotates from 90 to 180 degrees
			end

			return rotation
		end

Do you plan on adding moon stuff, and phases to your script? :slight_smile:

EDIT:
This seems to actually be more precise, however, there’s still some offset between actual sunset and 180°.

local function calculateRotationAngle(t, dayLength)
	t %= 24
	local anglePerUnitTime = 180 / dayLength
	local rotationAngle = (t - sunriseTime) * anglePerUnitTime
	return rotationAngle
end
2 Likes

Moon phases would be far fetched, especially I tend to work this script alongside 3D Atmosphere, which involves a mesh that blocks FogEnds to create an atmosphere. Hope your script works now, sorry for the late reply but yeah. If Roblox has a feature where parts can appear behind another part, I will definitely add realistic Moon phases too!

1 Like

Hello
I am doing a small game, where you will be able to fly in space
I have an invisible Part which represents the Sun and I would like to position the Sun Skybox texture “behind” the Sun Part when the player looks at the Sun Part (i.e. at the place on the skybox where the direction vector between the player and the Sun Part “hits” the skybox).
I care only about the X and Z position. I would like the “height” of the Sun texture to remain more or less same all the time (around 15-16 o’clock)
So I wonder how can I set (and update) the correct Sun texture position in game using the Geographical Latitude setting?

Thanks in advance

1 Like