2
\$\begingroup\$

I'm trying to create a game in HTML canvas, but somehow it keeps flickering, I've taken a look at the tutorial and I perceived that my code is more or less similar. Can anybody help me fix this?

var canvas = document.getElementById("screen");
var ctx = canvas.getContext("2d");
var pwidth = 16;
var pheight = 16;
var cwidth = 12;
var cheight = 12;
var mariox = 16;
var marioy = 120;
var tiles;
var marioSprite;

function drawMarioSprite(){
    marioSprite = new Image();
    marioSprite.src = 'file:///C:/Users/User/Desktop/img/characters.gif';
    marioSprite.addEventListener('load', e => {
    ctx.drawImage(marioSprite, 276, 44, pwidth, pheight, mariox, marioy, cwidth, cheight);
    //pic x and y, pic width and height, canvas x and y, canvas width and height
    });
}//drawMarioSprite() end

//draw floor
function draw() {    
    tiles = new Image();
    tiles.src = 'file:///C:/Users/User/Desktop/img/tiles.png';
    tiles.addEventListener('load', e => {
        drawTiles();
    });
}
function drawTiles(){
    for (let x = 0; x < 25; x++){
        for (let y = 11; y < 13; y++){
                ctx.drawImage(tiles, 0, 32, pwidth, pheight, x*12, y*12, cwidth, cheight);
        }
    }
}//drawTiles() end


function clear(){
    ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function component(){
    drawMarioSprite();
    draw();
}
function level1(){
    console.log('level1');
    clear();
    component();
}



var inter = setInterval(level1, 1000);
\$\endgroup\$
1
  • \$\begingroup\$ My first thought is that you seem to be reloading your images every time before you draw them. Try loading them once, saving the result, and then only redraw in the setInterval using those saved loaded images. You'll probably also want to use requestAnimationFrame instead of setInterval. \$\endgroup\$ Commented May 29, 2020 at 9:42

2 Answers 2

1
\$\begingroup\$

Two things need to be improved in your code:

  1. Preload images
  2. Use requestAnimationFrame instead of setInterval

Demo:

console.clear();
const INTERVAL = 16; //16 ms animation interval
const src = "https://www.c00lsch00l.eu/Games/AA/BulletOrbRed.png";
var CTX = document.getElementById("myCanvas").getContext("2d");
console.log(CTX);
var frame = {
  start: null,
  delta: null,
  count: 0
};

var img = new Image();
img.onload = function () {
  run(test.bind(null, CTX, img));
};
img.src = src;

function run(func) {
  if (!frame.start) frame.start = performance.now();
  frame.delta = performance.now() - frame.start;
  if (frame.delta >= INTERVAL) {
    func.call();
    frame.start = null; //reset
    frame.count++;
  }
  //for this example the loop will be repeated only 100 time
  if (frame.count < 100) requestAnimationFrame(run.bind(null, func));
  //without counting limit
  //requestAnimationFrame(run.bind(null, func));
}

function test(CTX, img) {
  //simple function for demonstration
  // avoid using console in animation loop, except for debugging
  // console operations are very slow
  console.log(`${frame.count}`);
  draw(CTX, img);
}
function draw(CTX, img) {
  CTX.clearRect(0, 0, CTX.canvas.width, CTX.canvas.height);
  CTX.drawImage(img, frame.count, frame.count);
}
 <canvas id="myCanvas" width="200" height="200"></canvas>

It's a very simple example; first we waited that image was loaded. Then the function run was executed, providing a simple animation, and a a count of frame in the console. Together with code comments this should provide some basis for further learning.

\$\endgroup\$
0
\$\begingroup\$

While Lovro's answer is solid, it's worth adding, having had this problem myself, that almost nothing will fix that kind of brute-force tile blitting behavior if there are too many pixels being pushed.

I once resorted to a software rasterizer so that I could blit the whole image in one call(mostly worked :) ).

I think most 2D Canvas games still use static backgrounds for this reason(dirty rectangles and what).

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .