Why would the canvas2d context no longer fill ellipses? - javascript

I'm working on a javascript game that simulates gravitational forces. It uses the HTML5 canvas element to draw 2D ellipses for planets. I test my game in Google Chrome. Here's a link to the game: http://gravitygame.hostingsiteforfree.com/index.php?page=playHTML
Up until May 24th, it worked just fine. However, after Chrome upgraded from 26.0.1410.64 to 27.0.1453.94, the filled ellipses are sometimes not drawn. It doesn't happen every time I load my game, and I've never gotten it to break while running locally.
Here's a screenshot of the game working:
And here's a screenshot that shows it not filling the ellipses:
I can't tell what's happening. I'll include the portion of the loop that draws all of the planets. I've modified it for readability.
var i = bodies.length;
while(i--){
var I = bodies[i];
var planetRad = (I.width/2)*_scale;
if(_showTrails){
//draw the planet's trail
}
if(//the planet is completely off the screen){
//draw a red planet on the edge of the screen
ctx.beginPath();
ctx.arc(nX, nY, 2.5, 0, TWOPI);
ctx.fillStyle = offScreenColor;
ctx.fill();
ctx.strokeStyle = offScreenOutline;
ctx.stroke();
}
else{
//draw planet
ctx.beginPath();
ctx.arc(nX, nY, (I.width/2)*_scale, 0, TWOPI);
ctx.closePath();
ctx.fillStyle = I.bodyColor;
ctx.fill();
}
if(_showMotionVector){
//draw a line from the center of a planet showing the direction and speed it's travelling
ctx.strokeStyle = motionColor;
ctx.beginPath();
ctx.moveTo(I.getScX(), I.getScY());
ctx.lineTo(I.motion.x * _scale * 12 + I.getScX(), I.motion.y * _scale * 12 + I.getScY());
ctx.stroke();
}
}
Why would it suddenly break on occasion?

I took a look at your online code and discovered you are using setInterval for the animation loop.
This is most likely the reason as if the code is not able to finish calling the calcs etc. you run the risk of stacking calls - for context that means you can have path's that reset each other.
Try first to replace setInterval with setTimeout. You will of course need to retrigger it again from within the code - better yet, put everything in a function with a setTimeout at the end of that function, ie.:
function animate() {
//... calcs and redraws which you have in setInterval
setTimeout(animate, 0);
}
animate();
I use 0 for timeout here for this test. setTimeout/setInterval won't sync to screen refresh rate in any case.
If that works then you know the reason. The next step would be to replace it with requestAnimationFrame, but let me know how it goes.
In an attempt to illustrate the problem we can look at this illustration:
Each block represent a function within the loop, and one loop is one color. Remember that setInterval calls at fixed intervals while setTimeout calls relative to when it's called. In this example the functions perform within the time budget so everything goes well.
In the next illustration:
the spending is outside the budget so setInterval is called again and queues up next call to the second loop before the first has finished. When the queue is processed between the calls you end up risking having two functions working on the context at the "same time" (or comes in a different order than you might expect).
Javascript is of course single-threaded so they do not execute at the same time, but one is held at wait - if the first block for next queue is called before the last block has time to be called then the first block will modify the context and perhaps even change the path before the last call of the previous call is invoked. Over time the lag-behind will increase and potentially (unless some extra available processing resources resolves the queue now and then - on a busy system this is less likely to happen) become worse and worse as more stacking occur.
Ie, in this case you could have lines added to the context with beginPath() before arc got filled.
(hope that made any sense...)
Using setTimeout will prevent this as it won't be executed before all calls in the animation loop has returned. The better option is to use requestAnimationFrame as this will call in sync with the screen-refresh rate, also when possible. It's more low-level and therefor also more efficient.
Another path (no pun intended) is to use Web-workers to do the calculations. This will be multi-threaded and can increase overall performance as a web-worker does not affect the UI thread.

Related

How should I organize my update/draw logic when using requestAnimationFrame?

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/

Three.js - Spawn an object every x seconds and move each object forward (along Z)

