Pessoal, a pedido de uma galerinha estou publicando novamente um artigo que fiz a algum tempo sbore XNA, neste artigo irei montar um jogo muito famoso – PONG, que foi o primeiro game a ser desenvolvido comercialmente.

Bem espero que aproveitem!

O videogame Pong, criado por Nolan Bushnell e Ted Dabney, foi instalado num console ligado a um monitor e era movido a moedinhas.
Para testar a sua nova criação, os inventores deixaram a máquina em um bar em San Francisco, Califórnia. No dia seguinte, tiveram uma surpresa quando checaram a máquina e viram que ela estava lotada de moedas. Pong havia sido jogado durante toda a noite. Aquelas moedas mudaram toda a perspectiva que os criadores tinham em relação a seu invento. Aí estava o primeiro videogame lucrativo da história.

Nolan Bushnell e Ted Dabney perceberam que, em vez de vender a idéia para alguém, era melhor abrir o seu próprio negócio. E em 27 de Junho de 1972, a empresa Atari foi fundada.

O objetivo era acertar a esfera (bola) com sua raquete (barra vertical) e lançar para o campo adversário, marcando ponto quando a bola passasse ao campo adversário.

xna_parte_II_00

Figura 1 – Primeira máquina desenvolvida com o jogo Pong – (Fonte: Wikipedia)

Agora que sabemos um pouco da historia de nosso “futuro” jogo, vamos ao que interessa.

Mas antes de começarmos a codificar, vamos dar uma olhada em alguns tópicos que serão cruciais, entre os mais importantes podemos destacar: Sistema de Coordenadas, Sprite, Sistema de Colisão e entrada de dados (Teclado), para que possamos entender da melhor forma possível o desenvolvimento de um simples jogo.

Sistema de Coordenadas

O sistema de coordenadas adotado pelo XNA não é o sistema convencional onde temos nosso ponto inicial na parte inferior esquerda conforme figura 2. No XNA nosso ponto inicial começa no canto superior esquerdo conforme figura 3.

xna_parte_II_01
Figura 2 – Sistema de Coordenadas Convencional

xna_parte_II_03

Figura 3 – Sistema de Coordenadas adotado pelo XNA Framework

Assim sendo, para que possamos desenhar uma imagem na tela, devemos indicar sua origem partindo do canto superior esquerdo, o mesmo vale para a imagem, onde seu ponto de origem não é o centro da imagem, mas o canto superior esquerdo conforme figura 4.

xna_parte_II_02

Figura 4 – Posicionamento de Imagem

Como podem ver na figura 4, posicionamos nossa imagem no ponto X = 80 e Y = 80 partindo do ponto de origem X = 0 e Y = 0 no canto superior esquerdo. Para trabalharmos com nosso sistema de coordenadas iremos utilizar a classe Vector2, onde iremos informar as posições X e Y respectivamente. Como exemplo temos a listagem 1.

Listagem 1 – Posicionando uma imagem na tela

Vector2 posicao = new Vector2( 80, 80 );


Sprite

Em computação gráfica, um sprite (do latim spiritus, significando "duende", "fada") é um objeto gráfico bi ou tridimensional que se move numa tela sem deixar traços de sua passagem (como se fosse um "espírito").

Os sprites foram inventados originalmente como um método rápido de animação de várias imagens agrupadas numa tela, em jogos de computador bidimensionais, usando hardware especial. A medida que a performance dos computadores melhorou, esta otimização tornou-se desnecessária e o termo evoluiu para referir-se especificamente às imagens bidimensionais que eram integradas numa determinada cena, isto é, figuras geradas por hardware ou software eram todas referenciadas como sprites. A medida que gráficos tridimensionais tornaram-se mais comuns, o termo passou a descrever uma técnica elementar de simulação de imagens em 2.5D ou 3D que prescinde do uso de renderizações complexas

Para que possamos carregar uma Sprite, utilizamos a classe Texture2D. E para desenharmos nosso Sprite na tela utilizamos a classe  SpriteBatch

Sistema de Colisão

O Sistema de colisão nada mais é do que verificarmos se dois objetos estam se sobrepondo. Esse tipo de sistema pode ser simples (BoundingBox e BoudingSphere) ou mais complexos (Colisão por Pixel). A colisão entre Sprites pode ser realizadas de várias formas entre as mais comuns podemos citar:

BoundingBox: a colisão é feita utilizando o retângulo da imagem (2D), onde verificamos se ambos estão se sobrepondo. Não é uma das melhores formas de se detectar uma colisão, porque pode ocorrer das imagens não ocuparem todo o retângulo, mas é uma das mais rapidas e mais facil de implementar. Uma das vantagem de se usar BoundingBox é que se migrarmos nosso código 2D para 3D não iremos ter q implemtar novamente o sistema de colisão já que o mesmo trabalha com coordenadas 3D.

xna_parte_II_04 xna_parte_II_05
Figura 5 – Não há colisão Figura 6 – Há colisão


BoundingSphere: bem parecido com a colisão por retângulo, porém utilizamos a forma esférica para testar se as imagens estão se sobrepondo. Assim como a anterior é de fácil implementação e bem ágil.

xna_parte_II_06 xna_parte_II_07

Figura 7 – Não há colisão

Figura 8 – Há colisão

Pixel: a colisão por pixel é realizada utilizando a área transparente de uma imagem. Nessas imagens a área transparente não é desenhada na tela, pois elas possuem o valor alpha 0 (zero). Assim basta testar se nas áreas da imagens estam desenhados pixels transparentes, sem colisão, ou pixels coloridos (colisão). A técnica de colisão por pixel é uma das melhores a ser utilizada, pois nos dá uma precisão maior na hora de trabalharmos com imágens que possuem muitas curvas e áreas onde a colisão por boundingbox ou boundingsphere não ficariam legal. É um método mais lento e mais difícil de ser implemtnado.

Na figura 9 vemos que mesmo as imagens estando sobrepostas não há colisão, isso porque somente a parte transparente estam se sobrepondo.

Já na figura 10 a imagens em si estam se sobrepondo.

Num próximo artigos veremos um pouco mais a fundo sobre física e como chegamos a certos cálculos, como por exemplo para detectar a colisão entre as esferas, que utiliza o Teorema de Pitágoras (A2 = B2 + C2), a colisão por Pixel e alguns calculos como velocidade, distancia, gravidade para que possamos implementar melhor nossos jogos.

xna_parte_II_08 xna_parte_II_09

Figura 9 – Colisão por Pixel – Não há colisão

xna_parte_II_10

Figura 10 – Colisão por Pixel – Há colisão

Entrada de dados

O XNA possui três tipos de classes para se trabalhar com entrada de dados que são:

– Keyboard – responsável por tratar eventos do teclado;

– Mouse – trata eventos do mouse;

– GamePad – trata eventos de controles como é o caso do Xbox 360.

Cada classe acima possui seu método estático para pegar o estado dispositivo. São eles:

– KeyboardState;

– MouseState;

– GamePadState.

Apesar de termos estes tipos de classes o XNA ainda deixa a desejar quanto a entrada de dados ou seja, se estivermos verificando se uma tecla foi pressionada não temos como saber se ele continua pressionada ou não, para que possamos fazer isto é necessário que armazenemos o “estado” anterior da tecla e testemos o mesmo posteriormente.

Coisas como essa a meu ver, devem estar prestes a serem disponibilizadas numa próxima versão do XNA, pelo menos é o que esperamos.

O pequeno trecho de código da listagem 2 nos mostra como implementar uma verificação de tecla.

Listagem 2 – Verificando a tecla pressionada.

KeyboardState keys = Keyboard.GetState();

  if ( keys.IsKeyDown( Keys.Up ) )
  {
     // Executa alguma ação com base na tecla precionada
  }

 

