27.01.2009

Javascript Tetris Pt 7: Gameplay

Full source code can be downloaded from project home at kjeldahlnilsson.net.

Today we finally get to strap together the actual game. Most of the work is already done by now - we just need to assemble the components. Let’s start by handling transitions between three basic game states; intro screen, playing the game, and game over.

main.js:

var QuickTetris = {

    gameModes: {
        titleScreen: "titleScreen",
        gamePlay: "gamePlay",
        gameOver: "gameOver"
    },

    gameMode: null,

    gotoTitleScreen: function() {
        this.gameMode = this.gameModes.titleScreen;
        Graphics.clearGameContainer();

        Graphics.drawString("- PRESS SPACE TO START -", 0, 0);

        setKeyReaction(function(keyCode) {
            if (keyCode === SPACE_KEY) {
                QuickTetris.gotoGamePlay();
            }
        });
    },

    gotoGameOver: function() {
        this.gameMode = this.gameModes.gamePlay;
        Graphics.clearGameContainer();

        Graphics.drawString("- PRESS SPACE TO RETRY -", 0, 0);

        setKeyReaction(function(keyCode) {
            if (keyCode === SPACE_KEY) {
                QuickTetris.gotoGamePlay();
            }
        });
    },

    gotoGamePlay: function() {
        this.gameMode = this.gameModes.gamePlay;
        Graphics.clearGameContainer();

        this.dropSpeed = 3;

        var fieldXPos = this.getFieldCenteredXPos();

        Field.init("white", "white", fieldXPos, 50);
        Piece.init("white");

        setKeyMemory();
    }

};

See that final call to setKeyMemory()? Instead of immediately reacting to keypresses, like we did in the tests previously, we instead store the last pressed key in a global variable. Our game then reacts to that stored event regularly during each pass of the game loop, preventing user input from disrupting the flow of the game.

util.js:

var currentKeyPress = null;
function setKeyMemory() {
    document.onkeydown = function(e) {
        if (window.event) // IE
        {
            currentKeyPress = window.event.keyCode;
        }
        else if (e.which) // Netscape/Firefox/Opera
        {
            currentKeyPress = e.which;
        }
    };
    document.onkeyup = function(e) {
        currentKeyPress = null;
    };
}

The heart of a typical game is the game loop. For every pass of this loop we react to user input, move the piece downwards in the playing field, and (sometimes) adjust the difficulty of the game. Let’s add that to the Quicktetris object above:

main.js:

    gameLoop: function() {
        if (QuickTetris.gameMode === QuickTetris.gameModes.gamePlay) {
            QuickTetris.reactToKeyPress(currentKeyPress);
            QuickTetris.adjustDifficulty();
            Piece.moveDown(QuickTetris.dropSpeed);
        }
    },

    reactToKeyPress: function(keyCode) {
        if (currentKeyPress === null) {
            return;
        }

        switch (keyCode) {
        case DIR_KEY_DOWN:
            Piece.moveDown(15);
            break;
        case DIR_KEY_LEFT:
            Piece.moveLeft(Piece.State.tileWidth);
            break;
        case DIR_KEY_RIGHT:
            Piece.moveRight(Piece.State.tileWidth);
            break;
        case SPACE_KEY:
            Piece.rotate(true);
            break;
        }

        //Reset key
        currentKeyPress = null;
    }

Finally we need a way of launching the game. We create our default index.html page, which, after loading, calls Quicktetris.startDefaultGameLoop():

main.js:

    startDefaultGameLoop: function() {
        this.gotoTitleScreen();

        // Launch game loop - set it to fire every X milliseconds
        setInterval(this.gameLoop, 50); // Attempting 20 FPS
    }

Aaand we’re done! Go on, try it out yourself!:)

We’ll do a brief post mortem summary of the project in the next and final part.


PreviousNext