How to reset three.js clock? - javascript

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 Problematic Design of Three.Clock Investigated in Depth
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.
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;

Related

Resetting the value of 'timestamp' in requestAnimationFrame()?

I am currently making a slideshow that turns slides after 10 seconds, and using requestAnimationFrame to do so.
I am having some issues with the timestamp, however. I want to keep track of the timestamp value (which is no problem), and when it gets to a value over 10000 (10 seconds), to reset the timestamp to 0 and keep going. However, when I try to change the value of timestamp, nothing happens. Assuming it is a const? Setting a variable to performance.now() with each call is also not behaving quite like I'd expect.
Wondering what the best workaround to this issue would be, maybe some use of performance.now()? Thanks!
When the loop starts, store a reference to when it starts. Then update that variable, rather than trying to modify the timestamp.
let start = null;
let loop = (timestamp) => {
if (!start) {
start = timestamp;
};
const progress = timestamp - start;
if (progress > 10000) {
console.log('loop', start);
start = timestamp;
}
requestAnimationFrame(loop)
};
requestAnimationFrame(loop);

What does date.now() 'then' does here? - Javascript

I've used this tutorial to help me out writing a simple JS game for my school assignment. However I am now looking at the gameloop and I have no clue how this particular function is working.
Here is the URL of the tutorial. The block of code you're looking for is at 8 "The main game loop"
http://www.lostdecadegames.com/how-to-make-a-simple-html5-canvas-game/
//Gameloop
var main = function () {
//var with timestamp
var now = Date.now();
//where does 'then' come from? I never declared it.
var delta = now - then;
//next up it just calls another func and provides parameter delta divided by 1000 which is the amount of miliseconds in a second
update(delta / 1000);
//and it calls my render function
render();
//then it sets then back to Date.now()
then = now;
//No idea what this line does. still looking into it
requestAnimationFrame(main);
};
I will try to explain what I understood from the code in the tutorial.
A game often runs in a fixed framerate, like 60 frames per seconds (FPS).
In the tutorial, that is not the case.
Instead of having a fixed framerate and moving the character at a fixed distance in the update function, you have a delta variable used to calculate the distance.
hero.y -= hero.speed * modifier; // modifier is (delta / 1000)
Like the other answers said, then is set in the beginning, in the outer scope of the main function.
// Let's play this game!
var then = Date.now();
reset();
main();
I will add that the tutorial is a little old (2011) and some details can be improved. For example, you can use performance.now() instead of Date.now() as reported by lighthouse.
Read Start Game Number 10:
// Let's play this game!
var then = Date.now();
reset();
main();
The following description also:
"Almost there, this is the last code snippet! First we we set our timestamp (with the variable then) to seed it. Then we call reset to start a new game/level."

Run a Function For Each Milliseconds

