Edit: I've reported this as a Chromium bug: https://bugs.chromium.org/p/chromium/issues/detail?id=668257
I'm creating a little canvas game in JS with enemies that can shoot. For testing, I created a flag, declared globally as let fancy = true;
, to determine whether or not to use a "fancy" targeting algorithm. I made it so that pressing P will toggle this flag. My main function, frame
, calls another function, autoShoot
, five times per second. autoShoot
uses the fancy
flag.
Today, something strange started happening; I don't remember what change introduced it. Sometimes, when I press P, autoShoot
acts like fancy
didn't get toggled. I did some debugging and discovered that the new, toggled value is reflected inside frame
, but in autoShoot
, the value isn't updated. It happens intermittently, and sometimes the value in autoShoot
will fix itself (without me having done anything).
I've reduced the code to the following, which still exhibits the problem for me. Try pressing P a bunch of times. For me, the two values get "out of sync" and display differently after pressing P just once or twice:
(I'm running Chrome "Version 54.0.2840.99 m" on Windows 10.)
const canvas = document.getElementById("c");
const width = 0;
const height = 0;
const ctx = canvas.getContext("2d");
const ratio =1;// (window.devicePixelyRatio||1)/(ctxFOOOOOOOOFOOOOOOOOOFOOOOO||1);
canvas.width = width*ratio;
canvas.height = height*ratio;
canvas.style.width = width+"px";
canvas.style.height = height+"px";
ctx.scale(ratio, ratio);
function testSet(id, val) {
console.log(id+": "+val);
document.getElementById(id).innerText = val;
}
let fancy = true;
document.body.addEventListener("keydown", function(e) {
if (e.keyCode == 80) {
fancy = !fancy;
console.log("Set fancy to: "+fancy);
}
});
let bullets = Array(2000);
let lastTime = 0, shotTimer = 0;
function frame(time) {
const dt = (time - lastTime)/1000;
lastTime = time;
if ((shotTimer -= dt) <= 0) {
testSet("frame", fancy);
autoShoot();
shotTimer = 0.2;
}
for (let b of bullets) {}
requestAnimationFrame(frame);
}
function autoShoot() {
testSet("autoShoot", fancy);
}
requestAnimationFrame(frame);
<code>
fancy (frame) = <span id="frame"></span><br>
fancy (autoShoot) = <span id="autoShoot"></span>
</code>
<canvas id="c"></canvas>
Playing around, here are some observations:
- removing any of the following causes the issue to go away:
- any line in the code at the top dealing with the canvas, even just the comment after
const ratio
- the empty for...of loop:
for (let b of bullets) {}
- changing
let fancy =
tovar fancy =
or justfancy =
- putting the whole thing out of the global scope (by using IIFE, onload handler, or block scope)
- any line in the code at the top dealing with the canvas, even just the comment after
- Increasing the size of the
bullets
array increases the frequency that the issue occurs. I think it's because it makesframe
take longer to execute; originally,bullets.length
was only 20, but each loop iteration did some stuff to update the bullet, etc.
Does this happen on your computers? Is there any logical explanation for this? I've tried restarting my browser, no change.
fancy (autoShoot)
is always true – KizerautoShoot
takes some time to follow the update. Weird. – Hallsyvar
in the meantime... – Sarsonconsole
implementation, so I'm ready to blame it for just about anything. – Thaolog
s in – Sarsonfor (let b of bullets) {}
. – Siouxwindow.onload=function(){...}
, which would explain why chrome gets out of sync. Try adding it inside an onload like jsfiddle does to get it working. – Larriganbullets
has length 99 or less – Siouxlet
forvar
. Now I'm just curious as to why the heck this is happening. – Sarsonid
of a DOM element. Does it still happen if you rename your functions? (It still wouldn't explain why there's a difference betweenframe
andautoShoot
though). – Hallsyid
is the<canvas id="c">
, and renaming the functions doesn't change anything. – Sarson