1

I just started learning javascript, and i already faced some problems. I have a very simple "game" where you move red box with arrow keys and there is black background. I saw some topics about the same question and some of the guys says that make a buffer and other half says that don't do buffer because your browser does it for you. But anyway, I just thought that there is something wrong in my code because it's flickering but it's just so simple that i think it should not.

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

canvas.width = 800;
canvas.height = 600;

var mySprite = {
    x: 200,
    y: 200,
    width: 50,
    height: 50,
    color: '#c00'
};
var yvel = 0;
var xvel = 0 ;
var moved = false;
var mspeed = 15;

var keysDown = {};
window.addEventListener('keydown', function(e) {
    keysDown[e.keyCode] = true;
});
window.addEventListener('keyup', function(e) {
    delete keysDown[e.keyCode];
});

function update() {
    if (37 in keysDown) {
    moved = true;
    if(xvel > -mspeed){
        xvel -= 0.5;
        }
    }
    if (38 in keysDown) {
    moved = true;
        if(yvel > -mspeed){
        yvel -= 0.5
        }
    }
    if (39 in keysDown) {
    moved = true;
        if(xvel < mspeed){
        xvel += 0.5
        }
    }
    if (40 in keysDown) {
    moved = true;
        if(yvel < mspeed){
        yvel += 0.5
        }
    }
    mySprite.x += xvel;
    mySprite.y += yvel;
    if(moved == false){
        xvel = xvel * 0.95;
        yvel = yvel * 0.95;
        }
    moved = false;

}

function render() {
    ctx.fillStyle = '#000';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = mySprite.color;
    ctx.fillRect(mySprite.x, mySprite.y, mySprite.width, mySprite.height); 
}

function run() {
    update();   
    render();
}

setInterval(run, 1000/60);

You can try it here:w3school Just copy my code and put it inside tags and change the "MyCanvas" name to "canvas"

FINDarkside
  • 2,102
  • 1
  • 18
  • 26

1 Answers1

4

The double buffering is in fact allready activated on major browsers. What you need to do is to get in sync with the screen, that's what's provide requestAnimationFrame :
https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame

here's the polyfill for requestAnimationFrame :

requestAnimationFrame (rAF) is not standard yet : every 'vendor' i.e. browser (Chrome, FF, Safari, ...) has its own naming.
A polyfill is a piece of code that will 'install' a function in your environement whatever the vendor. Here you will be able to access requestAnimationFrame using window.requestAnimationFrame and you will not care any more about the vendor.
The || operator acts as a 'scan' : it will stop on the first 'truthy' ( == true) expression and return it without evaluating the remaining ones. So the last function definition, for instance, won't get evaluated if msRequestAnimationFrame is defined.
I have to add that the fallback in case requestAnimationFrame is not found is awfull : setTimeout has a very poor accuracy, but thankfully rAF will be found : it is allready there on Chrome(without prefix), Safari (webkit), IE >9 (ms), Firefox (moz) and Opera (o). )

// requestAnimationFrame polyfill
var  w=window, foundRequestAnimationFrame  =    w.requestAnimationFrame ||
                w.webkitRequestAnimationFrame || w.msRequestAnimationFrame ||
                w.mozRequestAnimationFrame    || w.oRequestAnimationFrame  ||
                      function(cb) { setTimeout(cb,1000/60); } ;
window.requestAnimationFrame  = foundRequestAnimationFrame ;

and after exchanging update and render (way better), your run loop will be :

function run() {
   render();
   update();   
   window.requestAnimationFrame(run);
}

run();

in any case you are interested, i discussed the javascript game loop here : http://gamealchemist.wordpress.com/2013/03/16/thoughts-on-the-javascript-game-loop/

Why would you need a timer ? the code above will call run again and again, since run ends by a defered call to run : run will be called next time display is available (typically 60 times per seconds). And you can know about the current time with Date.now().

For more insights about the game loop, rAF, and some time handling issues, see my link above.

Here is your code after some refactoring :