I am trying to run a function for each milliseconds, In order to achieve so, I just preferred setInterval concept in javascript. My code is given below,
HTML:
<div id=test>0.0</div>
Script:
var xVal = 0;
var xElement = null;
xElement = document.getElementById("test");
var Interval = window.setInterval(startWatch, 1);
function startWatch(){
xVal += 1;
xElement.innerHTML = xVal;
}
so the above code is working fine. But while I am testing the result with a real clock, the real clock requires 1000 milliseconds to complete 1 second, at the same time the result require more than 1000 milliseconds to complete a second.
DEMO
Can anybody tell me,
Is there any mistakes with my code? If yes then tell me, How to display milliseconds accurately.?
There are no mistakes in your code, but JavaScript timers (setInterval and setTimeout) are not precise. Browsers cannot comply with such a short interval. So I'm afraid there is no way to precisely increment the milliseconds by one, and display the updates, on a web browser. In any case, that's not even visible to the human eye!
A precise workaround would involve a larger interval, and timestamps to calculate the elapsed time in milliseconds:
var start = new Date().getTime();
setInterval(function() {
var now = new Date().getTime();
xElement.innerHTML = (now - start) + 'ms elapsed';
}, 40);
You can't. There is a minimum delay that browsers use. You cannot run a function every millisecond.
From Mozilla's docs:
...4ms is specified by the HTML5 spec and is consistent across browsers...
Source: https://developer.mozilla.org/en-US/docs/Web/API/window.setTimeout#Minimum.2F_maximum_delay_and_timeout_nesting
The DOM can't actually update 1000 times per second. Your monitor can't even display 1000 frames in one second, for that matter. Calculate the difference between the start time and current time in milliseconds within your function and use that:
(function(){
var xElement = document.getElementById("test");
var start = new Date;
(function update(){
xElement.innerHTML = (new Date - start);
setTimeout(update, 0);
})();
}();
Updated fiddle
You can't do so using your method because of the delay rendering the HTML and running the interval. Doing it this way will display the time correctly at about 60FPS.
http://jsfiddle.net/3hEs4/3/
var xElement = null;
var startTime = new Date();
xElement = document.getElementById("test");
var Interval = window.setInterval(startWatch, 17);
function startWatch(){
var currentTime = new Date();
xElement.innerHTML = currentTime - startTime;
}
You might also want to look into using requestanimationframe instead of a hardcoded setInterval like that.
The setInterval callback probably does not happen with millisecond accuracy, since the thread the timer is running on might not even actually be running when the time is up, or the browser throttles events, or any other of quite a few things.
In addition, since most Javascript engines are single threaded, what the implementation of setInterval might do is once it triggers, run your callback, and then reset the clock for the next call. Since you're doing some DOM manipulation, that might take several milliseconds on its own.
In short, you're expecting a Real Time Operating System behavior from an interpreter running inside of another application, on top of what is more than likely not an RTOS.
I had the same question and couldn't find any working solution, so I created one myself. The code below essentially calls five setTimouts every 5 ms, for each ms between 5 and 10. This circumvents the minimum 4 ms constraint, and (having checked in Firefox, Chrome, and Opera) works fairly well.
const start = performance.now();
let newNow = 0;
let oldNow = 0;
const runner = function(reset) {
// whatever is here will run ca. every ms
newNow = performance.now();
console.log("new:", newNow);
console.log(" diff:", newNow - oldNow);
oldNow = newNow
if (newNow - start < 1000 && reset) {
setTimeout(function() {
runner(true);
}, 5);
for (let i = 6; i < 11; i++) {
setTimeout(function() {
runner(false);
}, i);
}
}
};
runner(true);
It could of course be written more elegantly, e.g. so that you can more easily customize things like the graduation (e.g. 0.5 ms or 2 ms instead of 1 ms), but anyway the principle is there.
I know that in theory you could call 5 setIntervals instead, but that would in reality cause a drift that would quickly ruin the ms precision.
Note also that there are legitimate cases for the use. (I for one need continual measurement of touch force, which is not possible otherwise.)

HTML Canvas Interval vs RequestAnimationFrame

So, maybe total brainfart here. The syntax for setInterval() is pretty clear. Do something every x miliseconds. How is this best translated to using the requestAnimationFrame() ?
I have about 300 objects and each is supposed to perform an animation sequence at a certain interval (every 8, 6, 2, etc seconds)? How can I best accomplish this using requestAnimationFrame() which gets called ~60 times a second? There is probably an easy answer, I just, for the life of me, can't figure it out.
To force requestAnimationFrame to stick to a specific FPS you can use both at once!
var fps = 15;
function draw() {
setTimeout(function() {
requestAnimationFrame(draw);
// Drawing code goes here
}, 1000 / fps);
}
A little weird, but noth the most confusing thing in the world.
You can also use requestAnimationFrame not with FPS but with elapsed time in order to draw objects that need to be updated based on the time difference since the last call:
var time;
function draw() {
requestAnimationFrame(draw);
var now = new Date().getTime(),
dt = now - (time || now);
time = now;
// Drawing code goes here... for example updating an 'x' position:
this.x += 10 * dt; // Increase 'x' by 10 units per millisecond
}
These two snippets are from this fine article, which contains additional details.
Good question by the way! I don't think I've seen this answered on SO either (and I'm here way too much)
requestAnimationFrame is pretty low level, it just does what you already said: roughly gets called at 60fps (assuming the browser can keep up with that pace). So typically you would need to build something on top of that, much like a game engine that has a game loop.
In my game engine, I have this (paraphased/simplified here):
window.requestAnimationFrame(this._doFrame);
...
_doFrame: function(timestamp) {
var delta = timestamp - (this._lastTimestamp || timestamp);
for(var i = 0, len = this.elements.length; i < len; ++i) {
this.elements[i].update(delta);
}
this._lastTimestamp = timestamp;
// I used underscore.js's 'bindAll' to make _doFrame always
// get called against my game engine object
window.requestAnimationFrame(this._doFrame);
}
Then each element in my game engine knows how to update themselves. In your case each element that should update every 2, 6, 8 seconds needs to keep track of how much time has passed and update accordingly:
update: function(delta) {
this.elapsed += delta;
// has 8 seconds passed?
if(this.elapsed >= 8000) {
this.elapsed -= 8000; // reset the elapsed counter
this.doMyUpdate(); // whatever it should be
}
}
The Canvas API along with requestAnimationFrame are rather low level, they are the building blocks for things like animation and game engines. If possible I'd try to use an existing one like cocos2d-js or whatever else is out there these days.

Counting down for x to 0 in Javascript?

I have from the backend a time on the format 00:12:54 and I display it to the screen. But, I would like to have this time to continue to go down. I have though to create a variable in javascript that will old the time and with setTimeout to loop to display with document.getElementById the new value. I think it can be problematic if I have many time to go down in same time. I might require an array?
How would you do that? If I have no other suggestion, I will try my way, but I am curious to know if it does have a more secure way to do it.
Do you know jQuery Framework? It's a Javascript framework that have a lot of utilities methods and functions that let you do Javascript stuff more easily.
Here is a count down plugin (haven't tested it).
I suggest you to download JQuery than download the plugin . Check the sample of code from the "relative" tab on the website. You can have something like :
$('#until2d4h').countdown({until: '+12M +54S'});
*The only drawback with what I suggest you is that you will require 2 .js to be added. Try to add them only when needed and you will be find.
General algorithm:
Read time from server.
Read the current time.
Call a function.
In your function, read the current time, get the delta from the initial time you read in step 2.
Subtract the delta from the initial time you read from the server in step 1 and display the remainder.
The function should call window.setTimeout to call itself in 1000ms (or adjust according to time elapsed within the function), if you want to continue counting down.
Here's a rough cut:
window.onload = function () {
var countdown_start_in_ms = 6000; // from server
function tick() {
var now = new Date().getTime();
var disp = start - now;
if (disp < 0) {
disp = 0;
}
var el = document.getElementById("countdown");
el.innerHTML =
// quick hack to format time
/(\d\d:\d\d:\d\d) ...$/.exec(new Date(disp).toUTCString())[1];
if (disp > 1000) {
var elapsed = new Date().getTime() - now;
window.setTimeout(tick, 1000 - elapsed);
} else {
// stop countdown and set color to light grey
el.style.color = "#ccc";
}
}
var start = new Date().getTime() + countdown_start_in_ms;
tick();
}
You won't like the taste of this one, but it'll do you good:
Google for 'javascript timer' and get your hands dirty reading through the various examples and tutorials returned by that search.
You'll learn a lot more than just how to write a count-down timer. :-)
Good luck!
Take a look at Grab hands and set your own time. and inspect its code. While it is written with Dojo, the "clock" part is in plain JavaScript. In your case the only difference is how to advance the counter — decrease rather than increase it.

Categories

Resources