Agora que incrementamos nosso conhecimento sobre coordenadas, sprite, colisão e enrtrada de dados, vamos dar inicio ao desenvolvimento de nosso jogo. A parte de Orientação a Objetos não séra abordada neste artigo, assim sendo aconselho aos menos experientes a procurarem no Google assuntos relacionados a OO (Orientação a Objeto).

Criando as classes Objetos, Bola e Player

Crie um novo projeto do tipo WindowsGame e de o nome de “Pong”.

Feito isso nós iremos criar mais três novas classes que são: Objetos.cs, Bola.cs e Player.cs, irei descrever e explicar cada classe separadamente

Começaremos por modelar nossa classe Objetos.cs

Esta classe irá conter as propriedades e métodos comuns as nossas outras duas classes (Player e Bola).

Após adicionar a nova classe dê o nome a ela de “Objetos.cs” e acrescente o código conforme a listagem 3, não esqueça de adicionar em todas a classes criadas os namespaces: Microsoft.Xna.Framework e Microsoft.Xna.Framework.Graphics;

Listagem 3 – Classe base Objetos.cs.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Pong
{
    class Objetos
    {
        private Vector2 posicao;
        private Vector2 tamanho;
        private Vector2 velocidade;
        private Texture2D textura;

        public Vector2 Posicao
        {
            get { return posicao; }
            set { posicao = value; }
        }

        public Vector2 Tamanho
        {
            get { return tamanho; }
            set { tamanho = value; }
        }
 

        public Vector2 Velocidade
        {
            get { return velocidade; }
            set { velocidade = value; }
        }

        public Texture2D Textura
        {
            get { return textura; }
            set { textura = value; }
        }

        public Rectangle Retangulo
        {
            get { return new Rectangle( (int)posicao.X, (int)posicao.Y, (int)tamanho.X, (int)tamanho.Y ); }
        }
    }
}

Na classe Objetos.cs temos as seguinte variáveis e seus respectivos get/set que serão herdados por outras classes:

será responsável por receber a posição do objeto

 Vector2 posicao;

receberá o tamanho do objeto

 Vector2 tamanho;

irá controlar a vbelocidade de cada objeto

 Vector2 velocidade;

receberá uma textura referente a cada objeto

 Texture2D textura;

     será utilizado na verificação de colisão entre os objetos

     public Rectangle Retangulo

     {

       get { return new Rectangle( (int)posicao.X, (int)posicao.Y, (int)tamanho.X, (int)tamanho.Y ); }

     }

Está será nossa classe base.

Adicione uma nova classe e dê o nome de Player.cs. Feito isso herde a classe Objeto, nossa classe player ficará como na listagem 4.

Listagem 4 – Classe Player.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Pong
{
    class Player : Objetos
    {
    }
}

Reparem que nossa classe player é bem simples, não implementaremos nada nela apenas iremos herdar as propriedades da classe Objeto, para os menos experientes isso é uma das vantagens da OO (Orientação a Objeto), Herança.

Por último adicione uma nova classe e dê o nome de Bola.cs. Esta classe irá conter apenas dois métodos e uma propriedade. Reparem o código da listagem 5, irei explicar cada método separadamente.

