The JavaScript code for my HTML5 game has the following structure:
// <body onload="load()">
function load() {} // Load all images then call init()
function init() {} // Get all images ready for the game logic then call animate()
function animate() {} // Use requestAnimationFrame(), update() and drawing()
function update() {} // Update the game logic
function drawing() {} // Render the images on canvas
The issue lies inside animate(). I'm not finding any consistent sources around the web on how to organize requestAnimationFrame(), update() and drawing() in it.
I tried to elaborate it by myself, but the game did run in pratically any approach, like passing either animate(), update() or drawing() as an argument to requestAnimationFrame(), or having requestAnimationFrame() at either the beginning or the end of the function, or having any of these functions in any order, or one function inside another, etc.
That, however, doesn't mean anything is fine. Some of those arrangements result in issues that I'd find out only later, like when testing in a different computer or at a different frame rate. And then I have to go back to the code to try another approach.
So, how should I organize that? I'd appreciate if you can present me a proper algorithm, and even more if you have any good sources on teaching about it.
Use requestAnimationFrame to call animate repeatedly. animate calls update then draw. That's basically it. To have more control of time since you don't control the intervals exactly, it makes sense to pass the last time that animate was invoked. Maybe event the delta time that has passed since, makes more sense. Then you can use delta time to calculate distance given a speed and so on.
Here's an example of a game loop which is explained here:
var now,
dt = 0,
last = timestamp(),
step = 1/60;
function frame() {
now = timestamp();
dt = dt + Math.min(1, (now - last) / 1000);
while(dt > step) {
dt = dt - step;
update(step);
}
render(dt);
last = now;
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
There are many resources online. Here's a decent one for beginners https://www.sitepoint.com/quick-tip-game-loop-in-javascript/
Related
I need pause my script to allow a wait between events in my game. If anyone could explain how I could do this, in simple terms (because I am a newbie), I would really appreciate it. Thank you in advance!
Ideally you'd want a loop to control timing in your game for "pauses", which would just be iterations of the loop. Think of FPS in a game and how each cycle it will draw something new.
Here's a very simple example:
function update(progress) {
// Update the state of the world for the elapsed time since last render
}
function draw() {
// Draw the state of the world
}
function loop(timestamp) {
var progress = timestamp - lastRender
//you would call your functions here when the game is "running":
update(progress)
draw()
lastRender = timestamp
window.requestAnimationFrame(loop)
}
var lastRender = 0
window.requestAnimationFrame(loop)
SetInterval will call your function everytime a fix interval of time passes. Like a clock.
SetInterval tutorial
SetTimeout will call a function, only once, after a set amount of time passes.
SetTimeout tutorial
setTimeout is the way to go.
Usage example:
console.log('before')
setTimeout(function() {
console.log('after - 500ms later')
}, 500)
console.log('still before')
setTimeout won't pause javascript, there's no way to do that. Instead, it'll schedule a function to be called at some point in the future. This is why when you run the code snippet, you'll see "before" and "still before" get logged at the same time. Only the contents in the function passed into setTimeout() will happen after the delay provided.
Note that this means that there is no way to return a value from the setTimeout call for usage afterwards - this is something that many newcomers struggle with when they're first trying to work with async logic (like setTimeout). everything that you want to happen after this delay has to be put inside the setTimeout function (or, you can of course call other functions from it if you feel like your function is starting to get a little big).
So I want to make a small JavaScript game.
And in order to do that I need to animate a few things.
I went researching and found about setInterval and requestAnimationFrame.
I can use either of those 2 for making my game work, however I understood that requestAnimationFrame is the better alternative there.
The problem I see with this is that while the function has its benefits , you are unable to set a framerate , or an update rate for it easily.
I found another thread that explained a way of making this work however it seemed somewhat complicated.
Controlling fps with requestAnimationFrame?
Is there an easier way of animating with a set framerate ?
Is there an easier way of animating with a set framerate ?
Simply put: no. Since rendering is one of the most computation heavy process for a browser, which can be triggered in various ways, it is not possible to foretell how long an update will take, since it can range from drawing one circle on a canvas up to a complete replace of all the visible content of the page.
To overcome this, browser offer a way to call a function a often as possible and the developer is responsible to make his animation sensitive to different time deltas/time steps.
One way to tackle that is to use the concept of velocity. velocity = distance / time. If you want an asset to have a constant velocity you can do the following, since distance = velocity * time follows:
var
asset_velocity = 1; //pixel per millisecond
last = new Date().getTime()
;
(function loop () {
var
now = new Date().getTime(),
delta = now - last,
distance = asset_velocity * delta
;
//update the asset
last = now;
window.requestAnimationFrame(loop)
})();
I'm using the ShaderParticleEngine library for Three.js to create particle emitters.
I picked several code snippets on the Internet to have a working emitter.
Firstly I believed that is wasn't working.
But in fact, the emitter was displayed on the map, but a single motionless particle was on the screen.
After some debugging I undestood that the particle was moving but infinitely slowly. I need to use tick(delta * 1000) to see the emitter in action. And the result is quite ugly (full of gaps, alone particles).I have no problem of low FPS.
The only solution I found is to remove delta argument in the tick function call: particleGroup.tick().
The result is better but is still deceiving, judge by yourself:
Online Emitter Editor:
My result:
I can't understand. I use the same code proposed in the library examples and I use the export feature in the emitter editor.
If I try other variations (eg. on particle life/velocity) I get a very different result in my game, maybe the particle life is not computed correctly because delta argument isn't given?
My game loop:
var animate = function () {
requestAnimationFrame( animate );
render();
stats.update();
};
var render = function() {
time = ctx3d.clock.getElapsedTime();
delta = ctx3d.clock.getDelta();
particleGroup.tick(delta);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
Delta value loop by loop:
30.0000010000003385357559
9.999985195463523e-7
30.0000020000006770715117
0.0000010000003385357559
30.0000020000006770715117
0.0000010000003385357559
0.0000020000006770715117
30.0000010000003385357559
0.000002999999196617864
0.0000010000003385357559
9.999985195463523e-7
0.000002999999196617864
0.0000010000003385357559
0.000001999998858082108
0.0000010000003385357559
20.0000020000006770715117
9.999985195463523e-7
0.0000010000003385357559
To solve smoothness, try the following:
function makeSmoothSPETick(simulator, timeDelta, maxSubstepSize){
var numSubsteps = Math.floor(timeDelta/maxSubstepSize);
var leftOverTime = timeDelta%maxSubstepSize;
while(numSubsteps-->0){
simulator.tick(maxSubstepSize);
}
if(leftOverTime> 0){
//handle the rest
simulator.tick(leftOverTime);
}
}
If you use this function in your code - it will allow you to essentially subdivide steps that are too large into smaller ones of fixed size. As SquareFeet pointed out, say 16ms for 60FPS - you could use something like this:
var render = function() {
time = ctx3d.clock.getElapsedTime();
delta = ctx3d.clock.getDelta();
makeSmoothSPETick(particleGroup, delta, 0.016);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
You should get results visually similar to what you'd expect if you were running at smooth 60fps. Beware though, if target hardware can't handle these substeps - you may need to get more logic into your solver algorithm. I'd suggest keeping statistics for past 100 frames or so, and using that to decide how much you can split your incoming step value.
EDIT:
To make sure your timing isn't getting mangled, please try the following:
var lastFrameTime = Date.now()/1000;
var animate = function () {
requestAnimationFrame( animate );
render();
stats.update();
};
var render = function() {
time = Date.now()/1000; //getting current time in seconds since epoch
delta = time-lastFrameTime;
lastFrameTime = time;
particleGroup.tick(delta);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
I hope posting this as an answer is okay...
I've bumped the particle engine up a minor version to 0.7.7, having implemented a fix for your issue of "not-very-smooth-looking" emitters.
What was happening before was this:
SPE.Emitter.tick() called with a dt value
This tick function determines how many particles should be marked alive based on the dt argument passed to it. For larger dt values, more particles are marked as alive, for smaller values fewer are marked as alice.
The emitter then resets these particles and waits for the next call.
Assuming more than one particle is going to be marked as alive per frame, and they all originate at the same position in space, then all the particles will be at the same place when they're activated. This is why you saw some "clumping" happening.
What happens now is this:
SPE.Emitter.tick() called with a dt value, just as before.
The tick function now determines how many particles should be marked as alive, and whilst marking them so, sets each particles age to be a fraction of the dt value passed in.
So (!), assuming 100 particles are emitted per frame, and a dt value of 0.016 is passed to the emitter's tick function, each of those 100 particles that will be marked as alive is assigned an age value of (0.016 / 100) * i where i is the particle index (in this case, a value of 0 to 100).
I hope that makes sense. You can see the changes here: https://github.com/squarefeet/ShaderParticleEngine/blob/master/src/ShaderParticleEmitter.js#L240-L246
Master branch has been updated.
I'm trying to improve an animation that I've been working on where things move across the screen.
Currently the object moves at a set speed and has no variance.
I'm trying to include two features that will ultimately end up doing the same thing; changing the speed of the animated object.
I'd like the user to be able to change the speed and also for the object to slow down or speed up depending on where it is on the screen.
I'm not sure if I'm looking in the right place as currently I've been unable to update the duration once the animation loop has started. I first thought I could replace the number with a function that would return an int. This works in that the value of 'speed' changes but the animate loop is not updated.
Any help is hugely appreciated, thanks.
Code snippets below.
function moveObj () {
//initially the duration was set here. I understand that will not work as the animation is only
//being called once.
//animation process
obj.animate('top', '+=' + canvas.height, {
duration: speedOfObj(0),
abort: function () {
},//end abort callback
onChange: function () {
//testing only//
speedOfObj(1000);
}
//test function to see what the results would be. speed changes when called within the on change but the animation is not affected.
function speedOfObj(modifier){
var speed = 10000 / (new Number(speedControl.value));
if(modifier == 0){
console.log("speed: "+speed);
return speed;
}else{
speed *= modifier;
console.log("speedBBBB: "+speed);
return speed;
}
}
Once a jQuery animation is off and running, it's pretty much off on its own. If you want to change how it works, you can .stop(true) it and then start up a new animation that starts again from where it is now at your new speed.
It's also possible to implement a custom step function in the animation that might takes some queues for how to work from outside influences that can change during the animation, but I think that would end being much more complicated than just stopping the original animation and starting a new one that moves at the newly desired speed.
Working demo: http://jsfiddle.net/jfriend00/tzxca/
I'm making a simple game and I'm having a problem where after minimizing the window for a few seconds, upon return the game runs at twice the framerate, and even more after that, adding 60 each time. My game loop looks like this:
function disp(){
update();
draw();
requestAnimationFrame(disp);
}
requestAnimationFrame(disp);
With both update and draw not including requestAnimationFrame. I have tried this on both firefox and chrome with the same results. Any ideas why this is happening? I've used this same method tons of times and this is the first time this has ever happened.
EDIT: You can find a fiddle of it at http://jsfiddle.net/5ttGs/
It's really simple so far as this kinda paused my progress. Click it a couple times and enjoy 10000+FPS gameplay
Fixed the problem with the help of cocco. Basically the onclick event called the disp function every time the canvas was clicked. Because the disp function had a requestAnimationFrame frame in it, it called it twice as much every second, resulting in the influx of frames. In order to fix I simply got rid of the disp in
canvas.addEventListener
function mouse_up() {
var matches = 0;
var overlap = 69;
mouse.up = false;
console.log("clicked", mouse.x,mouse.y);
disp();
}
You can find the result here: http://jsfiddle.net/5ttGs/