I know this question has been asked a lot of times, and I have seen the solutions to them on SO as well as other forums. Most of the times the solution suggested is to use Web Workers.
A game I'm developing requires me to run multiple functions at the same time. One of them is an on click function and other is a setInterval.
My approach at doing this can be seen here in this JSFiddle. (keep clicking in gray area to make player jump).
The whole idea is to continuously spawn those blue obstacles after an interval of 1000ms.
In my earlier approach the obstacles would spawn only when I click to make player jump, otherwise they wouldn't as expected.
How can I run such two functions side by side in order to achieve
the aim of spawning obstacles while also making player jump.
Secondly, what would be the best approach to carry out this process
in view of game development i.e attaining a certain level of
efficiency so that the animations are not affected.
Here is the HTML and Javascript code I've been working on:
<div class="container">
<div class="player"></div>
<div class="obstacle-container">
<div class="obstacle"></div>
</div>
</div>
$.fn.animator = function () {
var hit_list, done = false;
$(".container").click(function () {
if (!done) {
$(".obstacle").stop().animate({
left: "-=105%"
}, 10000, "linear");
$(".player").stop().animate({
bottom: "+=100px"
}, {
duration: 300,
complete: function () {
$(".player").animate({
bottom: "0"
}, 800);
},
step: function () {
//Test for collision
hit_list = $(".player").collision(".obstacle");
if (hit_list.length !== 0) {
$(function () {
if (!done) {
$(".container").append("Game Over!");
return false;
}
});
done = true;
}
}
});
}
});
};
$(function () {
$('.container').animator();
});
var interval = null;
$(".obstacle-container").obstacle_generator();
$.fn.obstacle_generator = function () {
interval = setInterval(function () {
$(".obstacle-container").append('<div class="obstacle"></div>');
}, 1000);
};
The generic concept you want to investigate is known as a game loop.
Almost every game will be built using some variant of this system:
Initialise game
Loop:
Check for user input
Update any actors
Draw the scene
Wait until it's time to repeat
A game running at 60 frames per second would perform this loop 60 times per second, or about once every 16ms.
Compared to your original question, you wouldn't need to be running multiple execution threads (running multiple functions together) to achieve this.
You are, in a way, already using a similar loop. jQuery maintains its own loop for updating animations. Where you are checking for collisions as part of your animation step, this is the sort of thing you would do in a hypothetical Player.update() method. You want to move this code out of jQuery, and in to a loop that you control.
Since you're running in a browser, the generic game loop becomes a bit more simple:
Check for user input - this can still be handled by event handlers, jQuery or not. Rather than directly changing properties like CSS position, though, they should act upon the state of the game object. For example, by changing the velocity of a Player object.
Update any actors - the important part of your loop. You should check how many milliseconds have passed since you last looped, since the browser doesn't guarantee that your code will be run exactly, or at least, 60 times per second. You should then loop through all of your game objects and update them all. In your Player.update() method, you would want to move it according to its velocity and the time passed, for example.
Draw the scene - if you're using DOM elements, then the browser handles drawing for you, of course. If you were using a <canvas> element, then you would do drawing yourself as part of the loop here.
Wait until it's time to repeat - this will be up to the browser to do for you, as part of normal setInterval/setTimeout behavior.
A simple game loop in JavaScript can look like this:
var gameObjects = [];
// Initialise game, create player objects etc, add them to the array
var gameLoop = function() {
// Loop through gameObjects, and call their respective update methods
};
setInterval(gameLoop, 16); // Try to run the loop 60 times per second.
In a complex game, you wouldn't have just a basic array to hold all game objects, this is just an basic example.
Related
I have the following functions
function render() {
init = timeStep(init)
if (steps <= yQuotient) {
requestAnimationFrame(render)
}
}
export function fire() {
setup();
render();
}
I then call fire() whenever I need to start/restart the application. My question is whether this will result in multiple requestAnimationLoop()'s active simulatenously as they build up like eventListeners do when called repeatedly, presumably affecting performance.
Do I need to amend my fire() function to something like this?
export function fire() {
cancelAnimationFrame(render)
setup();
render();
}
Or does requestAnimationLoop() just automatically know to end it's current loop when a new one is started?
Yes, calls to requestAnimationFrame will stack all the callbacks into the map of animation frame callbacks.
Each painting frame, the keys of this map are all gotten and looped over, allowing to schedule a new animation frame callback from such a callback, for firing at the next painting frame.
So yes, scheduling multiple times the same callback in the same event loop iteration will worsen the performances of your page, since it will have to do multiple times the same work.
It's quite unclear how all your system works, but a quite often used strategyfor games and interactive animations is to keep a single main animation loop running all the time and calling sub-tasks. External events only modifying states of objects that get visited from these sub-tasks.
I'm working on a javascript application that performs 2 jobs.
The first job is more important and needs to run at 60fps. The other job is a "background" job that still needs to run but it's okay if it takes longer.
Normally the way I would do this is have the more important job's code in a RequestAnimationFrame loop, and put the background job on a web worker.
However the main job is already spawning 2 web workers, and I don't want to spawn a third for context switching and memory consumption reasons.
There is ~8 ms of processing time left over on the RequestAnimationFrame loop that I have to work with for the background job to run on, however it is a job that will take about 100 ms to complete.
My question is there a way to write a loop that will pause itself every time the ui is about to be blocked?
Basically run as much code as you can until the remaining 8ms of time are up for the frame, and then pause until there is free time again.
This is currently experimental technology which isn't well-supported yet, but: There's requestIdleCallback, which:
...queues a function to be called during a browser's idle periods. This enables developers to perform background and low priority work on the main event loop, without impacting latency-critical events such as animation and input response. Functions are generally called in first-in-first-out order; however, callbacks which have a timeout specified may be called out-of-order if necessary in order to run them before the timeout elapses.
One of the key things about rIC is that it receives an IdleDeadline object which
...lets you determine how much longer the user agent estimates it will remain idle and a property, didTimeout, which lets you determine if your callback is executing because its timeout duration expired.
So you could have your loop stop when the deadline.timeRemaining() method returns a small enough number of remaining milliseconds.
That said, I think I'd probably add the third worker and see what it looks like in aggressive testing before I tried other approaches. Yes, it's true that context-switching is costly and you don't want to overdo it. On the other hand, there's already plenty of other stuff going on on mobiles and architectures these days are quite fast at context switching. I can't speak to the memory demands of workers on mobiles (haven't measured them myself), but that's where I'd start.
I recommend requestIdleCallback() as the accepted answer does, but it is still experimental and I like coming up with stuff like this. You might even combine rIC with this answer to produce something more suited to your needs.
The first task is to split up your idle code into small runnable chunks so you can check how much time you have/spent between chunks.
One way is to create several functions in a queue that do the work needed, such as unprocessed.forEach(x=>workQueue.push(idleFunc.bind(null,x)));}, then have an executor that will at some point process the queue for a set amount of time.
If you have a loop that takes awhile to finish, you could use a generator function and yield at the end of each loop, then run it inside recursive calls to setTimeout() with your own deadline or requestIdleCallback().
You could also have a recursive function that when processed, would add itself back to the end of the queue, which could help when you want to give other work time to run or when creating a function per piece of work would be absurd (e.g., hundreds of array items bound to a function that together only take 1ms to process).
Anyway, here's something I whipped up out of curiosity.
class IdleWorkExecutor {
constructor() {
this.workQueue=[];
this.running=null;
}
addWork(func) {
this.workQueue.push(_=>func());
this.start();
}
//
addWorkPromise(func) {
return new Promise(r=>{
this.workQueue.push(_=>r(func()));
this.start();
});
//DRY alternative with more overhead:
//return new Promise(r=>this.addWork(_=>r(func())));
}
sleep(ms) {
return new Promise(r=>setTimeout(r,ms));
}
//Only run the work loop when there is work to be done
start() {
if (this.running) {return this.running;}
return this.running=(async _=>{
//Create local reference to the queue and sleep for negligible performance gain...
const {workQueue,sleep}=this;
//Declare deadline as 0 to pause execution as soon as the loop is entered.
let deadline=0;
while (workQueue.length!==0) {
if (performance.now()>deadline) {
await sleep(10);
deadline=performance.now()+1;
}
/*shift* off and execute a piece of work. *push and shift are used to
create a FIFO buffer, but a growable ring buffer would be better. This
was chosen over unshift and pop because expensive operations shouldn't
be performed outside the idle executor.*/
workQueue.shift()(deadline);
}
this.running=false;
})();
}
}
//Trying out the class.
let executor=new IdleWorkExecutor();
executor.addWork(_=>console.log('Hello World!'));
executor.addWorkPromise(_=>1+1).then(ans=>{
executor.addWork(_=>console.log('Answer: '+ans));
});
//A recursive busy loop function.
executor.addWork(function a(counter=20) {
const deadline=performance.now()+0.2;
let i=0;
while (performance.now()<deadline) {i++}
console.log(deadline,i);
if (counter>0) {
executor.addWork(a.bind(null,counter-1));
}
});
If you can use requestIdleCallback() in your code, adding it to IdleWorkExecutor is pretty simple:
function rICPromise(opt) {
return new Promise(r=>{
requestIdleCallback(r,opt);
});
}
if (!deadline||deadline.timeRemaining()>0) {
deadline=await rICPromise({timeout:5000});
}
I'm getting into game developing online. I am trying to make an online FPS game, and I've only gotten to the point where I need to update my character. I am trying to keep my code simple, using only a draw and update function. When the html loads, I execute both: (Is this necessary?)
<body onload='DRAW(); UPDATE();'>
The draw function draws the player to the screen, and the update is supposed to check for a keypress to move the character. I am trying to make the script update using this:
function UPDATE()
{
update = setInterval(UPDATE, 60);
}
and to my knowledge, it is working fine because when I try and edit code in my online IDE (c9.io) which I use to test the site, it freezes when the site is running. I am also calling eventListeners in the draw function. (Is this proper if I want to test for a key down every frame?)
function DRAW()
{
window.addEventListener('keydown', function (e) {
keys.keys = (keys.keys || []);
keys.keys[e.keyCode] = true;
});
window.addEventListener('keyup', function (e){
keys.keys[e.keyCode] = false;
});
}
My questions are:
Is there an easier way to make a script update every frame?
Is there a JavaScript addon (like Three.js) I can use to make
developing this easier on myself?
Any knowledge is greatly appreciated.
This makes everything crash:
function UPDATE()
{
update = setInterval(UPDATE, 60);
}
You are recursively creating a new interval every 60ms; the first time you call UPDATE, you create an interval that creates a new interval every 60ms. All newly create intervals do the same. Don't really know what you actually want to do here.
I am also calling eventListeners in the draw function. (Is this proper
if I want to test for a key down every frame?)
It's fine to create eventlisteners in the draw function, provided you only call this function once. Which I guess you don't. Each time you call DRAW() a new set of eventlisteners will be added, and you really don't want that.
What you need is a form of game loop. Explaining how to create an FPS game is a bit more than I can do, but you can start by looking at this article Anatomy of a video game
I've faced the following scenario quite often so I'm wondering if there is a built-in jQuery way of solving the issue.
Imagine the following code:
$(document).click(function() {
paintCanvas();
});
The problem with this code is that if the user clicks on the screen 50 times in rapid succession you are going to overload the browser with 50 calls to paintCanvas.
If paintCanvas is currently executing and a new request is created, we want to queue the new request so that it waits until paintCanvas is finished executing. However, at the same time, we can drop any previously queued calls to paintCanvas as we only care about the final state of the mouse, not all the intermediate states.
Here is some code that solves the problem:
var _isExecuting, _isQueued;
function paintCanvas() {
if (_isExecuting) {
if (!_isQueued) {
_isQueued = true;
setTimeout(function() {
_isQueued = false;
paintCanvas();
}, 150);
}
return;
}
_isExecuting = true;
// ... code goes here
_isExecuting = false;
};
This AJAX queue plugin essentially implements this functionality, but does so only in terms of AJAX. Surely this is a very common problem that can be solved in more generic way?
You shouldn't have to solve this problem with mousemove because the system already does that for you. While paintCanvas is executing, it is not generating hundreds of mousemove events even if the mouse is moving vigorously. Rather, the next event will be the current location of the mouse, not a queue of all the intervening mouse events.
Look at this jsFiddle: http://jsfiddle.net/jfriend00/4ZuMn/.
Wiggle your mouse around in the body (lower, right pane) as fast as you want. Then move the mouse out of the pane and notice that the count stops immediately - there are no more mouse events. It doesn't stack up mouse events ever. Whenever the system is ready for the next mouse event, it gets the latest position of the mouse. Individual mouse moves are NOT queued up - they do not accumulate. You can also see in the listing of mouse events that lots of intervening mouse events are not present (e.g. lots of coordinates are missing) even though the mouse went through more positions. This is because the system wasn't ready to make a mouse event when the mouse was in that position so that position was skipped.
Further, because javascript is single threaded, you will never get a new mouse event while you are currently processing one. The system won't generate a new one until you're done processing the one you're already one. So, you will never, ever see _isExecuting as true in javascript in your code. You simply don't need that check. And, since you don't need that check and it will never be true, none of your queuing code will ever execute. You can see here in this jsFiddle, that you can never catch a mousemove event that was re-entered: http://jsfiddle.net/jfriend00/ngnUT/. The inAction flag is never caught as true, no matter how fast or much you wiggle your mouse around.
Sounds like you want throttle/debounce features.
There are no built in methods that I know of from jQuery, you can use any of these though:
http://benalman.com/projects/jquery-throttle-debounce-plugin/
http://jsperf.com/jquery-throttle-methods
Though #rkw provided a link, I always prefer to show code here on SO. Here's some simple code that kind does what you want. A function that returns a buffered version of another function. This will keep delaying until it stops receiving the event for the given delay. You can tweak this if you don't want to to wait for the delay after the last event. All you'd need to do is keep track of when you first set the timeout and offset the subsequent calls to setTimeout.
Here's a working example http://jsfiddle.net/mendesjuan/qfFjZ/
function createBuffered(handler, delay) {
var timeoutId = null;
return function() {
var me = this;
if (timeoutId) {
window.clearTimeout(timeoutId);
}
timeoutId = setTimeout(function() {
handle.apply(me, arguments);
timeoutId = null;
}, delay);
}
}
I am trying to make a simple hidden object game using javascript. When the user finds and clicks an image, I want 3 things to happen in the following order; a sound plays, the image size increases, and the image goes invisible. The problem I am running into is getting the 3 events to happen sequentially, not concurrent. Right now, seems that all three events happen all at the same time.
I've tried using setTimeout(), and while that does create a delay, it still runs all functions at the same time, even if each function is nested in setTimeout.
Example: (all this does is waits 1.5 sec then plays the sound and makes the image invisible):
function FindIt(image, id){
var t = setTimeout('sound()',10);
var b = setTimeout('bigger(' + image + ')',30);
var h = setTimeout('hide(' + image + ')',1500);
}
Below are the functions I am currently using and the actual results are: click the image, nothing happens for 2 seconds, then the sound plays and the image goes invisible.
function FindIt(image, id){
sound();
bigger(image);
hide(image);
}
function sound(){
document.getElementById("sound_element").innerHTML= "<embed src='chime.wav' hidden=true autostart=true loop=false>";
}
function bigger(image){
var img = document.getElementById(image);
img.style.width = 112;
img.style.height = 112;
}
function hide(id){
var ms = 2000;
ms += new Date().getTime();
while (new Date() < ms){} //Create a 2 second delay
var img = document.getElementById(id);
img.style.visibility='hidden';
}
Any guidance would be greatly appreciated!
To trigger things sequentially, you need to execute the second item some amount of time after the first one completes, execute the third item some amount of time after the second one completes, etc...
Only your sound() function actually takes some time, so I'd suggest the following:
function FindIt(image, id){
sound();
// set timer to start next action a certain time after the sound starts
setTimeout(function() {
bigger(image);
// set timer to start next action a certain time after making the image bigger
setTimeout (function() {
hide(image);
}, 1000); // set this time for how long you want to wait after bigger, before hide
}, 1000); // set the time here for how long you want to wait after starting the sound before making it bigger
}
FYI, the animation capabilities in libraries like jQuery or YUI make this sort of thing a lot easier.
Also, please don't use this kind of construct in your JS:
while (new Date() < ms){}
That locks up the browser for that delay and is very unfriendly to the viewer. Use setTimeout to create a delay.
For reference, using the animation libraries in jQuery, the jQuery code to handle a click on the object and then animate it over a 2 second period to a larger size, delay for 1 second, then slideup to disappear is as follows:
$("#rect").click(function() {
$(this).animate({height: 200, width: 400}, 2000).delay(1000).slideUp();
});
jQuery manages an animation queue and handles setting all the timers and doing all the sequencing and animation for you. It's a lot, lot easier to program and gives a very nice result.
You can see it work and play with it here: http://jsfiddle.net/kC4Mz/.
why don't use "event" approach. like onTaskDone();
function task1(arg, onTask1Done){
console.log(arg);
if(onTask1Done)onTask1Done();
}
task1("working", function(){console.log("task2");});
The Frame.js library is designed to elegantly handle situations like this:
function FindIt(image, id){
Frame(10, function(next) { sound(); next(); });
Frame(30, function(next) { bigger(image); next(); });
Frame(1500, function(next) { hide(image); next(); });
Frame.start();
}
Frame.js offers many advantages over using standard timeouts, especially if you are doing a lot of this kind of thing, which for a game, you likely are.
https://github.com/bishopZ/Frame.js