How to use Tweening for day/night cycle

spawn(function()
	while wait() do
		-- Tween to midnight
		local ToMidnight = TweenService:Create(Lighting, TweenInfo.new(60 * 5), {ClockTime = 0})
		
		ToMidnight.Completed:Wait()

		-- Tween to noon
		local ToNoon = TweenService:Create(Lighting, TweenInfo.new(60 * 5), {ClockTime = 12})
		
		ToNoon.Completed:Wait()
	end
end)

At the moment, nothing happens. Script is definitely running tho. I’ve tried just doing

local Speed = 0.05

spawn(function()
	while wait() do
		Lighting:SetMinutesAfterMidnight(Lighting:GetMinutesAfterMidnight() + Speed)
	end
end)

Which while yes, works, it makes the movement of the sun jittery, and thus the shadows don’t move smoothly, even with the speed being set really low. If I set the speed any lower, it’d take hours for a single day cycle to complete

5 Likes

Do not use wait().
Use RunService:BindToRenderStep() (or .Heartbeat:Wait()/:Connect())

If you want it to look the best on all machines, sync the time periodically with clients, and then the clients can manage the Day/Night cycle locally so that it is smooth.

1 Like

When a client joins tho they’ll be given a different time to others tho?

This does not matter, the server can use server time to keep track of the time, and the client can ask for that time (or recieve it periodically) so that they can offset it themselves.

On client:

on every frame

  • minutes = minutes + deltaTime * speed
  • SetMinutesAfterMidnight(minutes)

Where the offset is given by the server as the current MinutesAfterMidnight
When you recieve it, you do

  • minutes = serverMins

Another way is
start = time()
on every frame

  • SetMinutesAfterMidnight((time() - start) * speed + offset)

And when getting the value from the server

  • start = time()
  • offset = serverMins

So if the servers time is 12:00:00 and stays at that on the server, and only the client changes the time, then let’s say client 1 is at 18:00:00, and now another players joins, they get given the server time of 12:00:00

The server will silently keep track of the minutes or just calculate them when a client needs them

First try one try to see if it really works, then you can add the second tween and third add the whole thing in a loop. Try step by step, don’t rush if it doesn’t work. So, I write from Mobile, but I would try it like this now:

local TweenService = game:GetService("TweenService")
game.Lighthing.ClockTime = 0
local Tween = TweenService:Create(game.Lighthing, TweenInfo.new(1.440), {ClockTime = game.Lighthing.ClockTime + 12})
while true do
    Tween:Play()
    Tween.Completed:Wait()
end
1 Like
while wait() do
		-- Tween to midnight
		local ToMidnight = TweenService:Create(Lighting, TweenInfo.new(60 * 5), {ClockTime = 0})
		
		ToMidnight.Completed:Wait()

		-- Tween to noon
		local ToNoon = TweenService:Create(Lighting, TweenInfo.new(60 * 5), {ClockTime = 12})
		
		ToNoon.Completed:Wait()
	end

You’re never actually :Play()ing the tween. Also while wait() do is bad practice.

Use am best:

while true do
    wait()
end

Second, why use a while loop in a spawn unction?

2 Likes

Wow I’m a pleb.

Problem though, having time set to 12 (midday by default) and then tweening to 0 (midnight) tweens down instead of up.

Also, what should I use instead of while wait() do?

Pls read above @NinjoOnline, i already say it

I already answered that before as well

These fire at faster intervals

That makes no difference… if anything it’s just adding an extra line of code, which is a waste

1 Like

For larger game this will make a difference, The While-Wait-Do Idiom, by cntkillme: Addressing while wait() loops

1 Like

Then @NinjoOnline you never defined Lighting, if you not have a variable with this then write game.Lighting, and not simply so Lighting. See above, hope my code will help

ClockTime uses 24 hour formatting if that helps. I personally just use something similar to your code snippet. But I use the return value (the delta time, aka the actual time waited) of wait to make it look more smooth.

local Lighting = game:GetService("Lighting")

local minutes = 0

while true do
    Lighting:SetMinutesAfterMidnight(minutes)
    minutes = minutes + wait(1) -- or whatever time
end
why the while wait() do idiom bad

In your case use

while true do
    -- ...
    wait(n)

It’s clearer.

It clearly indicates that the condition is true and that the loop is infinite because there is no terminating condition.
The while wait() do idiom only works because of a hack; a trick. It abuses the fact that wait returns a number, and numbers are truthy values in Lua. Which has led to tons of bad habits. Such as forgetting about the power of the conditional field of while loop.

while wait(n) do
    if terminating_condition then
        break
        --[[
            Don't get me wrong,
            breaks aren't bad but using them over the conditional part isn't good. That's what it's for.
        ]]--
    end
end

-- vs

while not terminating_condition do
    wait(n)
end

-- So use the latter. It clearly indicates what the condition is.

And you are essentially throwing away that value which would have helped with the second code snippet you provided.

Finally, the idiom is misleading. It implies that the return value of wait is important, when really it isn’t.

There’s also that very unlikely chance that wait might be updated to return false or nil which will break your loops.

That still causes issues with the sun/moon visibly jumping (even if it’s a small jump)

I want a constant, smooth movement.

I already told you that if you want a “smooth movement” then do not use wait() at all.

I’m not…

spawn(function()

	while true do
	    Lighting:SetMinutesAfterMidnight(minutes)
	    minutes = minutes + wait(1)
	end
end)

“I’m not”
Why are you doing wait(1 ) then…???
It will wait for ~1 second to update each time.