Pré-requisitos:
- Assistir o vídeo promocional do framework de jogos LibGDX
- Conhecimento de Java
- Um ambiente de desenvolvimento:
- NetBeans com plugin Gradle Support
Objetivos:
- Familiarizar-se com o funcionamento da LibGDX
- Conhecer técnicas comuns de renderização em 2D - Sprites, Animações etc.
- Praticar conceitos de renderização em 2D na LibGDX
- Uso de texturas
- Sprites
- Animações
- Entender a importância de se reduzir o número de "chamadas de desenho" (draw calls)
Você deve começar usando o código do professor como ponto de partida para a atividade. Você deve fazer 3 exercícios:
Desenhar o cenário como duas texturas (Texture
), uma em cima da outra,
carregando map-level-1.png
e map-level-2.png
(dentro do método
void create()
) e mandando desenhar (dentro do render()
) na posição (0, 0) -
canto inferior esquerdo.
-
map-level-1.png
(nível 1: o chão - já está lá) -
map-level-2.png
: (nível 2: as cerquinhas) -
Nota: tipicamente, um cenário em um jogo bidimensional é formado por um grid de tiles, como no RPG Maker/Boss. Contudo, neste exercício, vamos simplesmente desenhar uma imagem "inteira" em cima da outra.
Criar uma Sprite
para o Goomba usando goomba.png
(sem animação ainda) que
pode ser controlada pelo teclado (via setinhas):
- Defina membros na classe
Game
para aSprite
e para aTexture
e as instancie devidamente.- Assim que instanciar a sprite, defina sua posição para (30, 10).
- Desenhe a sprite entre os níveis 1 e 2 do cenário
Agora você deve possibilitar o jogador a controlar a posição do Goomba.
- Na função
update()
, quando o jogador pressionar alguma das setas do teclado, altere a posição da sprite de acordo.- Como exemplo, veja como o pressionar da tecla Esc está encerrando o jogo
- Faça uma verificação da nova coordenada da sprite para que o
personagem não saia da tela
- Basta garantir que sua posição
x
está entre [0
,LARGURA_DA_TELA - LARGURA_GOOMBA
] e o análogo paray
- Para pegar a largura e altura da janela podemos usar
Gdx.graphics.getWidth()
eGdx.graphics.getHeight()
- Esta não é uma solução boa (redimensione a janela e veja o que acontece), mas por ora está ok.
- Para pegar a largura e altura da janela podemos usar
- Basta garantir que sua posição
A esta altura, o código de Game
está um pouco bagunçado e, então, você deve
organizá-lo. A ideia é criar uma classe Goomba
que vai conter o código
relacionado ao personagem, e a Game
vai conter uma instância dela e vai
passar a chamar seus métodos.
- Crie uma classe
Goomba
, com um construtor que recebe apenas a textura do Goomba. - Crie métodos
update()
erender(batch)
, com seus respectivos códigos. - Mova o código de
update()
erender()
relativo ao Goomba deGame
paraGoomba
e, então, chame os métodos doGoomba
emGame
.
Nesta parte, você deve colocar uma animação de movimentação do Goomba. Na
LibGDX existe a classe Animation
que representa "algo que vai trocando
ao longo do tempo" - é basicamente um array de quadros com um índice de
qual é o quadro corrente, que vai trocando ao longo do tempo.
Ao usar Animation
, não será mais possível usar a Sprite
, porque elas são
incompatíveis (na LibGDX - um erro de projeto do framework, na opinião
do professor). Portanto, vamos comentar o código da Sprite
dentro de Goomba
e precisar gerenciar a posição do Goomba
nós mesmos.
- Configuração da spritesheet:
- Largura do quadro:
21px
- Altura do quadro:
24px
- Largura do quadro:
Sugestão de passos:
- Comente todo o código relativo à
Sprite
emGoomba
- Em
Game
create()
, instancie a textura da spritesheet e passe-a para oGoomba
pelo construtor - Nele, divida a textura em vários quadros usando
TextureRegion.split(textura, larguraDoQuadro, alturaDoQuadro)
- Uma
TextureRegion
é um pedaço retangular de uma textura, i.e., um quadro
- Uma
- Defina um membro do
Goomba
e instancie uma animação (no construtor) com os 5 quadros da primeira linha da spritesheet, com um intervalo de tempo de 0.1f segundo- Configure a animação para ficar em loop e em modo de ping-pong (vai da
esquerda para a direita, depois volta para a esquerda):
andarParaFrente.setPlayMode(PlayMode....);
- Configure a animação para ficar em loop e em modo de ping-pong (vai da
esquerda para a direita, depois volta para a esquerda):
- Crie um membro em
Goomba
que receberá o tempo que o Goomba está em animação (i.e.,private float tempoDaAnimacao;
).- Em
update(delta)
, some nela o tempo passado desde o último quadro. - Durante o
render()
, em vez de desenhar aSprite
(que agora está comentada), desenhe o quadro corrente da animação- Repare que não sabemos mais qual é a posição (x,y) do Goomba, visto que
não podemos usar a
Sprite
junto com animações. - Sendo assim, você deve criar um membro
private Vector2 position
e "gerenciar" a posição do Goomba você mesmo.
- Repare que não sabemos mais qual é a posição (x,y) do Goomba, visto que
não podemos usar a
- Em
Em vez de ter apenas uma animação, faça com que o Goomba tenha a animação relativa à direção para onde ele está andando. Além disso, faça ele parar a animação quando estiver parado.
Dica: será necessário ter 4 Animation
s diferentes, uma para cada
direção. Além disso, você pode ter mais uma Animation
que é um ponteiro
para qual das 4 é a animação corrente.
Este trabalho deve ser entregue via Moodle. Mas caso o Moodle ainda
não esteja funcionando de vento em polpa, considere o parágrafo a seguir.
Os exercícios desta aula prática serão corrigidos ao final do nosso horário. Assim que estiver pronto, chame o professor para que possa ver seu trabalho.
- Como desenho uma textura na LibGDX?
- É necessário (1) carregar a textura para depois (2) desenhá-la:
- Carregando a textura dentro do
create()
:@Override public void create() { batch = new SpriteBatch(); algumaTextura = new Texture("nome-do-arquivo"); }
- Desenhando no
render()
, em alguma posição:@Override public void render() { // ... batch.begin(); batch.draw(algumaTextura, x, y); batch.end(); }
- Carregando a textura dentro do
- É necessário (1) carregar a textura para depois (2) desenhá-la:
- Qual a diferença entre uma
Texture
e umaSprite
na LibGDX?- Uma
Texture
é apenas uma imagem que foi carregada pela placa de vídeo. É possível desenhar uma textura, em determinada posição, usando um SpriteBatch, e.g.:batch.begin(); batch.draw(algumaTextura, 25, 0); batch.end();
- Uma
Sprite
é um objeto que possui uma textura e uma posição no mundo. Diferentemente da textura, a sprite "sabe se desenhar" porque ela sabe onde está posicionada:// dentro de update() algumaSprite.setPosition(100, 25); // ... // ... // dentro de render() batch.begin(); algumaSprite.draw(batch); batch.end();
- Uma
- Como funciona essa
Animation
mesmo?- Veja os slides da aula, ou então este breve tutorial sobre animação na LibGDX.
- Não existe uma classe
AnimatedSprite
na LibGDX?- Na LibGDX não, mas você pode criar a sua, que herde da
Sprite
da LibGDX. Na verdade, um membro da comunidade já fez isso e disponibilizou tal classe juntamente com outros utilitários do projetogdx-utils
(veja como "instalar"). - A
AnimatedSprite
dagdx-utils
, no entanto, contém apenas 1 animação. Mas, no nosso caso, temos 4 animações. Para não precisar de criar 4xAnimatedSprite
, você pode criar umaMultiAnimatedSprite
, que herde deAnimatedSprite
mas possa trabalhar com várias animações diferentes.- Na verdade, o professor já criou uma
classe
MultiAnimatedSprite
, que você pode usar, se quiser.
- Na verdade, o professor já criou uma
classe
- Na LibGDX não, mas você pode criar a sua, que herde da