user4586547
user4586547

Reputation:

requestanimationframe drawing with for loop issue

I'm working on a tetris game - still - and am trying to use requestAnimationFrame to draw my T piece on the black board.

This is the problem. the requestAnimationFrame draws the piece 2 times, then stops drawing even though the for loop is still running. That is, after two times, I only see the black background. When I comment out the black background the piece shows up/animates just fine.

I really am at a loss why this is happening.

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

const T = [
        [
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 1, 1, 1, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],

       [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 1, 1, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],

       [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 1, 1, 1, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0]
        ],

        [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 1, 1, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],
    ]

    var piece = T[0];

    const player = {
        position: {x: 5, y: -1},
        piece: piece,
    }

function colorPiece(piece, offset) {
    for(y = 0; y < piece.length; y++) {
        for(x = 0; x < piece.length; x++) {
            if (piece[y][x] !== 0) {
                ctx.fillStyle = "red";
                ctx.fillRect(x + offset.x, y + offset.y, 1, 1);
            }
        }
    }
}

function drawCanvas() {
    ctx.fillStyle = "black";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.scale(20, 20);
    colorPiece(player.piece, player.position);
}

function update() {
        drawCanvas();
        requestAnimationFrame(update);
}

update();

Upvotes: 4

Views: 227

Answers (1)

cxw
cxw

Reputation: 17041

OK - working version, with a fiddle here. A number of changes. The biggest are:

  • Don't use canvas.scale(), since it's cumulative per this (see "More Examples"). Instead, use 20*x and 20*y for blocks 20x20.

    Edit Based on a further test, it looks like this was the most significant change.

  • Rename so that piece is not used as all of a variable, a field name in player, and a parameter of colorPiece

  • Move the ctx creation into update() (now called doUpdate()) per this fiddle example. Pass ctx as a parameter to other functions.

  • Move the red fillStyle assignment out of the loop, since you only need to do it once, and then you can draw all the rectangles without changing it.

  • In the loops:

    for(var y = 0; y < thePiece.length; ++y) {
        for(var x = 0; x < thePiece[y].length; ++x) { ... } }
    

    Keep x and y in the local scope, using var.

    When you are ready to go across a row, that's thePiece[y].length, i.e., the length of the row. Using thePiece.length there would have broken for non-square elements of T.

  • Added a <p id="log"/> and a javascript framenum so that I could see that doUpdate() was indeed being called.

If you haven't already, make sure to open the console while you're testing so you can see error messages. If drawCanvas causes an error, it will prevent requestAnimationFrame from being called again. I think that's why the fiddle I linked above calls requestAnimationFrame at the beginning rather than the end of the frame-draw function.

Hope this helps!

Code

const T = [
        [
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 1, 1, 1, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],

       [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 1, 1, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],

       [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 1, 1, 1, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0]
        ],

        [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 1, 1, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],
    ]

    const player = {
        position: {x: 5, y: -1},
        piece: T[0]
    }

function colorPiece(ctx, thePiece, offset) {
    ctx.fillStyle = "red";
    for(var y = 0; y < thePiece.length; ++y) {
        for(var x = 0; x < thePiece[y].length; ++x) {
            if (thePiece[y][x] !== 0) {
                ctx.fillRect(20*(x + offset.x), 20*(y + offset.y), 20, 20);
            }
        }
    }
}

function drawCanvas(ctx) {
    ctx.fillStyle = "#000000";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    colorPiece(ctx, player.piece, player.position);
}

var framenum=0;

function doUpdate(timestamp) {
        document.getElementById("log").innerHTML = framenum.toString();
        ++framenum;
        var canvas = document.getElementById("canvas");
        var ctx = canvas.getContext("2d");

        drawCanvas(ctx);
        window.requestAnimationFrame(doUpdate);
}

doUpdate();

Upvotes: 1

Related Questions