2
\$\begingroup\$

Here's my code for a day/night cycle. I have it set up so that when time (Statistics.duration) is the right number, the light levels switch. It works great, but I'm at a loss for how to make it work indefinitely - as an actual cycle. How can I make this code work for all numbers up to 10,000? I know there has to be an easy way to do this, but I'm at a loss...

public void updateTime()
{
    if (!dawn && (Statistics.duration <= 50 ) && Dungeon.depth <= 100){
        dawn = true;
        night = false;
        Dungeon.nightMode = false;
        GLog.i("It is now dawn.");
    }

    if (!noon && Statistics.duration > 50 && Statistics.duration <= 100 && Dungeon.depth <= 100){
       noon = true;
       dawn = false;
        GLog.i("It is now noon.");
    }

    if (!dusk && Statistics.duration > 100 && Statistics.duration <= 150 && Dungeon.depth <= 100){
        dusk = true;
        noon = false;
        GLog.i("It is now dusk.");
    }

    if (!night && Statistics.duration > 150 && Statistics.duration <= 200 && Dungeon.depth <= 100){
        night = true;
        dusk = false;
        GLog.i("It is now night.");
    }

    if (!dawn && Statistics.duration > 200 && Statistics.duration <= 250 && Dungeon.depth <= 100){
        dawn = true;
        night = false;
        GLog.i("It is now dawn.");
    }
}

I'm calling updateTime() whenever the player does something - moving, fighting, sleeping, etc. They perform the action, which gets added to the .duration.

\$\endgroup\$
2
  • \$\begingroup\$ Are you calling updateTime() from within your central game loop? \$\endgroup\$
    – Engineer
    Mar 24, 2016 at 20:29
  • \$\begingroup\$ @OP you should visit this page to recover your account; this will allow you to edit the question and comment the question and the answers. \$\endgroup\$
    – Vaillancourt
    Mar 24, 2016 at 20:37

5 Answers 5

8
\$\begingroup\$

Modular arithmetic can help you here. % is the modulus operator in Java, and the expression X % Y returns the remainder of the division operation X / Y. This is useful for making numbers "wrap around."

Based on your sample code, a full cycle of your day is 200 units of Statistics.duration. The first 50 are the day portion, the second 50 are the noon portion, the next 50 are dusk, the last 50 are night.

Thus, Statistics.duration % 200 will give you a value between 0 and 199. If that value is less than 50, you're in the day time. Between 50 and 100, dusk. And so on. Your code can then become something like...

public void updateTime()
{
    int timeOfDay= Statistics.duration % 200;
    if (timeOfDay <= 50){
        // it's dawn...
    } else if (timeOfDay > 50 && timeOfDay <= 100) {
        // it's noon...
    } else if (timeOfDay > 100 && timeOfDay <= 150) {
        // it's dusk...
    } else {
        // it's night...
    }
}

You can wrap that entire block in your check for dungeon level as well as whatever else you might need.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ If branching can be avoided in the main game loop you can end up with a nice performance boost depending on branch prediction. Additionally branching increases complexity because each condition is a possible point of failure. This problem can be resolved using basic mathematics in a much cleaner manner. \$\endgroup\$ Mar 29, 2016 at 15:52
  • 1
    \$\begingroup\$ @user3730788 can you point why this performance boost would exist without branching? \$\endgroup\$
    – MVCDS
    Apr 3, 2016 at 12:59
  • \$\begingroup\$ This really doesn't seem to be a section of code that will be incredibly dependent on such fine tuned optimization to worry about branch prediction misses. It only happens once per frame. Don't make code more complex or uglier for minuscule performance. This isn't a critical section of code in the physics simulation or rendering pipeline. \$\endgroup\$
    – user5665
    Apr 3, 2016 at 13:22
  • \$\begingroup\$ I am advocating a cleaner/simpler solution that also happens to not have branching. I do advocate avoiding branching as much as possible because it can waste cycles. We are viewing a small portion of code here, but it is very likely there is more branching than just this one instance; ounces make pounds. \$\endgroup\$ Apr 3, 2016 at 13:31
  • \$\begingroup\$ I like your sinusoidal solution but it doesn't fit his initial question, he has boolean flags that affect his world state, if his solution was purely a lighting/rendering issue your solution would prove valuable but it seems he may be using the flags he is for other aspects of the world state. People also prefer simple solutions they understand even if it means slower performance, he may not be as comfortable with the notation you've used in your answer to express mathematical ideas. People in these parts love their terse code snippets. \$\endgroup\$
    – user5665
    Apr 3, 2016 at 13:45
