Desenvolvendo em XNA

GameDev #8: Criando, desenhando e trabalhando com sprites no XNA

O último artigo da coluna GameDev guiou você pelos seus primeiros passos no mundo dos desenvolvedores de jogos. Você conheceu o Visual Stu... (por Sergio Oliveira em 18/03/2013, via Xbox Blast)

O último artigo da coluna GameDev guiou você pelos seus primeiros passos no mundo dos desenvolvedores de jogos. Você conheceu o Visual Studio, ferramenta que usará para desenvolver e programar seu jogo; aprendeu a como criar seu primeiro projeto, analisou as primeiras linhas de código do seu jogo e o colocou para rodar. Nós entenderemos se você tiver ficado decepcionado com a grande tela azul que apareceu na sua frente. Mas anime-se! Hoje você aprenderá o que são sprites e como a trabalhar com eles, além de animá-los na tela. Prepara-se para dar um importante passo no seu aprendizado.

Não esqueça de preparar seu ambiente de desenvolvimento

Se você não acompanhou o último artigo e não tiver o projeto vazio que foi criado naquela oportunidade, poderá fazer o download dele acessando o endereço http://sdrv.ms/ZJ5XxY

Lembrando que, para desenvolver o que é proposto na coluna, é necessário ter instalado em sua máquina o Microsoft Visual C# 2010 Express e o XNA Game Studio, ambos gratuitos.


Se você está utilizando o Windows 8, então talvez tenha algum problema ao instalar o XNA Game Studio. Esse erro ocorre porque o Game Studio instala consigo uma versão do Games for Windows – LIVE nos bastidores. O problema é que há incompatibilidade entre o Games for Windows – LIVE que o XNA está tentando instalar e o Windows 8, e isso pode ser corrigido baixando e instalando manualmente a versão mais recente no seguinte link: http://bit.ly/W96StP.

Ao finalizar a atualização, basta tentar instalar o XNA Game Studio novamente e deverá funcionar.

Entendendo os termos e os conceitos dos gráficos 2D 

Se você não entende o que é um sprite, ou pensa que se trata daquele refrigerante de limão, não se desespere. De hoje em diante você não só saberá o que é um sprite, como também aprenderá a criá-lo e a trabalhar com ele no projeto do seu jogo.

Então já que vamos falar de sprites, não custa nada aprender um pouco mais sobre outros termos e conceitos utilizados por aqueles que produzem jogos bidimensionais, certo?

  • Sprite: é uma imagem 2D que pode ser manipulada independentemente do restante dos gráficos que estão na tela do jogo. Geralmente é o personagem que você controla nos jogos de plataforma, como o Tim de Braid, por exemplo. Porém, ao contrário das demais imagens utilizadas no jogo, possui características definidas em nível de programação, como velocidade, posição, altura e largura. É importante perceber que o computador sempre irá desenhar as imagens como retângulos no jogo e é muito comum nós desenharmos imagens com áreas transparentes para dar noção de desenho não retangular. Ainda fala-se em sprite animado para denominar um sprite que muda de forma a cada determinado intervalo de tempo. 
  • Textura: é uma imagem 2D que é carregada sobre um modelo 3D para criar a ilusão de gráficos bem detalhados.
  • Billboard: é um tipo especial de textura utilizada para representar objetos complexos sem que seja necessário desenhar um modelo 3D completo. Sua ideia consiste em, onde pudesse haver um objeto 3D, haverá um sprite 2D com uma imagem da textura do objeto sobre ele. Obviamente não é uma técnica que substitui um modelo 3D e, por isso, é utilizada somente quando se deseja manter o desempenho do desenho e processamento de uma cena cujos detalhes devem ser voltados a outras coisas.
Confira um exemplo de Billboard no endereço http://creators.xna.com/en-US/sample/billboard
  • Background: essa aqui todos nós conhecemos. É a imagem que fica no plano de fundo nos jogos de plataforma 2D. Fala-se em 3 tipos de backgrounds: estáticos, animados e animados por paralaxe. Como o próprio nome diz, os estáticos não saem do canto e são background para uma tela apenas – os primeiros Ninja Gaiden eram desse jeito. Os backgrounds animados estão presentes nos jogos batizados de scrolling, como Braid. Já os backgrounds animados por paralaxe são formados por mais de um background, cada um deles se movendo a velocidades diferentes, e às vezes em sentidos opostos, dando a falsa impressão de tridimensionalidade e profundidade de cenário.
Não tem como fugir desses termos quando se trabalha com jogos 2D. No entanto, por enquanto você trabalhará apenas os conceitos de desenho, movimento e colisão de dois objetos no jogo, então foque apenas no conceito de sprites.

Porém, antes de sair criando inúmeros sprites e fazê-los colidir uns com os outros, também é importante ter uma noção mínima de como funciona o sistema de coordenadas 2D no XNA, se não será impossível compreender o que se está fazendo.

2D e o sistema de coordenadas da tela

Lembra-se daquelas aulas chatas de geometria plana que você teve no ensino fundamental e até mesmo no ensino médio? Quem nunca quis esganar o professor por causa daqueles inúmeros planos cartesianos desenhados no caderno para calcular a distância entre dois pontos, área do triângulo, e tudo o mais a partir das coordenadas cartesianas, que atire o primeiro esquadro!

Pois bem, saiba que aquilo tudo não foi em vão – você precisará daquele conceito aqui. Recapitulemos como funcionam as coordenadas 2D para que você possa compreender a maneira correta de posicionar os nossos objetos dentro do jogo.

Lá nas aulas de geometria você utilizava o plano cartesiano comum para definir as figuras geométricas:


Perceba que nesse plano a origem dos pontos se dá no canto inferior esquerdo e o valor de Y é crescente quando o seu eixo sobe, e decrescente quando desce.

Quando se trabalha com computadores, no entanto, também se trabalha com o sistema de coordenadas de tela, que é semelhante ao cartesiano:


Analisando a figura, podemos perceber as três diferenças existentes entre esses dois sistemas. Primeiro, a origem dos eixos se dá na parte superior esquerda do plano. Segundo, o valor de Y é crescente quando seu eixo cresce para baixo e, por último, os valores de Y e X terão um valor máximo igual ao da resolução da tela, e que não devem ser ultrapassados.

Ou seja, se você estiver trabalhando em uma tela cuja resolução seja 1024x768, o valor máximo que X poderá assumir é 1024 e Y é 768.

Desenhando um sprite no XNA

Agora sim, vamos começar a usar o XNA para desenhar um sprite na tela do jogo. Para efeitos de demonstração, será utilizada apenas uma imagem simples que pode ser criada no próprio Microsoft Paint – ou você pode pegar qualquer imagem pronta por aí.

Aqui, criei uma imagem de 64x64 pixels, com fundo magenta e que atribui o nome de bola.bmp. Salvei-a em BMP para que pudesse esconder a área em magenta no jogo e deixá-la com fundo transparente. Se eu salvasse-a em JPG, isso não seria possível, pois esse formato não preserva o formato original das cores quando salvamos a imagem. Tente fazer a imagem abaixo no Paint. Se não conseguir, apenas salve-a para utilizar no projeto.


Com a imagem criada, abra o Visual C# 2010 Express e o projeto em branco criado no artigo anterior para criar uma classe que será responsável por agrupar a imagem do sprite e associá-la a algumas propriedades, tais como tamanho, posição e velocidade. Por enquanto essa classe será bastante simples e ao longo dos artigos ela receberá aprimoramentos consideráveis.
Observação: conforme foi dito no artigo anterior, nós faremos uso de alguns termos que são comuns a quem trabalha com programação orientada a objetos e que podem ser totalmente novas para aqueles que nunca nem ouviram falar em programação. A ideia e conceito de classe é um desses termos e para que ninguém fique perdido, novamente sugiro a leitura de uma apostila de introdução a programação orientada a objetos e/ou artigos da Wikipédia que abordem o assunto. Para entender um pouco mais sobre classes, acessem o link http://pt.wikipedia.org/wiki/Classe_(programação)
Para criar uma classe no projeto, clique com botão direito em cima do nome do projeto no Solution Explorer do Visual C# 2010 Express e selecione a opção Add/New Item (Figura 1) e logo em seguida escolha Class e atribua um nome à sua classe – aqui foi atribuido o nome classSprite.cs (Figura 2)

Figura 1

Figura 2

Ao finalizar, o Visual C# 2010 Express mostrará, automaticamente, a classe recém-criada e mostrará uma estrutura básica que foi gerada automaticamente. Vamos trabalhar um pouco nessa estrutura. Comece apagando tudo o que está lá e adicione as linhas que seguem abaixo no seu escopo:



Por enquanto a classe não tem nada de especial, mas aqui cabe explicar um pouco as três propriedades que estão sendo utilizadas:
  • Textura: armazenará a imagem do sprite usando a classe Texture2D do XNA. Essa classe fornece vários métodos que ajudarão a lidar com os sprites que serão utilizados;
  • Tamanho: armazenará o tamanho do sprite utilizando a classe Vector2 do XNA. Essa classe tem duas propriedades – X e Y – que serão utilizadas, respectivamente, para definir a largura e altura do sprite;
  • Posição: tal qual o tamanho, utilizará a classe Vector2 do XNA. Todavia, as propriedades X e Y armazenarão as coordenadas de tela para o posicionamento do sprite.
Agora que já foram criadas a imagem do sprite e a sua respectiva classe no projeto do jogo, é hora de importar essa imagem para o Content Pipeline do projeto. Para fazer isso, basta clicar com o botão direito na pasta Content no Solution Explorer do projeto e selecionar Add/Existing Item (Figura 3). Na janela que abrir, procure pelo local em que você salvou a imagem do sprite e aperte em Add e voilá – a imagem do sprite estará lá na pasta Content do seu projeto (Figura 4)


Figura 3

Figura 4
Você já criou a imagem do sprite, já definiu sua classe no projeto e o importou para o projeto. Agora lhe resta desenhá-lo na tela. Para tanto, será utilizada a classe SpriteBatch (uma classe do XNA que auxilia o desenho de sprites na tela) e a textura/imagem que acabamos de carregar. Nesse caso, essa textura será carregada para dentro da classe classSprite e então você poderá utilizá-la.

Existem diversos meios de desenhar o sprite na tela. Porém, geralmente ou se lê a textura na classe classSprite e desenha no método Draw da classe Game1, ou se adiciona um método Draw dentro da própria classe classSprite que desenhará, a partir da classe, o sprite na tela. Trabalharemos dessa forma por enquanto.

Voltando à classe classSprite, adicione o seguinte método após o método construtor:


Aqui o método está fazendo uso do método Draw da classe SpriteBatch e está recebendo três argumentos – a textura, a posição e a cor. Essa é a maneira mais simples de desenhar algo na tela.
Caso queira conhecer outras formas de utilizar o método Draw, basta acessar o endereço http://bit.ly/cWHXPt
Agora você precisa ajustar a classe Game1. Ao acessar o arquivo, perceba que, no ato da criação do projeto, o Visual C# já criou um objeto spriteBatch no começo do arquivo. Agora crie um objeto classSprite logo após a definição dos objetos graphics e spriteBatch. O resultado deve ser algo mais ou menos assim:


Como havia sido comentado no artigo anterior, é preciso carregar o conteúdo que será utilizado no jogo já no método LoadContent, que possui toda a inicialização gráfica do jogo. Então, busque o método LoadContent na classe Game1 e inicialize o objeto mySprite1:


É aqui nessa parte que o método construtor da classe classSprite se mostra necessário – a partir da inicialização de mySprite1, já se pode definir que “aspecto” ele terá. É possível carregar nele a textura que foi importada há pouco tempo (bola.bmp), a posição inicial do sprite (0, 0 – ou seja, o canto superior esquerdo da tela) e o tamanho do sprite (64, 64). Apesar das poucas linhas de código, até aqui já fizemos muitas coisas.

Uma dica de boa prática de programação é destruir tudo aquilo que foi criado por você após a finalização do jogo. Para quem acompanha a coluna, já deve saber que isso deverá ser feito no método UnloadContent. Adicione a seguinte linha a ele:


Finalmente, você pode ir ao método Draw da classe Game1 para desenhar o sprite na tela:


Aqui, você está simplesmente dizendo ao XNA que começará a desenhar algo na tela (spriteBatch.Begin). Logo em seguida, diga o que você deseja desenhar (mySprite1.Draw) e finalmente diga ao XNA que tudo foi desenhado (spriteBatch.End). Fácil, não?

Uma dica importante aqui: como o projeto está utilizando uma textura que possui áreas de transparência (lembra para quê serve o magenta do fundo da bola?), é preciso dizer ao spriteBatch isso. Para tanto, basta modificar o spriteBatch.Begin para:


Pronto, o circo está armado! Se você seguiu todos os passos atentamente, ao pressionar F5 e mandar o “jogo” rodar, obterá a seguinte tela:


Perfeito – antes você tinha aí uma grande e depressiva tela azul, agora temos uma grande e depressiva tela azul com um ponto preto no canto superior esquerdo! Você pode brincar com o posicionamento da bola lá no método LoadContent, basta alterar os valores que lá se encontram – se divirta, mexa e procure entender como tudo está funcionando mexendo por si próprio antes de seguir para o próximo passo.

Movendo o sprite na tela

Como o objetivo desse projeto é trabalhar com gráficos 2D, é fácil imaginar como será feito para que o sprite se mova na tela, certo?

Na verdade não há nada de complicado aqui. É tudo bem lógico. Para mover o sprite para a direita, basta incrementar a coordenada X da sua posição. Para movê-lo para a esquerda, basta decrementar o valor da coordenada X. O mesmo vale para Y: se quiser que desça, incremente seu valor; se quiser que suba, decremente-o.
É importante não esquecer como funciona o sistema de coordenadas da tela do computador nesse momento.
Na estrutura básica do jogo, o XNA nos fornece um método específico para realizarmos cálculos durante a execução do jogo. É o Update. Aqui, você poderá adicionar as lógicas de cálculo de posicionamento, velocidade e muito mais.

Para que o sprite possa mover na tela, você só precisa adicionar uma única linha de código dentro do método Update:


Aperte F5 e observe a bola em movimento infinito para a direita, até desaparecer da tela. Tá, funciona, mas está feio. Agora é hora fazer algo mais elegante e bonito. O objetivo agora é fazer com que o sprite fique “preso” aos limites da tela do jogo.

Para isso, primeiramente apague a linha que digitou acima no método Update da classe Game1. Logo em seguida, vá à classe classSprite para fazer algumas pequenas modificações.

O que será feito agora é a declaração de uma nova propriedade chamada “velocidade” que indicará a velocidade do sprite nas coordenadas X e Y da tela. Também será definida uma propriedade que armazenará a resolução atual da tela do jogador para que seja possível limitar a área de movimentação do objeto.

Tem-se, então, o seguinte:

Em classSprite.cs

Agora volte à classe Game1 para alterar a inicialização de mySprite1 no método LoadContent:


Perceba que agora, na instanciação do objeto do sprite, você está capturando a largura e altura da tela do jogo com o comando graphics.PreferredBackBufferWidth e Height.

Em seguida, define-se a velocidade do sprite. Ao dizer que ele terá velocidade 1 para horizontal e 1 para vertical, o XNA entende que o objeto se moverá 1 pixel por atualização de tela, tanto na coordenada X, quanto Y – ou seja, ele se moverá na diagonal.

Você já tem a velocidade do objeto e já sabe quais os limites de tela e, consequentemente, de movimentação dele. Agora só está faltando um método que fará com que a bola se mova dentro dos limites da tela.

Na classe classSprite, abaixo do método Draw, adicione um método (chamado aqui de “Mover”) que fará a bolinha se mover dentro dos limites da tela.


Calma, não precisa se desesperar com o tamanho do código e com esses “if”. Basicamente, o que está sendo feito aqui é verificar se o sprite já atingiu a posição limite da tela e, caso tenha atingido, terá sua velocidade invertida.

Ora, é bem lógico. Como havia explicado anteriormente, para mover o sprite para a direita, basta incrementar o valor de X – para mover para a esquerda, basta decrementar. Então, se a bolinha alcançou o limite da direita da tela, o que temos que fazer? Decrementar o valor de sua velocidade no eixo X, pois é a velocidade que fará com que a bola mude de posição com o passar do tempo – correto?

Dessa forma, só lhe resta um passo para finalizar essa etapa: adicionar uma chamada do método “Mover” no Update de Game1 e comentar o comando de alteração de posição da bolinha:


Pronto, está feito! Ao apertar F5 você perceberá que a bolinha se movimentará dentro dos limites da janela do jogo! Para consolidar o aprendizado, tente alterar a velocidade do sprite e busque por aí a fora uma forma de alterar o tamanho da tela do jogo. Não esqueça também de ler atentamente tudo o que foi feito para garantir que não houve nenhuma dúvida.

Hoje você aprendeu a desenhar um sprite na tela, adicionar movimentação a ele e ainda trabalhou com os limites da tela. Também construiu a sua primeira classe – a classSprite – para armazenar os sprites e que será utilizada no jogo daqui para frente. No nosso próximo artigo, continuaremos a utilizar o projeto de hoje e adicionaremos detectores de colisão para duas ou mais bolas no jogo. Fique ligado, estude, tire suas dúvidas e até lá!

O projeto atualizado com o código desse encontro pode ser baixado no endereço http://sdrv.ms/ZJ6bFv
Revisão: Leandro Freire
Sergio Oliveira escreve para o Xbox Blast sob a licença Creative Commons BY-SA 3.0. Você pode usar e compartilhar este conteúdo desde que credite o autor e veículo original do mesmo.

Comentários

Google+
Disqus
Facebook