Adventures in game dev

with HTML5 Canvas

@davetayls


I work at Pogo Kid

Be a sucker for a challenge

... there's always an adventure in it

Frame

the controller


two timing loops
  1. game logic
  2. drawing


Game logic frame

setTimeout ~ 60 frames per second

Use a self calling function


  function logicFrame(){
    // update players position, listen for collisions etc

    // process the game logic at a target of 60fps
    setTimeout(logicFrame, 1000/60);
  }



Draw frame

requestAnimationFrame


  function drawFrame(){
    // kick ass circles and squares

    window.requestAnimationFrame(drawFrame);
  }



requestAnimationFrame

the browser can optimise concurrent animations
together into a single reflow and repaint cycle


  • offload animation timing to the browser
  • higher fidelity (sync with monitor)
  • save's battery (doesn't run when tab not focused)
  • use the rAF shim

Step

the commander


takes the current state and applies

  • interactions
  • time based logic


moves a ship / fires a bullet

Ship



    function Ship(){...}
    Ship.prototype = {

      // we need to be able to position it
      x:0, y:0, w:28, h:16

      // it needs to do stuff
      shoot: function(){...}
      jumpLeft: function(){...}
      jumpRight: function(){...}

      // we need to see it
      draw: function(){
        this.sprite.draw(0, this.x, this.y);
      }
    };

Invader


    function Invader(){...}
    Invader.prototype = {

      // we need to be able to position it
      x:0, y:0, w:28, h:16,

      // how eeeeeeevil is this invader?
      isHit: false, // not at all if it's hit
      points: 10,   // more points for tougher invaders

      // it needs to do stuff
      checkHit: function(x, y){
        if (distance(this.x, this.y, x, y) < 10){
          this.isHit = true; // KAPOOOOOWWWWW
        }
      },

      // we need to see it
      draw: function(){
        this.sprite.draw(0, this.x, this.y);
      }
    };



Collisions

can be an utter headache




Controllers

keyboard, mouse, touch, gestures...

keep state


    shootKey = {
      keyCode: 32, // space
      down: false    };

    window.addEventListener('keydown', function(e){
      if (shootKey.keyCode === e.which){
        shootKey.down = true;
        e.preventDefault();
      }
    });

and then use it

    function logicFrame(){
      if (shootKey.down){
    // trigger shot!
      }
    // other game logic
      setTimeout(logicFrame, 1000/60);
    }

Controller Sources


By triggering actions within the logic frame tick
based on state allows you to use other
sources for your controllers

They just need to be able to tell the game
what state they are in



Game states

a game is not a game
if you can't win, lose or draw

Draw

the still life painter

Canvas

a few basic tasks

  1. We're using 2d context
  2. clear the previous frame
  3. Go through each of the objects
    on stage and...
  4. draw ()
    fillStyle, font, fillRect, fillText
    drawImage

A bit of code



    <canvas id="canvas" width="500" height="500"></canvas>


    var canvas = document.getElementById('canvas'),
      context = canvas.getContext('2d')
    ;


    context.clearRect(0, 0, w, h);
    context.fillStyle = '#000';
    context.fillRect(0,0,w,h);


2d Games ♥ Sprites



drawImage


    var img = new Image();
    img.src = 'http://davetayls.me/space-invaders/sprites.png';


    var spriteWidth = 350,
      spriteHeight  = 170,
      pixelsLeft    = 170,
      pixelsTop     = 10,

      // Where are we going to draw
      // the sprite on the canvas      canvasPosX    = 20,
      canvasPosY    = 20
    ;
    context.drawImage(img,
      pixelsLeft,   pixelsTop,
      spriteWidth,  spriteHeight,
      canvasPosX,   canvasPosY,
      spriteWidth,  spriteHeight
    );

Reusable Class


    function Sprite(img, width, height, positions){
      this.img = img;
      this.width = width; this.height = height;
      this.positions = positions;
    }


    Sprite.prototype = {
      draw: function(position, x, y){
          var pos = this.positions[position];
          context.drawImage(
            this.img,
            pos[0], pos[1], // xy position of sprite
            this.width, this.height,
            x, y            // xy position to draw on canvas
            this.width, this.height
          );
        }
    };

Reusable Class


        var sprite = new Sprite(img, 32, 16, [
            // specify a few sprite locations
            [10, 523],  // green
            [131, 523], // pink
            [191, 523]  // hit
        ]);
        sprite.draw(0, 10, 200);
        sprite.draw(1, 50, 200);
        sprite.draw(2, 90, 200);
    


Let's play already!


http://bit.ly/YThJIE

Your challenge...

fork the repo and make the invaders fight back

http://github.com/davetayls
http://twitter.com/davetayls