I am making an endless runner style game using Three.js. The basic set up of the scene and idea for the game is a long moving road with cars coming towards you that you have to dodge out of the way of. I am still at the early stages of creating this game, and so my first problem is that I need the hero character (who is dodging the cars) to seem like he is moving forwards, and at the same time have the cars seem like they are moving (faster) towards the hero character.
My thinking was to create road strip objects (the white lines in the middle of a road), and have them move towards the hero character, who is at (0, 0), at a certain speed.
I have successfully created a road strip object and positioned it at the very back of the road (RoadStrip.mesh.position.z = -5000;). Here is my code for that:
var objectRoadStrip = function() {
this.mesh = new THREE.Object3D();
this.mesh.name = "roadStrip";
geomRoadStrip = new THREE.BoxGeometry(20, 11, 300);
matRoadStrip = new THREE.MeshPhongMaterial({color: Colors.white});
RoadStrip = new THREE.Mesh(geomRoadStrip, matRoadStrip);
RoadStrip.name = 'roadStripName';
this.mesh.add(RoadStrip);
}
function createRoadStrip() {
new objectRoadStrip();
RoadStrip.position.y = -72.5;
RoadStrip.position.z = -5000;
scene.add(RoadStrip);
}
In the render() function, which is the function that loops over every frame and is called last to make sure the camera and scene update every frame, I am able to successfully move this strip forwards along the z axis by 10 every time render() is called. I also added some code so that when the RoadStrip touches (0,0), it is removed from the scene. See this below:
function render(){
// moves RoadStrip towards (0,0). When it reaches z = -150, remove that strip from the scene
if (RoadStrip.position.z <= -150) {
RoadStrip.position.z += 10;
} else {
scene.remove(RoadStrip);
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
I have also added the following code to the init() function which creates a RoadStrip when the scene is created, and the continues to create a RoadStrip every 10 seconds (roughly every time the RoadStrip reaches (0,0).
createRoadStrip();
setInterval( function() {
createRoadStrip();
}, 10000);
This is similar to the effect I'm going for, but read The Problem section below where I explain what I truly need.
The Problem
I need to spawn a RoadStrip every x amount of seconds (still to be decided once I get it working, but lets say 3 seconds for now) continuously. Each RoadStrip needs to move towards (0,0) with z += 10 independently. When a RoadStrip instance reaches (0,0), it should be removed from the scene, but other RoadStrips should continue to spawn regardless every 3 seconds at the original position (z = -5000).
My Attempts / Solution Ideas
I've done a lot of reading on this, trawling through code from other people's endless runner games and reading through SO answers but nothing seems to have worked. Below are some of the things I have tried, or some things that I feel would work but I am not doing right/don't have a good understanding of:
Idea: Instead of calling the createRoadStrip() function inside a setInterval, push a RoadStrip object to an array every 3 seconds, and then call that array and move the array along the z axis by += 10.
Possible solution help: I tried changing the setInterval to less than 2 seconds instead of 10 seconds. This caused the RoadStrip to move along the Z axis for 2 seconds as expected, but of course, after 2 seconds another RoadStrip was spawned, and so the first RoadStrip stopped moving along the Z axis, and the new one did instead (for 2 seconds as well) and this process repeated infinitely. This is so close to what I need, but I need each RoadStrip to continue moving, and be remove from the scene when it reaches (0,0)
Thanks for taking the time to read my Question, I look forward to your solutions!
Examples of similar style games: First, Second.
Thanks to #prisoner849 and his link to this thread, I managed to find the solution to the problem, and so I am writing an answer here for anyone who comes across this with the same problem in the future!
I read through the thread and found a link to this JSFiddle, that includes a successful animation similar to the one I was trying to achieve, and I would highly suggest studying the code of that JSFiddle to fully understand how to create an endless runner effect.
Here is a detailed explanation of how to do this:
Instead of infinitely creating objects and have them animate forwards until they reach the end point and disappear (like I originally thought was the right solution), you have to create an array of objects and animate that instead.
Here is my code for doing this:
var roadStripArray = []
function objectRoadStrip() {
for (var i = 0; i < 100; i++) {
geomRoadStrip = new THREE.BoxGeometry(20, 11, 500);
matRoadStrip = new THREE.MeshPhongMaterial({color: Colors.white});
RoadStrip = new THREE.Mesh(geomRoadStrip, matRoadStrip);
RoadStrip.position.set(0, -72.5, -150 - i * 1250);
RoadStrip.receiveShadow = true;
scene.add(RoadStrip);
roadStripArray.push(RoadStrip);
}
}
The for loop has the code i < 100 as my road is quite long and therefore needs a lot of strips
This code:
RoadStrip.position.set(0, -72.5, 0 - i * 1250);
sets the position of each strip to be different from each other, and the number 1250 is the distance between each strip
After creating the objects, you must animate them in the render() function. You have to set them to move along the Z axis, and then create an if statement that says "if any strip reaches the end point (where you want it to disappear), reset it's position back to the start (i.e. the start of the road for me). This means you are constantly looping through your array of objects, and therefore don't infinitely create them.
Here is the code that animates the strips:
// loop that runs every frame to render scene and camera
var clock = new THREE.Clock();
var time = 0;
var delta = 0;
var direction = new THREE.Vector3(0, 0, 1);
var speed = 2000; // units a second - 2 seconds
function render(){
requestAnimationFrame(render);
delta = clock.getDelta();
time += delta;
roadStripArray.forEach(function(RoadStrip){
RoadStrip.position.addScaledVector(direction, speed * delta);
if (RoadStrip.position.z >= 10000) {
RoadStrip.position.z = -10000;
} else {
}
});
renderer.render(scene, camera);
}
The code that moves each strip is:
RoadStrip.position.addScaledVector(direction, speed * delta);
You can read more about .addScaledVector here, but essentially this is the code that animates the strip.
The if statement then checks if the strip touches 10000 (i.e. the end of the road), and if it does, sets the position of that strip to -10000. That strip then moves back towards the end point along the Z axis.
We wrap this all in a forEach function to loop through each RoadStrip in the array and animate them all in the same way. We need to animate them individually so that we can detect when one of them reaches the end of the road.
Thanks, hope this helps!
Usually this kind of scenario is best handled with some kind of particle-system like approach: you don't insert/remove objects continuously to the scene but create a set of objects during initialization, let's say the player can only see 10 road stripes at a time, and your game logic is always moving those 10 stripes, updating positions as needed, once one strip goes out of the field of view, it is recycled at the begining and so on. I don't think you will find a canned solution that does exactly what you are looking for, you would need to come up with the update logic that suits best your game.
I have an example of custom particle system there. Once a particle is getting out of scope, it is made available for the system when it needs to emit a new particle. The number of particle in the pool is always constant and can be defined by the user here just for testing purpose. A similar approach can be used to manipulate your infinite stripes. The repo for that code is available at https://github.com/leefsmp/Particle-System but you can find many other particle system implementations out there, this one is a bit specific to my needs.
Hope that helps.

JavaScript for loop issue when drawing object

Somehow i did my research and found out javascript is single threaded !.
I've been trying to figure out , how to make animation via for loop in javascript.
This is what i've done so far.
I'm trying to draw an object on a canvas using javascript. It run the loop it doesn't go step by step. It's just run the loop and draw once instead of 10 times and it ignores the timeout function.
In a single instance , it drew (P/S. Ignore the multiple version cause i was testing it so i removed the context.clearRect(0, 0, context.canvas.width, context.canvas.height); :
JS:
// down button click
down.onclick = function() {
if (!imgLoaded) return;
flag=false;
setTimeout(function() {
for(var i = 0; i < 15 ; i++) {
posY += 10;
context.drawImage(img, posX, posY );
}}, 9);
// call next step
That's not the way setTimeout works. The way you wrote it, setTimeout calls its first parameter one time, after 9 ms, then loops with no delay.
You could use the setInterval function (don't forget to removeInterval after the 15th iteration) if you want to be called a regular interval.
Note that in both case (setTimeout, setInterval), given delay is only indicative.
What you might want to use is the Window.requestAnimationFrame() function, which is the usual way to do animation in the browser.

Waiting for animations in a SetInterval gameloop

So my problem is I want to insert a custom animation but I don't want to ruin my gameloop.
My initial gameloop is stated here:
function init(){
if(!gameOver){
if(resetInterval>-1) clearInterval(resetInterval);
createBlock();
resetInterval = setInterval(moveDownCheck,gameSpeed);
}
}
My game is a tetris like game except instead of dropping tetriminos, I drop 2x1 blocks of different color. The moveDownCheck method checks if there are any blocks under my 2x1 block and then drops it by 1 row. This works fine until I have a block hanging without a block underneath since the 2x1 blocks are connected. I want to insert a drop animation that would take about a second and drop the hanging block by the same gameSpeed increment.
Here is my attempt that doesn't work:
function moveFallingDown(){
fbDownFlag = false;
clearInterval(resetInterval);
fbInterval = setInterval(function(){
fallingBlock.row++;
console.log("Dropped One Row");
},gameSpeed);
while(landscape[fallingBlock.row+1][fallingBlock.col]==0){
console.log("Waiting to Drop Falling Block");
}
clearInterval(fbInterval);
resetInterval = setInterval(moveDownCheck,gameSpeed);
}
Here I am attempting to wait for the function(){fallingBlock.row++;}, but my game just crashes and in the console "Dropped One Row" yet "Waiting to Drop Falling Block" will display thousands of times.
I guess I shouldn't be using a while loop here, but the only other solution I can think of would be a complete rework of my design, or nested setInterval methods which would just make my head hurt too much.
You can't do this with a while loop, you need to use a recursive function. window.setTimeout would work, however this seems like a good use case for requestAnimationFrame. Check it out here: https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame
You can use that to call your moveFallingDown method, and check how long has passed since the last animation frame to move your animation the right amount according to the game speed, by using the high precision timestamp passed to the requestAnimationFrame callback.
#Adrien Delessert's advice is correct but I'll just add that you're definitely confusing setTimeout and setInterval.
First of all you don't need a while loop.
setInterval IS a loop. So, if you wanted to use it you'd need to wrap (basically) the whole game in a method that moves the game forward (whatever that means) and pass that to setInterval.
However, what you're doing (and this is actually not a terrible approach) is to use it as a means to animate specific things. In that case you should have a recursive(ish) function that keeps calling setTimeout when it's done, if the conditions for another round of animation are met.
I haven't ever used requestAnimationFrame, but that does sound like a much more elegant approach to the problem.
The reason it's better is that it leverages the browser's own refresh timer (about 60 times per second) and will slot your animation frames in along with its own refresh.
So yes, you will listen for that callback and then react to it as necessary. If 60x per second is too fast, you'll need to put in a % based counter for how many of those frames you wish to actually react to.

How to drop Frames in HTML5 Canvas

I am making a small game in HTML5 with the canvas elements. It runs great on MOST computers, but it is lags on others. However, it doesn't skip frames, it continues to render each frame and the game slows down. I am trying to write a function to skip frames, but I can't come up with a formula to do it.
I've tried searching around, but I have found nothing.
I have a function that renders the game called render and it is on a loop like this:
var renderTimer = setInterval("render(ctx)", 1000/CANVAS_FPS);
render()
{
/* render code here */
}
Thank you for any help,
Brandon Pfeifer
This pattern will allow you to skip frames on computers known to be slow
var isSlowComputer=true;
var FrameSkipper=5;
function render(){
// if this is a slow computer
// just draw every 5th frame
if(isSlowComputer && --FrameSkipper>0){ return; }
// reset the frame skipper
FrameSkipper=5;
// draw your frame now
}
If your target market is people with HTML5 capable browsers, you can just use window.requestAnimationFrame. This allows all of your rendering logic to be bound in a simple place, and it will slow down only if it has to. It will try hard to reach the 16ms per frame allocation, which gets you to your 60fps.
var canvas = document.getElementById("#canvas");
(function drawFrame(){
window.requestAnimationFrame(drawFrame, canvas);
// your main code would fire off here
}());
As long as you let the browser figure out the frame rate you're golden.
I've written some different experiences using the canvas before, and until I used requestAnimationFrame things were a little choppy.
One other thing to keep in mind is the double buffer. If you are going to write a lot of things to the screen at any given moment, I find it is easier to write them all to a second hidden canvas element, then just use context.drawImg(buffer,0,0); that will get rid of a lot of the chop. As long as you have thought your code through the canvas shouldn't get choppy even under a lot of streign.
Good Luck

Categories

Resources