I want to reset the clock so that clock.getElapsedTime()
gives me a new time from when I reset the clock (for example, useful when restarting a game level/scene the second time).
I am initiating clock = new THREE.Clock();
in init()
, and in my game loop update()
, I am using this clock. But when the game is over, I want to reset the clock (I am not initiating the level again and am just positioning the player back to the beginning so I am not initiating a new clock).
How can I achieve this?
Bad news: It's impossible to reset the THREE.Clock
to zero time, as of r73, released Oct 2015. The explanation is below, and the only possible workarounds are at the end of this answer.
The design of Clock is dangerous...
-- Mrdoob, GitHub issue comment
To understand the flaw(s) in THREE.Clock
we must inspect the source code to see how it works. We can see that in the constructor for a Clock, a few variables are instantiated, which at first glace looks like we can overwrite on our Clock
instance to reset to zero:
this.startTime = 0;
this.oldTime = 0;
this.elapsedTime = 0;
However, digging a little deeper, we need to figure out happens when getElapsedTime()
is called. Under the hood, it calls getDelta()
, which mutates this.elapsedTime
, which means that getDelta
and getElapsedTime
are dangerous, conflicting functions, but we still need to look closer at getDelta
:
var newTime = self.performance.now();
diff = 0.001 * ( newTime - this.oldTime );
this.oldTime = newTime;
this.elapsedTime += diff;
This function is referencing some unknown, implicit global variable, self
, and calling some odd function, self.performance.now()
. Red flag! But let's keep digging...
It turns out Three defines a global variable, self
, with a property performance
with a method now()
in the "main" Three.js file.
Stepping back to THREE.Clock
for a moment, look how it calculates this.elapsedTime
. It's based on the value returned by self.performance.now()
. That seems innocent enough on the surface, except the true problem arises here. We can see that self.performance.now()
creates a true "private" variable in a closure, meaning no one in the outside world can ever see / access it:
( function () {
var start = Date.now();
self.performance.now = function () {
return Date.now() - start;
}
} )();
This start
variable is the start time of the app, as returned in milliseconds from Date.now()
.
That means that when the Three.js
library loads, start
will get set to the value Date.now()
. To clarify, simply including the Three.js
script has global, irreversible side effects for the timer. Looking back on the clock, we can see that the return value of self.performance.now()
is used to calculate the current elapsed time of a Clock
. Because this is based on the private, inaccessible "start time" of Three.js
, it will always be relative to when you include the Three.js
script.
Store startTime = clock.getElapsedTime()
on your game/level start and making all your calculations relative to that. Something like var currentTime = clock.getElapsedTime() - startTime
which is the only way to get the true absolute time from your scene load / start.
Three.Clock
under the hood is really just a thin wrapper around Date.now()
, which is an IE9+ available method. You're probably better off creating your own tiny abstraction around this to get a sane clock, with an easy to implement reset()
method. If I find an npm
package with this functionality, or make my own, I will update this answer.
Wait for the Three.js Three.Clock
source code to be updated, possibly by me. Since there is an open ticket for it, a pull request to fix it will likely be accepted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With