4
\$\begingroup\$

I'm trying to make a smooth day/night cycle for a game project that I've been working on. I've already set up a clock that adds deltaTime to seconds, wraps seconds to gameMinutes, etc. The issue that I'm having is that I want a signed normalized float to represent day (+1) and night (-1), and I'm not exactly sure what the most effective way of doing that is.

Here's my time class in pseudocode:

class GameTime {

    float snDayNight; //signed normalized

    float seconds;
    int gameMinutes;
    int gameHours;
    int gameDays;
    int gameMonths;

    //Omitted consts used for wrapping (SEC_PER_MIN, HR_PER_DAY, etc.)..

    //Per-frame update..
    public void Update(){
        seconds += Timer.GetDeltaTime();
        WrapTimeValues (); //Wraps sec to min, min to hr, etc.
        UpdateDayNightCycle ();
    }

    private void UpdateDayNightCycle(){
        //?????
    }

    //...//
}

So, my GameTime has been tested and seems to be working fine; when the value in seconds is >= the value of SEC_PER_MIN it adds another minute and stores the leftover fraction of a second. It then goes on to check minutes, hours, days, etc.

I've been a little bit stuck at UpdateDayNightCycle() though. I want to be able to convert my time into a smooth decimal value where -1 represents absolute night and 1 represents absolute day. I've normalized values between 0~1 and -1~1 before, but I've never tried to use a normalized value to represent a repeating/periodic value like night~day before..

I tried drawing a simple graph where time of day is on the x-axis and daynight value is on the y-axis. What I get is essentially a graph of a triangle wave. Sadly my math skills are a little bit underdeveloped and even after looking into formulas for triangle waves, I've been having trouble implementing it in my code..

So, am I even on the right track? How can I smoothly convert my game world's time into a single decimal value between -1 and 1?

\$\endgroup\$
  • 1
    \$\begingroup\$ If something is not clear then please let me know. \$\endgroup\$ – wolfdawn Sep 29 '14 at 4:27
  • \$\begingroup\$ btw, I would do a more thorough transition: google.com/… google.co.il/… google.co.il/… google.co.il/search?q=night+skies The transition between those... \$\endgroup\$ – wolfdawn Sep 29 '14 at 5:44
  • 1
    \$\begingroup\$ My idea was to use the normalized day/night value elsewhere as a parameter for various effects; light color, light direction, intensity, etc. \$\endgroup\$ – MrKatSwordfish Sep 29 '14 at 7:10
  • 1
    \$\begingroup\$ Hmm, does your game's time implement leap seconds, leap years or Daylight Saving Time? Human-readable time isn't even monotonically increasing; it sometimes moves backwards. There are an incredible number of subtleties here. \$\endgroup\$ – Anko Sep 29 '14 at 11:58
  • 1
    \$\begingroup\$ You track days and months - do you care about seasonal effects? What about latitude? \$\endgroup\$ – corsiKa Sep 29 '14 at 16:22
9
\$\begingroup\$

tl;dr

Func 1 on Wolfram Alpha:

1 - 2 * |(x mod 2) - 1|

Or in your specific case:

1 - 2 * |((time % entireDay) - halfDay) / halfDay|

You can even use a sinus wave instead (much more pretty).

sin(x - pi/2)

Sin Wave on Wolfram Alpha

Or in your specific case:

sin (- pi / 2 + 2 * pi * time / entireDay);

Long tedious explaination in fine detail:

If in military time: 00:00 (midnight) is -1, 12:00 (noon) is 1 and 23:59 is again ~-1 then we are measuring distance from noon (how long ago was noon or how long until it's noon again?). Then negating the value and subtracting one.

To measure distance in 1d we use the abs operator: |7 - 5| = |3 - 5| = 2(because seven and three are the save distance from 5.

float hours = gameHours + gameMinutes / 60.0 + seconds / 3600.0;//<time in hours with fracation>

distanceFromNoon = |hours - 12.0|; // Equals 12 at most (so we"ll divide by 6)

""normalized distance"" = distanceFromNoon / 6.0;

result = 1 - ""normalized distance"";

or

""normalized time"" = -2 * |hours - 12| + 1;

where | | means the absolute value of... This is normally accomplished with Math.abs();

So you get:

result = -2 * Math.abs(gameHours * 3600 + gameMinutes * 60 + seconds - 12 * 3600) / (3600) + 1;
\$\endgroup\$
  • \$\begingroup\$ Works like a charm, thanks for the detailed answer! Just out of curiosity, how different would this be if the scale was from 0 (midnight) to 1 (noon)? Am I correct in understanding that you divided 'distanceFromNoon' by 6 (halfday) to get a normalized range from 0-2, then shifted that down to a -1 to 1 range by subtracting nDistance from 1? \$\endgroup\$ – MrKatSwordfish Sep 29 '14 at 5:21
  • 1
    \$\begingroup\$ That is exact. So if you divided by 12 and than did a 1 - result you'd get noon = 1 and midnight = 0. You'd essentially get distance from midnight. \$\endgroup\$ – wolfdawn Sep 29 '14 at 5:32
  • \$\begingroup\$ @MrKatSwordfish Great, now I just need to keep my "helpfulness score" static for a few days. I really like triple 7. ;) \$\endgroup\$ – wolfdawn Sep 29 '14 at 5:45
  • \$\begingroup\$ I'll make sure not to upvote that comment! \$\endgroup\$ – MrKatSwordfish Sep 29 '14 at 6:59
  • 2
    \$\begingroup\$ @Zehelvion I'm sorry. But your answer is too good to redact my upvote ;) \$\endgroup\$ – bummzack Sep 29 '14 at 13:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.