@Sjors -- millis() starts when the sketch starts -- in a sketch with or without setup. Calling millis(); immediately in setup will still occur a short delay after the sketch has spun up.
A version of millis() that is keyed to the start of the first draw frame is as simple as recording an offset -- but finding that offset is a bit tricky.
setup() runs during frame 0. That means that draw won't begin until the start of frame 1 -- for example, at frameRate(1) that would be 1000 millis after setup() is called, which is already some amount of delayed time after the sketch begins. So, if you check the time at the end of setup(), this may be much earlier than the start of draw() if there is still time left before frame 1 fires. Conversely, draw() may also begin much later the expected frame 1 time if setup() itself takes more than the duration of a frame to complete running.
One solution is to record the startup offset at the beginning of setup, including the anticipated delay time of a single frame. Then, at the end of setup, check if the setup time took less then one frame. If it did, use the estimate. If it didn't, use the current time. This will get your timer within a few milliseconds of 0 when draw is called for the first time.
// A clock based on draw()
// forum.processing.org/two/discussion/22272/when-does-millis-start
int FPS = 10;
int offset;
void setup() {
// first line -- offset is current time plus estimated length of frame 0
offset = millis()+(int)(1000.0/FPS);
frameRate(FPS);
// the rest of setup goes here
// simulate setup process
delay((int)random(0,2000));
// last line -- offset is the longer of either:
// 1. our estimate (wait until frame 1)
// 2. the current time (if setup took longer than a frame to run)
offset=max(offset, millis());
}
void draw() {
println(dmillis());
}
// clock with 0 close to the first time draw() ran
int dmillis() {
return millis()-offset;
}
@GoToLoop -- nice use of a one-time pre() that unregisters itself -- and with it, nice paired use of both Override, and of System.currentTimeMillis() to get around how to initialize the override.
A registered method pre() is executed just before draw(), so this is another way around the issue of timing startup and setup().
Both of the solutions above are ways to avoid something like this third, less elegant solution, which is more concise and gets the idea across without fancy tricks -- but which has the drawback of requiring checking the if every frame for the rest of the sketch even though it only runs a single time on the first draw frame:
int drawMillisStart;
void draw(){
if(frameCount==1){
drawMillisStart = millis();
}
int currMillis = millis() - drawMillisStart;
println(currMillis);
}
Note that in all three cases resetting the sketch by setting frameCount=0 will reset the clock:
Answers
@Sjors --
millis()
starts when the sketch starts -- in a sketch with or without setup. Calling millis(); immediately in setup will still occur a short delay after the sketch has spun up.Try these test sketches to see:
no setup
911
setup
917
931
setup with delays
1022
1523
1526
2031
This is the implementation of PApplet::millis() method:
https://GitHub.com/processing/processing/blob/master/core/src/processing/core/PApplet.java#L3158-L3160
As you can notice, it invokes System::currentTimeMillis(), subtracts its value from field millisOffset, and downcasts the total result to
(int)
, returning it:http://docs.Oracle.com/javase/8/docs/api/java/lang/System.html#currentTimeMillis--
Field millisOffset is declared and initialized this way:
https://GitHub.com/processing/processing/blob/master/core/src/processing/core/PApplet.java#L700
Given it is initialized at the moment of its declaration, that fixed value is established at the very early moments of PApplet's instantiation.
Which happens much earlier than setup() & even settings() are called back btW. >-)
And if class PApplet had any explicit constructors, that'd happen even before them! =P~
A version of
millis()
that is keyed to the start of the first draw frame is as simple as recording an offset -- but finding that offset is a bit tricky.setup()
runs during frame 0. That means that draw won't begin until the start of frame 1 -- for example, at frameRate(1) that would be 1000 millis after setup() is called, which is already some amount of delayed time after the sketch begins. So, if you check the time at the end of setup(), this may be much earlier than the start of draw() if there is still time left before frame 1 fires. Conversely, draw() may also begin much later the expected frame 1 time if setup() itself takes more than the duration of a frame to complete running.One solution is to record the startup offset at the beginning of setup, including the anticipated delay time of a single frame. Then, at the end of setup, check if the setup time took less then one frame. If it did, use the estimate. If it didn't, use the current time. This will get your timer within a few milliseconds of 0 when draw is called for the first time.
@GoToLoop -- nice use of a one-time pre() that unregisters itself -- and with it, nice paired use of both Override, and of System.currentTimeMillis() to get around how to initialize the override.
A registered method
pre()
is executed just beforedraw()
, so this is another way around the issue of timing startup and setup().Both of the solutions above are ways to avoid something like this third, less elegant solution, which is more concise and gets the idea across without fancy tricks -- but which has the drawback of requiring checking the
if
every frame for the rest of the sketch even though it only runs a single time on the first draw frame:Note that in all three cases resetting the sketch by setting
frameCount=0
will reset the clock: