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?
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?
Share Improve this question edited Jun 5, 2017 at 6:35 Aasha joney 5465 silver badges25 bronze badges asked Jan 21, 2016 at 21:21 krikeykrikey 1951 gold badge3 silver badges11 bronze badges 1- Have you looked into the '.getDelta()' method to see if it suits your needs? – gromiczek Commented Jan 22, 2016 at 15:46
4 Answers
Reset to default 10Bad 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 Problematic Design of Three.Clock Investigated in Depth
The design of Clock is dangerous...
-- Mrdoob, GitHub issue ment
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.
Workaround #1
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.
Workaround #2
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.
Workaround #3
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.
Save the time at the start of the game: var gameStartTime = clock.performance.now()
, and calculate the run of time thereafter by subtracting gameStartTime
from clock.performance.now()
at any other point during the game, including its end. For example: gameStartTime - gameStartTime
would equal zero, and clock.performance.now() - gameStartTime
would give you the seconds or minutes since the game began.
Here's a reference to the performance.now()
timer function in JavaScript.
This will not reset the clock but it will generate a new clock object and assign the new value to the elapsedTime variable every 5 seconds:
let clock = new THREE.Clock();
const animate = () => {
let elapsedTime = clock.getElapsedTime();
if (elapsedTime > 5) {
clock = new THREE.Clock();
}
window.requestAnimationFrame(animate);
};
Here is simple workaround I use to start
pause
and reset
the runtime of my animation systems.
let offsetTime = 0; // in milliseconds
let start = false;
function reset() {
offsetTime = 0;
start = false;
}
function start() {
if ( start ) return;
offsetTime += performance.now();
start = true;
}
function pause() {
if ( start ) return;
offsetTime -= performance.now();
start = false;
}
const animationRuntime = performance.now() - offsetTime;