Simple canvas game

Sometimes it can be difficult, for beginning developers to grasp the inner mechanics of a simple game. To help people understanding i created a very simple starting point for a canvas based game.

character-canvas

The code

You can download the code in this repo https://github.com/jeroenoliemans/canvasletter After downloading you can run “npm install” to install the dependencies and “webpack-dev-server” to run the code. By default the server will point to localhost:8080 .

First i will begin with a walkthrough of the code.The games consists of 2 classes the Character class is responsible for the character and the CharacterCanvas class is responsible for creating the Character instances, controlling these instances, stopping and starting the game and giving feedback over the scoring.

The Character

class Character {
    constructor() {
        this.characterX;
        this.characterY;
        this.characterChar;
        this.reset();
    }

    reset() {
        this.characterX = (Math.floor(Math.random()*(document.body.clientWidth)-30) + 15);
        this.characterY  = 0 - Math.floor(Math.random()*1000);
        this.characterChar = String.fromCharCode(97 + Math.floor(Math.random() * 26));
    }

    setyPos(newy) {
        this.characterY = newy 
    }
};
 
export default Character;

The Character has a reset method which moves the character back to the top on a random position if the user entered key matches the instances CharacterCode. The setY method is used, to position the Character instance from the CharacterCanvas class.

The CharacterCanvas

The CharacterCanvas has a few important methods:

  • createCanvas: creates the canvas and sets the defaults
  • createCharacters: this creates the Character instances and starts the game loop
  • startGameLoop: the actual game, which will be discussed in the next section

The game loop

startGameLoop() {
        this.clearCanvas();
        for(let i = 0; i < this.characters.length; i++) {
            let char = this.characters[i];
            this.ctx.font = '62px serif';
            this.ctx.fillText((char.characterChar).toString(), char.characterX, char.characterY);
            char.setyPos( char.characterY + this.velocity);

            // remove if off screen
            if( char.characterY > this.canvas.height ){
                this.characters.splice(i,1);
            }
        }
        
        // fontsize  for ui
        this.ctx.font = '36px serif';

        // start loop
        if(this.characters.length !== 0) {
            requestAnimationFrame(() => this.startGameLoop() );
        } else {
            this.isPlaying = false;
            this.ctx.fillText('GAME OVER', 50, 50);
            this.ctx.fillText('Hit space to restart', 50, 90);
            // write highscore
            if(this.score > this.getHighScore() ) {
                localStorage.setItem('textHighScore', this.score);
            }            
        }

        // display score
        this.ctx.fillText(`Score (${this.highScore.toString()}) : ` + (this.score).toString(), this.canvas.width - 250, this.canvas.height - 50);      
    }

This method clears the canvas,loops through all the available Character instances being hold in the this.characters array and updates their new position. If a character moves off screen than it is removed from the this.characters array. If there aren’t anymore characters left than the loop ends the game and displays ‘GAME OVER’.

The game is boring

It is, hence i kept the game to a bare minimum to improve fast understanding of the game mechanics. Nevertheless it is easy to make the game more interesting. We can improve the game or add the following features.

Create Levels

  • Multiply the velocity if the player reaches a score of 50, 100 ..
  • You can also use the modulus operator to create infinite levels

Add more difficult characters, with more speed and another color

  • Add a color property to the Character class
  • Add a velocityMultiplier property to the Character class, which defaults to 1
  • In the CharacterCanvas class multiply the velocity with the velocityMultiplier
  • Change the velocityMultiplier and the color of the Characters instances in the createCharacter method

What’s next

This game principle could be easily extended, with image sprites, to create a space invaders ore a tetris clone. And of course the code should be divided in more modules, for example modules for scoring, user events and configuration/constants.

Collapsible Table module

Last week I was given the task to create a collapsible datatable, which confronted me with some oddities in the dom which I wasn’t aware of.

Get the example at github-collapsible-table

I had given myself the following requirements

  • The code needs to be extensible
  • The table should be just a table, no extra markup
  • It should be easy to rewrite the table for a framework
  • Keep it simple
  • No jQuery

simple collapsible table

Vertical table headers

My first attempt was css transforms, easy to adjust and to implement. However the problem was that I needed extra markup to keep the header in the normal page flow. After some consideration is seemed to me that SVG is the best option, it is scriptable, stylable and it maintains the normal paqe flow. See the snippet below

<th>
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="150">
    <text text-anchor="end" transform="rotate(90, 12, 0) translate(160,10)">Vertical header
    </text>
  </svg>
</th>

cellIndex to the rescue

I needed to have a proper way to get track of the current clicked cell and it column index. Luckily JavaScript provides a native property element.cellIndex.

Rowspan messing up

One thing to notice is that each subsequent rowspan decreases the cellIndex by one. This needs to be compensated for, in the main script loop. The script loops through all the toggle elements and creates an array with their corresponding cellIndexes. On toggle click these cellIndexes are used to get the cells between two toggle elements and push them into an array. This array can be used to manipulate the cells ( hide/ show ).

Disadvantages

Since each cell is manipulated independently it is not possible to create an block animation. If that is a requirement than markup should be added to create containers for the cells.