(fiddle is here : http://jsbin.com/uvACEVA/2/edit )

var canvasWidth = 800, canvasHeight = 600;

var canvas = document.getElementById('canvas');
canvas.width = canvasWidth;   canvas.height = canvasHeight;
var ctx = canvas.getContext('2d');

// player definition

var mySprite = {
    x: 200,
    y: 200,
    xvel : 0,
    yvel : 0,
    velIncr : 0.5,
    velAttenuation : 0.95,
    maxVel : 15,
    width: 50,
   height: 50,
   color: '#c00',
  moved : false
};

mySprite.update = function (dt) {
  if (keysDown[37]) {
     this.moved = true;
     if(this.xvel > -this.maxVel){
        this.xvel -= this.velIncr;
      }
  }
  if (keysDown[38]) {
      this.moved = true;
      if(this.yvel > -this.maxVel){
         this.yvel -= this.velIncr;
      }
  }
  if (keysDown[39]) {
      this.moved = true;
      if(this.xvel < this.maxVel){
       this.xvel += this.velIncr;
      }
  }
  if (keysDown[40]) {
      this.moved = true;
      if(this.yvel < this.maxVel){
         this.yvel += this.velIncr;
      }
   }
                     // to have a frame-rate independant game,
                     // compute the real dt in run() and ...
  this.x += this.xvel; // ... use this.x += this.xvel * dt;
  this.y += this.yvel; // ...     this.y += this.yvel * dt;

  if(this.moved == false){
    this.xvel *= this.velAttenuation;
    this.yvel *= this.velAttenuation;
  }
  this.moved = false;
};

mySprite.draw = function(ctx) {
   ctx.fillStyle = this.color;
   ctx.fillRect(this.x, this.y, this.width, this.height); 
};

// Keyboard handling

var keysDown = [];

window.addEventListener('keydown', function(e) {
     keysDown[e.keyCode] = true;
     e.preventDefault();
     e.stopPropagation();
});

window.addEventListener('keyup', function(e) {
     keysDown[e.keyCode] = false;
     e.preventDefault();
     e.stopPropagation();
 });

// requestAnimationFrame polyfill

var  w=window, foundRequestAnimationFrame  =    w.requestAnimationFrame ||
                w.webkitRequestAnimationFrame || w.msRequestAnimationFrame ||
                w.mozRequestAnimationFrame    || w.oRequestAnimationFrame  ||
                      function(cb) { setTimeout(cb,1000/60); } ;
window.requestAnimationFrame  = foundRequestAnimationFrame ;


// main animation loop

function update(dt) {
    mySprite.update(dt); // only one for the moment.
}

function render(ctx) {
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);
    mySprite.draw(ctx);
}

var dt = 16;  // milliseconds elapsed since last call.
              // ideally you should compute it in run(), i didn't for simplicity
function run() {
   render(ctx);
   update(dt);   
   window.requestAnimationFrame(run);
}

run();
GameAlchemist
  • 18,995
  • 7
  • 36
  • 59
  • Ok, thanks for the great answer. So i don't need the old interval anymore? I'll give it a try. Could you please explain what this does?:function(cb) { setTimeout(cb,1000/60); } – FINDarkside Aug 28 '13 at 16:28
  • Oh sry, i do need the old timer. But I still don't understand what that function is for. Sorry for not using code tags in last comment, i tried [code], but seems like it's ` in comments. – FINDarkside Aug 28 '13 at 16:45
  • @FINDarkside About `function(cb){setTimeout(cb,1000/60);}`. It's the last line of the polyfill. If the browser doesn't have `requestAnimationFrame` it will use `setTimeout` (just like the `setInterval` at the end of your code). Do you know what a polyfill is? http://remysharp.com/2010/10/08/what-is-a-polyfill/ – slacktracer Aug 28 '13 at 16:57
  • I'm not sure did I understand what polyfill is, but i'm not really sure do i even need to understand that yet, because I just started learning javascript today (Still using VB.net sometimes and tried c++ once too). But what i would like to know that what does that cb mean? To me it looks like it will call the setTimeout after the setTimeout time has passed, wich makes no sense to me. – FINDarkside Aug 28 '13 at 17:35
  • i added some precisions. – GameAlchemist Aug 28 '13 at 21:05
  • Sorry, but that did't help. I tried that and only thing it did was that it raised the fps to 62 from 60. – FINDarkside Aug 29 '13 at 10:34
  • @VincentPiel I also tried the full code you put there, but it's not working. – FINDarkside Aug 30 '13 at 11:46
  • there was a typo, didn't you watch in the console ? It is working now. – GameAlchemist Aug 31 '13 at 13:17
  • Sry, but that makes no difference, it still flickers. – FINDarkside Sep 12 '13 at 12:56