Listagem 5 – Classe Bola.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace Pong
{
    class Bola : Objetos
    {
        private float aceleracao = 0.15f;

        public float Aceleracao
        {
            get { return aceleracao; }
        }

        /// <summary>
        /// Move a bola
        /// </summary>
        /// <param name="gw"></param>
        /// <param name="pontosPlayer1"></param>
        /// <param name="pontosPlayer2"></param>
        public void mover( GameWindow gw, ref int pontosPlayer1, ref int pontosPlayer2 )
        {
            this.Posicao += new Vector2( Velocidade.X, Velocidade.Y );

            if ( this.Posicao.X <= 0 )
            {
                pontosPlayer2++;
                this.Velocidade *= new Vector2( -1, 1 );
            }
            if ( this.Posicao.X >= gw.ClientBounds.Width - this.Textura.Width )
            {
                pontosPlayer1++;
                this.Velocidade *= new Vector2( -1, 1 );
            }
            if ( this.Posicao.Y < 0 || this.Posicao.Y >= ( gw.ClientBounds.Height - this.Textura.Height ) )
                this.Velocidade *= new Vector2( 1, -1 );

        }

        /// <summary>
        /// Checa a colisão
        /// </summary>
        /// <param name="jogador1"></param>
        /// <param name="jogador2"></param>
        public void checaColisao( Rectangle jogador1, Rectangle jogador2 )
        {
            bool colidiu = false;

            if ( this.Velocidade.X < 0 )
            {
                colidiu = this.Retangulo.Left   < jogador1.Right &&
                             this.Retangulo.Right  > jogador1.Left &&
                             this.Retangulo.Top    < jogador1.Bottom &&
                             this.Retangulo.Bottom > jogador1.Top;
            }
            else
            {
                colidiu = this.Retangulo.Left   < jogador2.Right &&
                             this.Retangulo.Right  > jogador2.Left &&
                             this.Retangulo.Top    < jogador2.Bottom &&
                             this.Retangulo.Bottom > jogador2.Top;
            }

            if ( colidiu )
            {
                //Muda a direção
                this.Velocidade *= new Vector2( -1, 1 );
            }
        }
    }
}

O primeiro método é responsável por mover a bola na tela. Nele passamos como parâmetros GameWindow que é na verdade a classe no qual iremos extrair o tamanho da tela utilizada no jogo, nela iremos pegar o comprimento e a largura da tela através do ClientBounds.Width e ClientBounds.Height. Os outros dois parâmetros dizem respeito à pontuação dos jogadores. Utilizamos parâmetros ref para passarmos por referência o valor dos pontos se cada jogador.

Reparem que utilizamos mais uma vez a classe Vector2 para incrementarmos a posição e a velocidade da bola na tela. Fizemos também a checagem do posicionamento da bola para que a mesma não saia da tela, caso sua posição no eixo X seja menor ou igual a 0 multiplicamos a velocidade da bola por -1 (menos um), isso fará com que a bola passe a decrementar o valor da posição invertendo sua direção. Note que multiplicamos dentro do vetor o X por -1 e o Y por 1, isso porque estamos verificando o eixo X, para o eixo Y faremos quase a mesma coisa, testamos o posicionamento da bola e se for o caso, invertemos sua posição.

Listagem 6 – Descrição do método mover

this.Posicao += new Vector2( Velocidade.X, Velocidade.Y );

   if ( this.Posicao.X <= 0 )
   {
  pontosPlayer2++;
       this.Velocidade *= new Vector2( -1, 1 );
   }

O código da listagem 7 nos mostra o método que será responsável por checar a colisão da bola com os bastões (Players).

Utilizamos a propriedade left, right, top e botton da classe Rectangle para verificarmos se a bola faz alguma interseção com o bastão, caso haja esse contato retornamos um valor true, ou seja, há colisão entre o player1 e a bola.

Para o player dois, realizamos os mesmo testes, porém antes de o fazermos, verificamos se a velocidade da bola é menor que zero isto porque, caso ela tenha velocidade negativa, sua trajetória será em direção ao player 1 (player da esquerda) sendo necessário realizar o teste em apenas um dos retângulos.

Detectada a colisão, multiplicamos nosso vetor velocidade por -1 no eixo X para alterarmos a direção da bola.

Listagem 7 – Teste de Colisão

