4

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();
cxw
  • 16,685
  • 2
  • 45
  • 81
  • Would you please [edit your question](https://stackoverflow.com/posts/44852846/edit) to show the code that creates `ctx`? I don't see it in this snippet. Thanks! **Also** please explain "stops drawing" vs. "animates" --- I don't see anything in this code that would make the piece move. Do you mean that after two frames you only get a black rectangle? – cxw Jun 30 '17 at 18:37
  • 2
    My OCD is bugging me to point out that "piece" is spelled "I-E", not "E-I"... just as long as you don't correct the spelling halfway through your code, you're fine though. – Patrick Roberts Jun 30 '17 at 18:38
  • Patrick, your comment made me laugh. Sorry. I think I'm at least consistently spelling it wrong. –  Jun 30 '17 at 18:42
  • Yes, cxw, I only see the black background after the red t shape iterates through twice. –  Jun 30 '17 at 18:43
  • Yeah, I'm still learning about clean code. I tried piece: T[0] and the same issue presents itself. –  Jun 30 '17 at 18:49

1 Answers1

1

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();
cxw
  • 16,685
  • 2
  • 45
  • 81
  • Yes! It did not even occur to me the fillStyle needed to proceed the fillRect. I tweaked my code bit by bit according to your suggestions with debugger, and as soon as I put the fillStyle before the fillRect, the problem was fixed. Thank you so much for the help!!! –  Jul 01 '17 at 16:54