Desafios de se criar um jogo em HTML5

Mailson Menezes

openBossa @ INdT

omailson.github.io/frontinrecife2013

Quem sou

Mailson Menezes

blog.mailson.org / @omailson / github.com/omailson

Desenvolvimento da versão HTML5 do Incredible Circus.

Jogue no Facebook

Também disponível para iOS, Android e Windows Phone

Sobre a palestra

  • Problemas iniciais do desenvolvimento do Circus
  • Apresentação de features úteis
  • Organização do jogo
  • Bibliotecas
  • Práticas de programação

omailson.github.io/frontinrecife2013

Situação

  • Versão nativa em C++
  • Outra versão em C#
  • Port para uma versão HTML5

Problemas iniciais

Menor controle sobre a plataforma

Estávamos acostumados a trabalhar em um ambiente nativo

Problemas iniciais

Preocupação com a performance do jogo

Problemas iniciais

Experiência do time

Web Apps

Por que só agora?

  • Novas features: Canvas, WebAudio
  • Performance dos browsers

Canvas

  • Elemento HTML de pintura
  • Suporte 2D (canvas2d) e 3D (WebGL)
  • <canvas id="game" width="200" height="200"></canvas>

Canvas 2D

  • Pintura de primitivas: arcos, curvas de Bézier, linhas, retângulos
  • Transformação afim: rotação, translação, cisalhamento
  • Composição: clip, blending, alpha

Canvas 2D

Acesso via JavaScript

var canvas = document.getElementById("game");
var context = canvas.getContext("2d");
context.fillStyle = "#c80000"
context.fillRect(10, 10, 55, 50);

context.fillStyle = "rgba(0, 0, 200, 0.5)"
context.fillRect(30, 30, 55, 50);

Image

As imagens não serão imediatamente exibidas, serão desenhadas

Criando uma imagem

var image = new Image();
var imageLoaded = false;
image.onload = function () {
	imageLoaded = true;
};
image.src = "images/smurf.png"

Desenhando no canvas

if (imageLoaded)
    context.drawImage(image, 30, 30);

Desenhando uma seção da imagem

context.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);

Desenhando uma seção da imagem

Útil para o uso de spritesheets

var image = new Image();
image.onload = function () {
    context.drawImage(image, 0, 0, 128, 128, 30, 30, 128, 128);
};
image.src = "images/smurfs_sheet.png"

Estrutura do jogo

Boa separação entre as camadas

  • Cada elemento do jogo tem uma representação nas 3 camadas principais (physics, core e UI)
  • PlayerBody, PlayerEntity, PlayerSprite

Main loop

Chamar o update do jogo em um intervalo fixo de tempo

Main loop

setInterval

function tick() { /* ... */ }
setInterval(tick, 30);

Não temos muito controle sobre o loop do jogo

Main loop

setTimeout

function tick() {
    /* ... */
    setTimeout(tick, 30);
}
tick();

Main loop

requestAnimationFrame

function tick(delta) {
    /* ... */
    requestAnimationFrame(tick);
}
tick();

Bibliotecas

  • Útil para manter a sanidade
  • Armazenamento: localStorage, IndexedDB, Cookies
  • Input: mouseup, mousedown, touchstart, touchfinished, MSPointerUp

Bibliotecas

  • Trate como se você fosse trocar de biblioteca no futuro
  • Às vezes você descobre no meio do projeto que a biblioteca não é adequada

Bibliotecas

  • Decidimos inicialmente não usar o jQuery
  • Receio quanto à performance
  • Criaria uma dependência muito forte com uma biblioteca

Armazenamento

Lawnchair

  • Variadas formas de armazenamento
  • Fallback caso não haja suporte

Audio

Howler

  • Mantendo a sanidade com as funções de audio
  • Suporta WebAudio e HTML5 Audio (<audio>)

Touch

Hammer.js

Touchy.js

Práticas usadas

JavaScript OO

var Person = function (name) {
    this.name = name;
};

Person.prototype.sayHello = function () {
    console.log("Hello, my name is " + this.name);
};

var p = new Person("John Doe");
p.sayHello(); // Hello, my name is John Doe

JavaScript OO

Introduction to object-oriented JavaScript

Simple JavaScript inheritance

Testes automáticos

Não muito usado em games, mas bastante importante

Buster.js

Documentação com geração de API

Melhora o reuso dos componentes

Mais fácil de identificar os tipos

YUIDoc

Mudanças constantes

Browsers evoluem rapidamente

HTML5 Rocks

Obrigado!

omailson.github.io/frontinrecife2013