Workshop - Graduação em Jogos Digitais

Transcrição

Workshop - Graduação em Jogos Digitais
Workshop
Jogos em HTML5 usando PhaserJS
Quem sou eu ?
Fernando Lunardelli
● Técnico em Eletrônica pela ETP
● Graduado em Gestão de TI pela Unisul
● Pós Graduado em Desenvolvimento de Jogos Digitais pela PUCRS
Desenvolvo sistemas desde 2000 e jogos desde 2010.
Email: [email protected]
Github: https://github.com/flunardelli
Twitter: https://twitter.com/flunardelli
Sobre este workshop
-
Destinado a iniciantes
Faremos um jogo em 2D
Usando tecnologia HTML5
Para fazer jogos digitais é necessário programar !!! ?
Serão vistos alguns princípios básicos
Uma versão alternativa deste treinamento pode ser
encontrado em: https://leanpub.
com/html5shootemupinanafternoon
Por que jogos em 2D e HTML5 ?
2D
● Simplicidade
● Importância (ex: retro, top 10 nas mobile stores)
HTML5
● Portabilidade - Web, Desktop, Mobile
● Facilidade - Rápida prototipagem, Javascript, Typescript
● Muitas funcionalidades - Canvas, WebGL, Audio, Video,
Gamepad, Websockets, Workers
O que faremos ?
Shoot 'em up baseado
em 1942 da Capcom
Veremos:
●
●
●
●
●
●
sprites
animações
física
movimentação
colisões
grupos
Nosso motor
PhaserJS
● Game engine para jogos em
HTML5
● Criado em 2013 por Richard
Davey @photomstorm, usando
TypeScript e depois portado para
JS puro.
● Baseado no engine Flixel (AS3)
● Open Source
http://phaser.io
Configuração inicial
Precisamos de:
1.
2.
3.
4.
Arquivos iniciais: http://jogos.faccat.br/workshop-html5/game.zip
Editor de texto / código - Sublime
Servidor web - Wamp
Navegador web - Google Chrome
Instalação
- Após a instalação do servidor web, descompacte o
arquivo game.zip para a raiz do servidor. Ex: c:
/wamp/www/game (verifique se este caminho é válido no
seu computador)
- Todos os testes do jogo devem ser feitos através do link
(abra no navegador web):
http://localhost/game
Arquivos da pasta "game"
index.html: Arquivo inicial a partir de onde o web browser irá
carregar todos os demais conteúdos do jogo.
Phaser-arcade-physics.min.js: Arquivo js minificado
contendo o phaser e o plugin de arcade-physics.
game.js: O conteúdo do nosso jogo.
assets: Diretório contendo as imagens e sons que serão
usados no jogo.
game/index.html
(abra um editor, copie o conteúdo abaixo e salve o arquivo. Teste no navegador.)
<html>
<script src="phaser-arcade-physics.min.js"></script>
<script src="game.js"></script>
</html>
Javascript básico (para conhecimento)
var nome_variavel = 'valor tipo texto';
// Variável do tipo string
var outravariavel = 1234.40;
// Variável do tipo numérica
var um_vetor = [3,4,99,'cool'];
//Array contendo 3 itens
um_vetor.push(77); //adiciona um valor
um_vetor.pop(); //Remove o último valor
var um_objeto = {prop1: 1234, prop2: 'boa
noite'}; //objeto
console.log(um_objeto.prop1); // 1234
function minhaFuncao(v) {
var msg = 'teste' + v;
return msg;
};
//definição de função
minhaFuncao(123); // teste123
If (outravariavel === 123){
console.log('É verdade');
} else {
console.log('Não é verdade');
}
//Condicional
game/game.js
(abra um editor, copie o conteúdo abaixo e salve o arquivo. Teste no navegador.)
window.onload = function() { //cria o jogo após o carregamento da página.
function preload() {
};
function create() {
};
function update() {
};
function render() {
};
var game = new Phaser.Game(800, 600, Phaser.CANVAS, null, {preload:
preload, create: create, update: update, render: render}); //objeto global do jogo
};
Game loop
preload
create
update
render
http://gameprogrammingpatterns.com/game-loop.html
● Preload
Carregamento de arquivos (imagens, mapas, audios, etc)
● Create
Ação de início do jogo. Criação de sprites, definição de ambiente,
física, etc
● Update
É onde acontece o laço do jogo (game loop), ou seja, toda a
lógica que precisa ser atualizada constantemente,
movimentações, controle de colisões, etc…
● Render
Usada para efeitos de visualização (render), debug, plugins ...
Sprite
É o elemento primordial do jogo 2D.
São as imagens que compõem o jogo.
Normalmente os jogos utiliza agrupamentos de
sprites em uma única imagem - Spritesheets.
Animações são feitas com vários Sprites
apresentados em sequência.
Adicionando sprites
// preload - coloca código abaixo dentro da função preload de
game.js e salvar.
game.load.image('sea', 'assets/sea.png');
game.load.image('bullet', 'assets/bullet.png');
// create - idem ao código anterior só que função create.
sea = game.add.tileSprite(0, 0, 800, 600, 'sea');
bullet = game.add.sprite(400, 300, 'bullet');
Coordenadas x,y
Sistema cartesiano
Sistema de tela
Animações
// preload
game.load.spritesheet('enemy', 'assets/enemy.png', 32, 32);
// create
enemy = game.add.sprite(400, 200, 'enemy');
enemy.animations.add('fly', [ 0, 1, 2 ], 20, true);
enemy.play('fly');
Âncora - centraliza o eixo do sprite
// create
enemy.anchor.setTo(0.5, 0.5);
bullet.anchor.setTo(0.5, 0.5);
Atualização - movimentação inicial
// update
//movimento da bala
bullet.y -= 1; //depois de visualizado na tela comentar usando
"//bullet.y -= 1;".
//movimento do mar
sea.tilePosition.y += 0.2;
Física
//create
//adicionando física
game.physics.enable(enemy,Phaser.Physics.ARCADE);
game.physics.enable(bullet,Phaser.Physics.ARCADE);
bullet.body.velocity.y = -500;
//render
//informações de debug (análise de código)
game.debug.body(bullet); //comentar depois
game.debug.body(enemy); //comentar depois
Colisão
//update
game.physics.arcade.overlap(bullet, enemy, enemyHit);
//Esta função deve ser copiada abaixo da função render
function enemyHit(bullet, enemy) {
bullet.kill();
enemy.kill();
}
Player
//preload
game.load.spritesheet('player', 'assets/player.png', 64, 64);
//create
player = game.add.sprite(400, 550, 'player');
player.anchor.setTo(0.5, 0.5);
player.animations.add('fly', [ 0, 1, 2 ], 20, true);
player.play('fly');
game.physics.enable(player, Phaser.Physics.ARCADE);
player.speed = 300;
Movimentação
//create - adicionando física
cursors = game.input.keyboard.createCursorKeys();
player.body.collideWorldBounds = true;
//update
player.body.velocity.x = 0;
player.body.velocity.y = 0;
if (cursors.left.isDown) {
player.body.velocity.x = -player.speed;
} else if (cursors.right.isDown) {
player.body.velocity.x = player.speed;
}
if (cursors.up.isDown) {
player.body.velocity.y = -player.speed;
} else if (cursors.down.isDown) {
player.body.velocity.y = player.speed;
}
Tiros
//create
bullets = [];
nextShoot = 0;
//update
// intercepta o comando de teclado e executa a função de tiro
if (game.input.keyboard.isDown(Phaser.Keyboard.Z)) {
fire();
}
function fire() { //copiar para abaixo da função enemyHit
var bullet = game.add.sprite(player.x, player.y - 20, 'bullet');
bullet.anchor.setTo(0.5, 0.5);
game.physics.enable(bullet, Phaser.Physics.ARCADE);
bullet.body.velocity.y = -500;
bullets.push(bullet);
}
Frequência de tiros
//deve ser copiado para dentro da função fire
//verifica se o disparo pode ser realizado
if (nextShoot > game.time.now) {
return;
}
nextShoot = game.time.now + 300;
Grupos de Sprites
// create
bullets = game.add.group();
bullets.enableBody = true;
bullets.physicsBodyType = Phaser.Physics.ARCADE;
// Adiciona 100 sprites do tipo "bullet" ao jogo
bullets.createMultiple(100, 'bullet');
bullets.setAll('anchor.x', 0.5);
bullets.setAll('anchor.y', 0.5);
// Restringe os sprites ao mundo do jogo
bullets.setAll('outOfBoundsKill', true);
bullets.setAll('checkWorldBounds', true);
Reutilizando Sprites
function fire() { //substitui a função fire anterior
if (bullets.countDead() === 0) { //verifica se existem sprites inativos ("mortos")
return;
}
if (nextShoot > game.time.now) {
return;
}
nextShoot = game.time.now + 300; //reutiliza o primeiro sprite sem uso encontrado
var bullet = bullets.getFirstExists(false);
bullet.reset(player.x, player.y - 20);
bullet.body.velocity.y = -500;
}
//update
game.physics.arcade.overlap(bullets, enemy, enemyHit); //comente a função overlap anterior
Aumentando o desafio
//create
enemies = game.add.group();
enemies.enableBody = true;
enemies.physicsBodyType = Phaser.Physics.ARCADE;
enemies.createMultiple(50, 'enemy');
enemies.setAll('anchor.x', 0.5);
enemies.setAll('anchor.y', 0.5);
enemies.setAll('outOfBoundsKill', true);
enemies.setAll('checkWorldBounds', true);
// adiciona uma animação para cada sprite
enemies.forEach(function (enemy) {
enemy.animations.add('fly', [ 0, 1, 2 ], 20, true);
});
nextEnemy = 0;
//copiar abaixo da função "fire"
function enemyCreate() {
if (nextEnemy < game.time.now && enemies.countDead() > 0) {
nextEnemy = game.time.now + 3000;
var enemy = enemies.getFirstExists(false);
// posiciona o inimigo em uma coordenada aleatória no topo da tela
enemy.reset(game.rnd.integerInRange(20, 780), 0);
// define uma velocidade aleatória
enemy.body.velocity.y = game.rnd.integerInRange(30, 60);
enemy.play('fly');
}
}
//update
game.physics.arcade.overlap(bullets, enemies, enemyHit);
enemyCreate();
Adicionando Placar
//create
game.score = 0;
scoreText = game.add.text(
game.width / 2, 30, '' + game.score,{font: '20px monospace', fill: '#fff',
align: 'center' });
scoreText.anchor.setTo(0.5, 0.5);
//update
scoreText.text = game.score;
//enemyHit
game.score += 1;
Game Over
//adicionar nova função
function playerHit(player, enemy) {
player.kill();
endText = game.add.text(
game.width / 2, game.height / 2 - 60, 'Game Over!!!',
{font: '72px serif', fill: '#fff' }
);
endText.anchor.setTo(0.5, 0);
}
//update
game.physics.arcade.overlap(player, enemies, playerHit);
Jogo Final
arquivo gamefinal.js
Próximos passos ...
●
●
●
●
●
●
●
Tiros dos inimigos
Chefe de fase
Múltiplas vidas
Dificuldade gradativa
Animações
Efeitos sonoros
Suporte Gamepad
Acesse o site phaser.io/examples para mais idéias.
Algumas
Ferramentas
Gráficos
Piskel - Editor de Pixel Art online
http://www.piskelapp.com/
Gimp - Editor de imagens
https://www.gimp.org/
ShoeBox - Ferramenta para edição de spritesheets
http://renderhjs.net/shoebox/
Online Tile Map Editor
http://apps.elias.media/Online-Tile-Map-Editor
Tiled Map Editor
http://www.mapeditor.org/
Audio
Bfxr - Sound effect generator
http://www.bfxr.net/
sfMaker - Sound effect maker
https://www.leshylabs.com/apps/sfMaker/
Audacity - Sound editor
http://www.audacityteam.org/
Obrigado