if ( this.Velocidade.X < 0 )
  {
     colidiu = this.Retangulo.Left   < jogador1.Right &&
               this.Retangulo.Right  > jogador1.Left &&
               this.Retangulo.Top    < jogador1.Bottom &&
               this.Retangulo.Bottom > jogador1.Top;
  }
  else
  {
     colidiu = this.Retangulo.Left   < jogador2.Right &&
               this.Retangulo.Right  > jogador2.Left &&
               this.Retangulo.Top    < jogador2.Bottom &&
               this.Retangulo.Bottom > jogador2.Top;
  }

  if ( colidiu )
  {
     //Muda a direção
     this.Velocidade *= new Vector2( -1, 1 );
  }

Implementando Game.cs

Agora que já temos nossas classes criadas, vamos dar inicio a implementação da classe Game.cs, responsável pelo nosso jogo.

Primeiro vamos adicionar ao nosso projeto quatro imagens que serão a bola, o player, a imagem com os números e a rede. Nosso jogo irá ter a aparência da figura 11.

xna_parte_II_11

Figura 11 – Tela do XNA Pong

 

Adicionado as imagens, vá para a classe Game.cs e adicione os seguintes dados conforme listagem 8.

Listagem 8 – Variáveis do jogo

//Classe Bola
    Bola bola;

    //Classe Player
    Player player1;
    Player player2;

    //Textura a ser utilizada na classe Bola
    Texture2D imgBola;

    //Textura a ser utilizada na classe Player
    Texture2D imgPlayer;

    //Textura para gerar os pontos dos jogadores
    Texture2D imgNumeros;

     //Textura da "rede"
     Texture2D imgRede;

     //Array que irá receber cada "fatia" da imagem com seu respectivo número
     Rectangle[] pontos = null;

     //Pontos dos jogadores
     int pontosPlayer1;
     int pontosPlayer2;

Vamos agora inicializar nossas variáveis. Conforme explicado no artigo anterior, temos um lugar propício para realizar esta tarefa, no Initialize() da classe Game, conforme listagem 9. Observem que inicializamos os pontos de nossos jogadores com o valor 0. Mas ok e o que tem isso a ver? Bem, isso não quer dizer que os ogadores irão começar com pontuação zerada, mas sim por que iremos passar os pontos de cada jogador por referência (ref) e nesse caso é obrigatória a inicialização das variáveis, caso estivéssemos utilizando out ao invés de ref isso não seria necessário.

Listagem 9 – Inicializando as variáveis

protected override void Initialize()
{
   bola = new Bola();
   player1 = new Player();
   player2 = new Player();
   pontosPlayer1 = 0;
   pontosPlayer2 = 0;

   base.Initialize();
}

Após inicializarmos nossas variáveis, vamos carregar nossos objetos(imagens), posicionar nosso players, a bola e preencher o array com as imagens do número para a pontuação. A listagem 10 nos mostra o método LoadContent por completo.

Listagem 10 – Método LoadContent

protected override void LoadContent()
{
   // Create a new SpriteBatch, which can be used to draw textures.
   spriteBatch = new SpriteBatch( GraphicsDevice );

   // TODO: use this.Content to load your game content here

  //Carregando as imagens que serão utilizadas no jogo
  imgBola = Content.Load<Texture2D>( "bola" );
  imgPlayer = Content.Load<Texture2D>( "player" );
  imgNumeros = Content.Load<Texture2D>( "numbers" );
  imgRede = Content.Load<Texture2D>( "rede" );
    
    
    //Definições da Bola
    //Aqui definimos as propriedades da bola como Tamanho, Posição inicial na tela
    //velocidade e qual a textura a ser utilizada.
    //Note tambem que utilizamos a classe Windows.ClientBound para posicionarmos
    //nossa bola no centro da tela
    bola.Tamanho = new Vector2( imgBola.Width, imgBola.Height );
    bola.Posicao = new Vector2( ( Window.ClientBounds.Width / 2 ) - ( bola.Tamanho.X / 2 ), ( Window.ClientBounds.Height / 2 ) - ( bola.Tamanho.Y / 2 ) );
    bola.Velocidade = new Vector2( 5.0f, 4.0f );
    bola.Textura = imgBola;
    
    //Player 1
    //Assim como a bola, definimos também os dados de cada player
    player1.Tamanho = new Vector2( imgPlayer.Width, imgPlayer.Height );
    player1.Posicao = new Vector2( 10f, ( Window.ClientBounds.Height / 2 - imgPlayer.Height / 2 ) );
    player1.Velocidade = new Vector2( 0, 5 );
    player1.Textura = imgPlayer;
    
    //Player 2
    player2.Tamanho = new Vector2( imgPlayer.Width, imgPlayer.Height );
    player2.Posicao = new Vector2( ( Window.ClientBounds.Width - 10 - imgPlayer.Width ), ( Window.ClientBounds.Height / 2 - imgPlayer.Height / 2 ) );
    player2.Velocidade = new Vector2( 0, 5 );
    player2.Textura = imgPlayer;
    
    pontos = new Rectangle[10];
    
    //Carrega um array com a definição dos numeros
    //Aqui realizamos o preenchimento de nosso array com seus
    //respectivos valores para termos a posição certa de cada
    //fatia dentro da imagem original
    for ( int i = 0; i < 10; i++ )
    {
      pontos[i] = new Rectangle( i * 45, 0, 45, 75 );
    }
}
        

Como podem observar, não fizemos nada demais, apenas informamos onde estão nossas imagens, onde posicionaremos nossos players, a bola, a rede e os pontos.

Para que possamos ver nosso resultado até o presente momento, vamos programar nosso método Draw(), assim poderemos ver como ficou nossa tela. A explicação encontra-se dentro do próprio método, como mostrado na listagem 11.



Listagem 11
– Método Draw

protected override void Draw( GameTime gameTime )
{
  graphics.GraphicsDevice.Clear( Color.Black );

  // TODO: Add your drawing code here
  //Inicializa o SpriteBatch no modo AlphaBlend, ou seja
  //faz com que as imagens sejam desenhadas com o fundo transparente
  spriteBatch.Begin( SpriteBlendMode.AlphaBlend );

  //Posicionamos nossa "rede" no centro da tela
  spriteBatch.Draw( imgRede, new Vector2( ( Window.ClientBounds.Width * .5f ), 0 ), Color.White );

  //Desenhamos a pontuação do player 1 +- a 1/4 de tela
  spriteBatch.Draw( imgNumeros, new Vector2( ( Window.ClientBounds.Width * .22f ), 10 ), pontos[pontosPlayer1], Color.White );
  //Desenhamos a pontuação do player 2 +- a 3/4 de tela
  spriteBatch.Draw( imgNumeros, new Vector2( ( Window.ClientBounds.Width * .72f ), 10 ), pontos[pontosPlayer2], Color.White );
        
  //Desenhamos nossa "bola" apesar de ser quadrada..rsrs
  spriteBatch.Draw( bola.Textura, bola.Retangulo, Color.White );

  //Desenhamos os Player, um de cada lado
  spriteBatch.Draw( player1.Textura, player1.Retangulo, Color.White );
  spriteBatch.Draw( player2.Textura, player2.Retangulo, Color.White );

  spriteBatch.End();
      
  base.Draw( gameTime );
}

Calma pessoal, falta pouco agora..hehehehe. Vamos programar agora o método que será responsável por “mover” os players na tela. Dei o nome de movePlayers() (bem intuitivo não?). Como vimos anteriormente assim como nossos outros métodos, programar o método para mover os players não é nada complicado, apenas verificamos qual tecla foi pressionada a partir daí executamos a ação desejada.

Vamos ver em detalhes o que ele faz.

Primeiro criamos a instância chamada Keys que será responsável por receber a tecla que foi pressionada.

Depois fazemos a verificação, no caso, qual seria esta tecla.