0
\$\begingroup\$

Josh Petrie is right.

Futhermore, you could use an enum to avoid using if else if.

public enum DayTime {

    DAWN(0), NOON(1), DUSK(2), NIGHT(3);

    private int n;

    DayTime(int n) {
        this.n = n;
    }

    public static DayTime valueOf(int n) {
        for (DayTime value : values()) {
            int i = value.n;
            if (n == i) {
            return value;
            }
        }
        return null;
    }

}



public void updateTime() {
    int dayTimeIndex = Statistics.duration % 200 / 50;
    DayTime dayTime = DayTime.valueOf(dayTimeIndex); 
...
}
\$\endgroup\$
4
  • \$\begingroup\$ This introduces additional function call overhead in addition to modulus operator to an operation that is presumably being performed frequently. This will result in unnecessarily slower execution. \$\endgroup\$ Mar 29, 2016 at 19:13
  • 2
    \$\begingroup\$ @user3730788 don't optimise prematurely \$\endgroup\$
    – Miles Rout
    Mar 30, 2016 at 2:50
  • \$\begingroup\$ It is more than simply optimization. It introduces granularity and reduces complexity which should lead to easier readability and maintainability. \$\endgroup\$ Mar 31, 2016 at 15:00
  • \$\begingroup\$ While this answer does seem to be an unnecessary level of abstraction that interrupts readability, I really doubt the OP will ever care about the actual performance impact of function call overhead. \$\endgroup\$
    – user5665
    Apr 3, 2016 at 13:27
0
\$\begingroup\$

The easiest thing you can do is, for example, % the actual time.

actualtime %= MAX_TIME;

Very easy method!

\$\endgroup\$
0
\$\begingroup\$

My first thought

If I understood correclty, you wants it to repeat the time as in a cycle, so you have a "class" which handles time by using modulus.

//I don't know how to write it on Java
class TimeHandler = {
   var current = 0;
   const max = 200;

   update() => {
      // it will make "current" eventually become 0 again
      current = (1 + current) % max;//it does not use "++" because some languages do not guarantee the order of operations
   }

   getPeriod() => {
     return PeriodFabric.for(current, max);
   }
}

The fabric return the right period for a given time

class PeriodFabric = {
   const periods = [DAWN, NOON, DUSK, NIGHT];

   for(time, max) {
     var qt = max / periods.length;
     var range = Math.floor(time / qt);
     return periods[range];
   }
}

This period can be forward to whatever classes need it, as the dungeon.

class Dungeon = {
   var nightMode = null;

   update(period) {
      nightMode = periord === NIGHT && depth > 100;
   }
}

Separating like this has the advantage of separing concers

  • you could have multiple cycles which are concerned to time (as day/night, seasons, etc) or even better modify TimeHandler to make it generic.
  • you can create more periods without modifying the TimeHandler
  • you can pass periods forward to classes which behave based on it, as I did on the dungeon, so the responsabily to declare its nightMode lives there

My friend's suggestion

I talked this answer to a friend, he's suggested me that instead of returning the period, classes would subscribe to "PeriodFabric" which would notify them when the period had changed, passing the new period to its subscrivers.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ You are creating an even more complicated system. A side note, please do not encourage people to use the pre and post increments inside of expressions like that. While the C# language does guarantee the order of operations many other languages do not (c++, c). Moving the action of incrementing the variable before or after the expression as necessary can make the code easier to read and will leave you with less problems in the case mentioned prior. \$\endgroup\$ Apr 3, 2016 at 13:21
  • \$\begingroup\$ Thanks, I think you just explained why my friend had a bug while it was working on my console. \$\endgroup\$
    – MVCDS
    Apr 3, 2016 at 13:32
-1
\$\begingroup\$

You probably want to use the sine function.

Suppose you have the time of day t for a 24 for hour day, so 0 <= t < 24.

Then you want to map t to a value t' between 0 <= t' < \pi.

So t' = t * \pi / 24. At t = 0 we want t' = 0, at t = 12 we want t' = \pi / 2

Now let y = sin(t').

When y = 0 it is midnight, and when y = 1 it is noon.

Let L be your light level at noon and l be your light level as a function of time.

You can define l(t) = Ly = Lsin(t').

Your light level now transitions smoothly through the entire cycle as a function of t and you have removed branching entirely.

You can further parameterize t if you wish manipulate how time passes.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .