Ponto V!

Home XNA XNA Criando um jogo em 40 linhas
Alexandre Lobão
Criando um jogo em 40 linhasImprimir
Escrito por Alexandre Lobão

Creio que todos já estamos cansados de ouvir que o XNA é simples de aprender, que é poderoso, e vários “etc”. Quem já programou pelo menos um joguinho simples, sabe o quanto isso é verdade. Mas que tal demonstrarmos o quão fácil criar um jogo pode ser com um desafio: fazer um jogo em apenas 40 linhas!

clip_image002[4]

O jogo escolhido – que precisa ser simples – foi um clone do clássico “breakout”, também conhecido como “arkanoid”.

Para este jogo, vamos aprender a criar uma classe derivada do tipo “gameComponent” do XNA, o que permite “cadastrar” os objetos do jogo para que o XNA invoque o método “Update” destes objetos automaticamente.

Para começar, crie um novo projeto do tipo “Windows Game (3.0)” e inclua uma nova classe, batizando-a de “clsSprite” e indicando que ela é derivada da classe “GameComponent” do XNA,

Dentro desta classe, vamos criar três propriedades (por razão de simplicidade, apenas como variáveis públicas dentro da classe) e um construtor que receba estas propriedades, além do objeto Game (que é obrigatório para classes derivadas de GameComponent.

O código fonte desses passos fica assim:

class clsSprite: GameComponent 
{
   public Texture2D textura;
   public Vector2 posicao, velocidade;
   public Boolean visivel = true;
   public clsSprite(Game _game, Texture2D _textura, Vector2 _posicao,  Vector2 _velocidade) : base(_game)
   {
      posicao = _posicao;  textura = _textura;  velocidade = _velocidade;
   }
}

Nesta classe teremos 3 métodos:

    • Update: chamado automaticamente pelo XNA, e que irá mover a sprite com base em sua velocidade, dentro dos limites da tela.
    • Draw: que irá ser chamado do Draw da classe Game1, para desenhar a sprite;
    • Colide: que irá verificar se a sprite colide com outra, recebida como parâmetro;

Confira o código destes métodos a seguir – nada muito sofisticado aqui, exceto pelo método Colide, que basicamente cria dois retângulos com a posição e tamanho das sprites, e depois utiliza o método Intersects do retângulo para verificar se eles se sobrepõe.

public override void Update(GameTime gameTime)
{
   //  Faz a sprite (32 pixels) quicar nas bordas da janela
   if (posicao.Y > 600 - textura.Width || posicao.Y <0)
      velocidade.Y *= -1;
   if (posicao.X > 800 - textura.Width || posicao.X  <  0)
      velocidade.X *= -1;
   posicao += velocidade;
}

public void Draw(SpriteBatch spriteBatch, Color cor)
{
   spriteBatch.Draw(textura, posicao, cor);
}

public bool Colide(clsSprite outraSprite)
{
   Rectangle esteRect = new Rectangle((int)posicao.X, (int)posicao.Y, textura.Width, textura.Height);
   Rectangle outroRect = new Rectangle((int)outraSprite.posicao.X, (int)outraSprite.posicao.Y, outraSprite.textura.Width, outraSprite.textura.Height);
   return esteRect.Intersects(outroRect);
}

Para criar as sprites, calcular as colisões e desenhar o jogo na tela, basta 5 passos, incluindo o código na classe Game1:

1. Definir as variáveis do tipo sprite

clsSprite bastao, bola;
clsSprite[,] tijolos = new clsSprite[12,5];

Nada misterioso aqui: uma sprite para a bola, outra para o bastão, e um array de 5 linhas e 12 colunas para os tijolos.

2. No método Initialize(), criar os objetos do tipo sprite

bastao= new clsSprite(this, Content.Load < Texture2D>("bat"), new Vector2(20f, 560f), Vector2.Zero); 
bola = new clsSprite(this, Content.Load < Texture2D>("ball"), new Vector2(400f, 300f), new Vector2(3, -3));
this.Components.Add(bola);
this.Components.Add(bastao);
for (int x = 0; x  <  12; x++)
    for (int y = 0; y  <  5; y++)
        tijolos[x,y] = new clsSprite(this, Content.Load < Texture2D>("WhiteBrick"), 
           new Vector2(x * 67f, 50f + 34f * y), Vector2.Zero);

A classe sprite espera: o jogo corrente (é o parâmetro this), a imagem/textura a ser usada, a posição inicial e a velocidade inicial do objeto a ser criado. No caso da bola e do bastão, para que o XNA controle a sua atualização, precisamos incluir os objetos como componentes do jogo.

3. No método Update(), incluir código para movimentar o bastão e testar as colisões

bastao.velocidade = new Vector2(GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X * -10, 0f);

if (bola.Colide(bastao))      
   bola.velocidade.Y *= -1;

for (int x = 0; x  <  12; x++)  
   for (int y = 0; y  <  5; y++)
      if (tijolos[x,y].visivel && bola.Colide(tijolos[x,y]))
      {
         tijolos[x,y].visivel = false;
         bola.velocidade.Y *= -1f;
      }

A primeira linha deste código move o bastão com o gamepad do Xbox – basta ligar no PC que funciona. Para usar o teclado, utilize o objeto Keyboard, por exemplo: Keyboard.GetState().IsKeyDown(Keys.Left).

A seguir, o código inverte a velocidade Y (vertical) da bola se ela colide com o bastão, e faz o mesmo, além de tornar o tijolo invisível, caso a bola colida com algum tijolo.

4. Por fim, basta incluir o código de desenho no método Draw()!

spriteBatch.Begin();
bastao.Draw(spriteBatch, Color.White);
bola.Draw(spriteBatch, Color.White);
for (int x = 0; x  <  12; x++)
   for (int y = 0; y  <  5; y++)
      if (tijolos[x,y].visivel)
         tijolos[x, y].Draw(spriteBatch, new Color(
            Convert.ToByte((x * 15 + y *20) % 250), 
            Convert.ToByte((x * 20 + y *40 ) % 250), 
            Convert.ToByte((x * 25 + y * 50) % 250 )));
spriteBatch.End();

O método Draw() do objeto sprite espera o spriteBatch (objeto do XNA usado para desenhar gráficos 2D) que irá desenhá-lo e a cor que ele deve usar para blending, ou seja, qual cor será sobreposta ao objeto. No caso do bastão e da bola, queremos desenhá-los com cor branca, para manter suas cores originais. No caso dos tijolos, criamos uma cor a partir de 3 bytes, usando uma fórmula maluca qualquer para cada elemento RGB da cor. Modifique esta fórmula à vontade para ter diferentes efeitos, só não esqueça de, ao fim de cada cálculo, incluir “% 256”, ou seja, “o resto em relação a 256”, pois isso garante que o resultado da conta ficará sempre entre 0 e 256 , valor máximo para um byte.


5. Jogar!
Isso mesmo, seu jogo já está pronto! Pode jogar. É um jogo simples, com física de colisão muito básica, mas funciona bastante bem para 40 linhas! Baixe o código fonte completo clicando aqui.

E agora? Use a classe sprite para inventar seus jogos, ou altere este jogo para torná-lo mais interessante! E uma chamada geral aos leitores: Enviem suas dúvidas e sugestões sobre o que gostariam de ver aqui na coluna – sua palavra é lei! Happy Coding!


Comentários (14)
  • Lohandus
    avatar

    Olá Alexandre,

    Não poderia ser usado o DrawableGameComponent em vez do GameComponent?
    A cor seria passada no contrutor da ClsSprite, assim o método Draw do Game1 poderia ficar com umas 3 linhas apenas.

  • Alexandre Lobão
    avatar

    A desvantagem de usar o DrawableGameComponent é que para cada sprite o XNA vai chamar o BEGIN, desenhar a sprite e chamar o END.
    O Spritebatch, usado para desenhos 2D, é extremamente eficiente ao desenhar "batches", ou seja, conjuntos de desenhos. Assim, se você chama o BEGIN, usa o DRAW para desenhar 500 sprites e chama o END, você vai à placa gráfica apenas uma vez, com 500 operações. Se você chama o bloco BEGIN/END cada vez que desenhar uma sprite, neste exemplo ele iria à placa gráfica 500 vezes, tornando o programa muitíssimo mais lento. :(
    :idea: Lógico que, para jogos simples, isso pode não ser sensível, mas mesmo assim o ideal é criar algo otimizado, que pode continuar a ser usado caso o jogo fique mais sofisticado!

  • Lohandus
    avatar

    Entendi...
    Eu pensava que quando era feito begin/base.draw/end no método Draw do Game1 o XNA não executava um begin/end para cada DrawableGameComponent.
    É melhor então usar GameComponent ou DrawableGameComponent apenas como "managers".

  • Kleber de Oliveira Andrade
    avatar

    Não necessariamente, se você adicionar o spriteBatch da classe game ao serviços do jogo, você pode recuperar sua referência na classe tijolos e não precisaria de Begin() e End() pois a referência de desenho seria da classe Game1.cs

  • Alexandre Lobão
    avatar

    Beleza, Kleber, concordo!
    Com certeza há diversas formas de resolver a questão; algumas mais sofisticadas, outras mais simples, outras simplesmente diferentes.
    Como os artigos são para iniciantes, eu busco uma abordagem que simplifique a abordagem, o que não necessariamente passa por uma solução mais elegante ou mais otimizada.
    Vale a dica para todos os outros: usem o código como uma base para começar a trabalhar, mas semore procurem formas criativas de modificá-lo, pois é nisso que reside a arte da programação! ;)

  • Luis Filipe de Oliveira  - Considerações sobre o jogo
    avatar

    Muito legal.. mas eu consegui apenas com o codigo fonte baixado aqui e com um recurso. Esse arquivo AssemblyInfo.cs nao tinha, fiz um txt vazio e renomei ele e funfo o jogo. Mas pelo tutorial eu não consegui. Porque tem alguns coisas que não fala no tutorial como, a bolinha tem q ter no projeto e a barrinha tbm né?

    Mas vlws mesmo.. para tentar comecar já é algo..

    só uma duvida: Porque a bolinha quica no fundo?(É porque ele bate em todos os lados.. temos que fazer no fundo cair?) Porque a barrinha some quando colocado do lado?

    Mesmo assim.. Parabéns

  • Sérgio Matos  - Erro
    avatar

    Olá gostei do tutorial mas não consigo compilar o código mesmo usando o código fonte dá erro aqui nesta linha de código

    Invalid expression term '

  • Sérgio Matos  - Erro
    avatar

    Não consigo compilar o código fonte dá erro na Linha 49

    Invalid term expression '

  • Bruno Crivelari Sanches
    avatar

    bom, aqui continua compilando sem problemas. Agora erro na linha 49 de qual arquivo? Aqui o único que tem mais de 49 linhas (o Game1.cs) não possui nada na linha 49.

    T+

  • sergio matos
    avatar

    essa instruçao na linha 49 arquivo game1.cs não deixa compilar

  • sergio matos
    avatar

    podes me dar o teu messenger aqui nem deixa escrever a instruçao que dá erro e lá explicava mellhor

  • sergio matos
    avatar

    Problema com caractere. comentário vazio

  • sergio matos
    avatar

    Onde dá erro é no metodo initialize na ultima instrução vê a ai no tutorial que está no site é na linha 8 do metodo initialize

  • Bruno Crivelari Sanches
    avatar

    Falta de caracteres de escape, corrigido.

Escrever um comentário
Your Contact Details:
Gravatar enabled
Comentário:
[b] [i] [u] [url] [quote] [code] [img]   
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch::(:shock:
:X:side::):P:unsure::woohoo::huh::whistle:;):S:!::?::idea::arrow:
Security
Por favor coloque o código anti-spam que você lê na imagem.
LAST_UPDATED2  

Busca

Linguagens

Twitter