O primeiro teste verifica se foi a tecla “seta para cima” ou “up”, a tecla pressionada, depois verificamos se a posição do Player1 não é menor que zero, isso porque caso a posição seja menor que zero no eixo Y, quer dizer que ele está fora da tela, nesse caso ele pára na posição atual e invertemos a velocidade para que agora ele possa descer, conforme a listagem 12.

Listagem 12 – Método movePlayers – Verificando Player1

public void movePlayers()
  {
      KeyboardState keys = Keyboard.GetState();

      //Player 1
      if ( keys.IsKeyDown( Keys.Up ) )
      {
          if ( player1.Posicao.Y < 0 )
             player1.Posicao = new Vector2( player1.Posicao.X, 0 );
           player1.Posicao -= player1.Velocidade;
      }

O segundo teste verifica se foi a tecla “seta para baixo” ou “down”, a tecla pressionada, depois verificamos se a posição do Player1 mais seu tamanho (height) não é maior que o tamanho da tela, isso para ele não ultrapassar a parte inferior da tela, depois invertemos a velocidade para que agora ele possa subir, conforme a listagem 13.

Listagem 13 – Método movePlayers – Verificando Player1

if ( keys.IsKeyDown( Keys.Down ) )
    {
      if ( player1.Posicao.Y > Window.ClientBounds.Height - player1.Textura.Height )
          player1.Posicao = new Vector2( player1.Posicao.X, ( Window.ClientBounds.Height - imgPlayer.Height ) );
         player1.Posicao += player1.Velocidade;
    }

Para o player 2 fazemos as mesmas verificações, conforme a listagem 14

Listagem 14 – Método movePlayers – Verificando Player2

//Player 2
    if ( keys.IsKeyDown( Keys.W ) )
    {
       if ( player2.Posicao.Y < 0 )
          player2.Posicao = new Vector2( player2.Posicao.X, 0 );
        player2.Posicao -= player2.Velocidade;
    }

    if ( keys.IsKeyDown( Keys.S ) )
    {
       if ( player2.Posicao.Y > Window.ClientBounds.Height - player2.Textura.Height  )
          player2.Posicao = new Vector2( player2.Posicao.X, ( Window.ClientBounds.Height - imgPlayer.Height ) );
        player2.Posicao += player2.Velocidade;
    }
}

Para finalizarmos, basta adicionar o código da listagem 15 ao método Update().

Listagem 15 – Método Update

// TODO: Add your update logic here
      bola.checaColisao( player1.Retangulo, player2.Retangulo );
      bola.mover( Window, ref pontosPlayer1, ref pontosPlayer2 );
      movePlayers();

      if ( pontosPlayer1 > 9 || pontosPlayer2 > 9 )
      {
          pontosPlayer1 = 0;
          pontosPlayer2 = 0;
      }

Muito simples, checamos se há colisão da bola com alguns dos players. O próprio método checaColisao faz com que assim que nossa bola colida com algum player automaticamente ela mude de direção.

Já o método move da classe Bola, move nossa bola ao longo da tela e caso haja colisão inverte sua direção na tela.

Chamamos o método movePlayers para darmos movimento aos nosso jogadores através do teclado.

E por fim, fazemos a verificação da pontuação de cada player, caso esta ultrapasse o valor 9, nós zeramos o contador de ambos os players.

Bem, ai está nosso primeiro jogo em XNA.

Conclusão

Como puderam ver o XNA Framework foi criado especialmente para facilitar a vida dos programadores de jogos, pois tanto o profissional quanto o entusiasta podem usufruir de uma ferramenta muito poderosa, e por encapsular funções do DirectX ele nos dá mobilidade para pensarmos mais no jogo do que na forma de codificarmos, sem contar que podemos muito bem ver nossas criações serem rodadas em um console como é o caso do XBox 360 (quem nunca teve vontade de ver seu jogo rodando em um console de vídeo game?).

O desenvolvimento de nosso Pong foi algo bem simples, mas que nos leva a entender um pouco da lógica por trás de um jogo.

Espero que tenham gostado do artigo.