Ponto V!

Home XNA XNA Desenvolvendo um Pong em XNA 4.0 (Iniciantes) - Parte 5/5
Kléber de Oliveira Andrade
Desenvolvendo um Pong em XNA 4.0 (Iniciantes) - Parte 5/5Imprimir
Escrito por Kleber Andrade

Enfim chegamos ao final desta série de tutoriais para os iniciantes no framework XNA 4.0 usando C# para desenvolver um Pong. Neste último tutorial sobre o Pong, vamos inicialmente melhorar um pouco o esquema de rebatida da bola para deixar nosso jogo mais divertido. Outra melhoria que iremos fazer é criar um sistema de telas no qual, inicialmente apareça uma tela de introdução com opções a serem escolhidas (1 Jogador, 2 Jogador, Sair) e colocar também um tela de game over. Então vamos lá?

Melhorando a rebatida da bola

A rebatida da bola agora vai funcionar da seguinte forma. Toda vez que a bola colidir com o bastão, vamos pegar a tangente inversa da diferênça das variáveis x e y. Não se assustem galera, existe a função Math.Atan2(double y, double x) que calcular a tangente inversa de y / x, exceto que os sinais de ambos os argumentos são usados para determinar o quadrante do resultado. Esta função retorna o resultado em radianos, estando entre -PI e PI (inclusive). Veja a figura abaixo do que acontece com o vetor direção quando a bola colide com o bastão.

Rebatida

Esse ainda não é o melhor método para deixar uma rebatida legal, mas o resultado é bem interessante. Vamos então pegar o trecho de código que checava a colisão da bola com os bastões e fazer a seguinte alteração conforme o código abaixo.

// Checa a colisão da bola com as raquetes dos jogadores
// Jogador 1 (Raquete da esquerda)
if (ball.GetBounding().Intersects(bat1.GetBounding()))
{
    // Centro da bola
    Vector2 cBall = new Vector2(ball.GetBounding().Center.X, ball.GetBounding().Center.Y);
    cBall.Normalize();
    // Centro do bastão a esquerda (jogador 1)
    Vector2 cBat = new Vector2(bat1.GetBounding().Center.X, bat1.GetBounding().Center.Y);
    cBat.Normalize();
    // Angulo de direção
    double angDir = Math.Atan2(cBall.Y - cBat.Y, cBall.X - cBat.X);
    // Inverte a direção X da bola
    ball.Direction = new Vector2((float)Math.Cos(angDir), (float)Math.Sin(angDir));
    // Toca o efeito sonoro de colisão com a bola
    soundToc2.Play();
}
// Jogador 2 (Raquete da direita)
if (ball.GetBounding().Intersects(bat2.GetBounding()))
{
    // Centro da bola
    Vector2 cBall = new Vector2(ball.GetBounding().Center.X, ball.GetBounding().Center.Y);
    cBall.Normalize();
    // Centro do bastão a esquerda (jogador 2)
    Vector2 cBat = new Vector2(bat2.GetBounding().Center.X, bat2.GetBounding().Center.Y);
    cBat.Normalize();
    // Angulo de direção
    double angDir = Math.Atan2(cBall.Y - cBat.Y, cBall.X - cBat.X);
    // Inverte a direção X da bola
    ball.Direction = new Vector2((float)Math.Cos(angDir), (float)-Math.Sin(angDir));
    // Toca o efeito sonoro de colisão com a bola
    soundToc2.Play();
}

Definindo os estados do jogo

Vamos agora criar uma nova classe chamada PongState.cs, que definirá um enumerador contendo os estados do jogo. Kleber para por ai, não entendi o que é enumerador e nem sei o que são estes estados do jogo.

  • Estados do jogo: são os estados existentes de um determinado jogo que indicam como ele se encontra, por exemplo: tela de abertura, tela de jogo, tela de game over, pause, etc.
  • Enumerador: a grosso modo, é uma maneira elegante de dar nome para os números. Então podemos dizer que o número zero se chama tela inicial, o número 1 se chama tela de jogo e o número 3 game over.

Então após criar este arquivo, digite o seguinte código:

namespace TutorialPong
{
    public enum PameState
    {
        IntroScreen,
        SinglePlayer,
        MultiPlayer,
        GameOver,
    }
}

A palavra chave enum que utilizamos serve justamente para enumerar as palavras começando do 0, ou seja: ( 0 ) IntroScreen; ( 1 ) SinglePlayer; ( 2 ) MultiPlayer e ( 3 ) GameOver. Perceba que agora ao invés de perguntarmos, você esta na tela 1, podemos perguntar você é SinglePlayer, ou melhor, você esta no estado SinglePlayer do jogo. Viu como fica muito legível usando enum.

Alternando estre as telas

Agora na classe Game1.cs e precisaremos fazer algumas alterações, mas primeiro vamos criar 4 novas variáveis. Uma variável para armazenar o estado do jogo, outra para a imagem da tela inicial, outra para a imagem da tela final e por fim uma constante com a quantidade máxima de pontos do jogo.

// Variável que armazena o estado do jogo
// Iniciamos a variável na tela inicial (IntroScreen)
PongState state = PongState.IntroScreen;
// Variável para armazenar a imagem da tela inicial
Texture2D intro = null;
// Variável para armazenar a imagem da tela de game over
Texture2D gameover = null;
// Pontução máxima do jogo
const int POINT_COUNT = 15;

Agora no método LoadContent(), vamos carregar as novas telas antes de dar o play na música, digite então o seguinte código lá.

// Carrega a textura da tela inicial
intro = Content.Load<Texture2D>(@"Textures\intro");
// Carrega a textura de game over
gameover = Content.Load<Texture2D>(@"Textures\gameover");

Agora vai ser um pouco difícil ma vamos tentar. Após verificarmos se pressionamos a tecla ESC dentro do método Update(), lá no começo, digite o seguinte código:

switch(state)
{
   case PongState.InitroScreen:
      break;
   case PongState.SinglePlayer:
   case PongState.MultiPlayer:
      break;
   case PongState.GameOver:
      break;
}

Isso é um interruptor que vai verificar qual o estado do jogo, então caso um estado do jogo seja igual aquelas palavras que definimos, ele executara todo o código que estiver abaixo da palavra até o primeiro break.

Então entre o case PongState.IntroScreen e break, vamos colocar o seguinte código.

case PongState.IntroScreen:
    // Entradas do jogador
    if (keyState.IsKeyDown(Keys.D1))//Se esta pressionado tlecla 1
    {
        state = PongState.SinglePlayer;
        RestartGame();
    }
    if (keyState.IsKeyDown(Keys.D2))//Se esta pressionado tlecla 2
    {
        state = PongState.MultiPlayer;
        RestartGame();
    }
    break;

Se estivemos na tela inicial só precisamos então verificar qual a tecla que o usuário pressionou, se foi o Digito 1 (D1) ele vai para o estado de SinglePlayer, se foi o Digito 2 (D2) ele vai para o estado de MultiPlayer. Mas quem é RestarGame? RestarGame é um método que você deve digitar dentro da classe Game1.cs abaixo do método Draw().

O código é o seguinte:

public void RestartGame()
{
   // Inicializando o objeto bola
   ball.Position = new Vector2(393, 313);
   // Inicializando o objeto bastão 1
   bat1.Position = new Vector2(10, 290);
   // Inicializando o objeto bastão 2
   bat2.Position = new Vector2(775, 290);
   // Inicializando o score dos jogadores
   score[0] = 0; // Jogador 1
   score[1] = 0; // Jogador 2
}

Caso seja, game over o estado atual do jogo precisou verificar se for pressionado Enter e mudar o estado para a tela inicial novamente.

case PongState.GameOver:
    // Entradas do usuario
    if (keyState.IsKeyDown(Keys.Enter)) // Se pressionar ENTER
    {
        state = PongState.IntroScreen;
    }
    break;

Bom galera todo aquele código que estava no método Update(), vamos colocar e fazer algumas mudanças. Veja como fica:

case PongState.SinglePlayer:
case PongState.MultiPlayer:
    // Se pressionar a tecla ESC encerra o jogo
    if (keyState.IsKeyDown(Keys.Escape))
        Exit();
    // Entradas do jogador 1 (bastão do lado esquerdo)
    if (keyState.IsKeyDown(Keys.W))
        bat1.Direction = new Vector2(0.0f, -1.0f);
    else if (keyState.IsKeyDown(Keys.S))
        bat1.Direction = new Vector2(0.0f, 1.0f);
    else
        bat1.Direction = Vector2.Zero;
    // Atualiza a posição do bastão 1
    bat1.Update(gameTime);

    if (state == PongState.SinglePlayer)
    {
        // Se o jogo for para single player o computador
        // é atualizado automáticamente
        MoveBastaoComputador();
    }
    else
    {
        // Entradas do jogador 2 (bastão do lado direito)
        if (keyState.IsKeyDown(Keys.Up))
            bat2.Direction = new Vector2(0.0f, -1.0f);
        else if (keyState.IsKeyDown(Keys.Down))
            bat2.Direction = new Vector2(0.0f, 1.0f);
        else
            bat2.Direction = Vector2.Zero;
    }
    // Atualiza a posição do bastão 2
    bat2.Update(gameTime);

    // Checa colisões da bola com as paredes do campo
    // Verifica se a bola colidiu em baixo
    if (ball.Position.Y + ball.Texture.Height > 570.0f)
    {
        // Inverte a direção em Y do vetor
        ball.Direction *= new Vector2(1.0f, -1.0f);
        // Toca o efeito sonoro de colisão com o campo
        soundToc1.Play();
    }
    // Verifica se a bola colidiu em baixo
    if (ball.Position.Y < 70.0f)
    {
        // Inverte a direção em Y do vetor
        ball.Direction *= new Vector2(1.0f, -1.0f);
        // Toca o efeito sonoro de colisão com o campo
        soundToc1.Play();
    }
    // Atualiza a posiação da bola
    ball.Update(gameTime);
    // Verifica se alguem marcou ponto
    // Se a bola passar pela direita da tela
    if (ball.Position.X + ball.Texture.Width > 800.0f)
    {
        // Toca o efeito sonoro para marcar pontos
        soundPoint.Play();
        score[0] += 1; // aumenta um ponto ao score do jogador 1
        // Coloca a bola no centro do campo novamente
        ball.Position = new Vector2(386.0f, 310.0f);
        // Muda a direção de disparo da bola em X
        ball.Direction *= new Vector2(-1.0f, 1.0f);
    }
    // Se a bola passar pela esquerda da tela
    else if (ball.Position.X < 0.0f)
    {
        // Toca o efeito sonoro para marcar pontos
        soundPoint.Play();
        score[1] += 1; // aumenta um ponto ao score do jogador 2
        // Coloca a bola no centro do campo novamente
        ball.Position = new Vector2(386.0f, 310.0f);
        // Muda a direção de disparo da bola
        ball.Direction *= new Vector2(-1.0f, 1.0f);
    }

    // Checa a colisão da bola com as raquetes dos jogadores
    // Jogador 1 (Raquete da esquerda)
    if (ball.GetBounding().Intersects(bat1.GetBounding()))
    {
        // Centro da bola
        Vector2 cBall = new Vector2(ball.GetBounding().Center.X, ball.GetBounding().Center.Y);
        cBall.Normalize();
        // Centro do bastão a esquerda (jogador 1)
        Vector2 cBat = new Vector2(bat1.GetBounding().Center.X, bat1.GetBounding().Center.Y);
        cBat.Normalize();
        // Angulo de direção
        double angDir = Math.Atan2(cBall.Y - cBat.Y, cBall.X - cBat.X);
        // Inverte a direção X da bola
        ball.Direction = new Vector2((float)Math.Cos(angDir), (float)Math.Sin(angDir));
        // Toca o efeito sonoro de colisão com a bola
        soundToc2.Play();
    }
    // Jogador 2 (Raquete da direita)
    if (ball.GetBounding().Intersects(bat2.GetBounding()))
    {
        // Centro da bola
        Vector2 cBall = new Vector2(ball.GetBounding().Center.X, ball.GetBounding().Center.Y);
        cBall.Normalize();
        // Centro do bastão a esquerda (jogador 2)
        Vector2 cBat = new Vector2(bat2.GetBounding().Center.X, bat2.GetBounding().Center.Y);
        cBat.Normalize();
        // Angulo de direção
        double angDir = Math.Atan2(cBall.Y - cBat.Y, cBall.X - cBat.X);
        // Inverte a direção X da bola
        ball.Direction = new Vector2((float)Math.Cos(angDir), (float)-Math.Sin(angDir));
        // Toca o efeito sonoro de colisão com a bola
        soundToc2.Play();
    }
    // Verifica se algum jogador chegou ao limite de pontos da partida
    // Se o jogador 1 ganhou
    if (score[0] >= POINT_COUNT)
    {
        state = PongState.GameOver;
    }
    // Se o jogador 2 ganhou
    if (score[1] >= POINT_COUNT)
    {
        state = PongState.GameOver;
    }
    break;

Movimentos do computador (Modo Single Player)

Agora precisamos definir o método MoveBastaoComputador() que colocamos no estado SinglePlayer. Vamos fazer algo simples conforme o método abaixo, mas se quiser modificar fique a vontade, pois você vai precisar melhorar os desafios para ele ficar mais legal.

public void MoveBastaoComputador()
{
    // Bola indo para direita
    if (ball.Direction.X > 0.0f)
    {
        if (bat2.GetBounding().Center.Y < ball.GetBounding().Center.Y)
            bat2.Direction = new Vector2(0.0f, 1.0f);
        else if (bat2.GetBounding().Center.Y > ball.GetBounding().Center.Y)
            bat2.Direction = new Vector2(0.0f, -1.0f);
        else
            bat2.Direction = new Vector2(0.0f, 0.0f);
    }
    else
    {
        bat2.Direction = new Vector2(0.0f, 0.0f);
    }
}

No código acima, simplesmente verificamos se o centro do bastão está acima ou abaixo do centro da bola, e assim fazemos o bastão seguir naquela direção.

Desenhando as telas

Para finalizar, agora só falta desenhar as telas, e para desenhá-las vamos precisar fazer um switch igual fizemos no método Update, vamos ver como fica o método Draw. Veja como ficaria o método Draw.

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);
    spriteBatch.Begin();
    switch (state)
    {
        case PongState.IntroScreen:
            // Desenha a tela de apresentação (tela inicial do jogo)
            spriteBatch.Draw(intro, Vector2.Zero, Color.White);
            break;

        case PongState.SinglePlayer:
        case PongState.MultiPlayer:
            // Desenha a imagem de fundo
            spriteBatch.Draw(background, Vector2.Zero, Color.White);
            // Desenha a bola
            ball.Draw(spriteBatch);
            // Desenha o bastão do jogador 1
            bat1.Draw(spriteBatch);
            // Desenha o bastão do jogador 2
            bat2.Draw(spriteBatch);
            // Desenhando o score do jogador 1 no centro do quadrado
            Vector2 textSize = fontScore.MeasureString(score[0].ToString("000"));
            spriteBatch.DrawString(fontScore,
                score[0].ToString("000"),
                new Vector2(300, 35) - textSize / 2,
                Color.White);
            // Desenhando o score do jogador 2 no centro do quadrado
            textSize = fontScore.MeasureString(score[1].ToString("000"));
            spriteBatch.DrawString(fontScore,
                score[1].ToString("000"),
                new Vector2(500, 35) - textSize / 2,
                Color.White);
            break;
        case PongState.GameOver:
            // Desenha a tela de game over
            spriteBatch.Draw(gameover, Vector2.Zero, Color.White);
            break;
    }
    spriteBatch.End();
    base.Draw(gameTime);
}

Como podemos ver, desenhamos o que vamos precisamos em cada estado do jogo, lembrando que esta é uma forma de fazer isso de forma simples, mas existem outras formas melhores porém um pouco mais complexas para iniciar um aprendizado.

Resultado do tutorial

Caso não tenhamos esquecido de nada, podemos rodar nossa aplicação e ver o resultado conforme imagens abaixo:

image

image

image

Download

Para fazer download do código criado neste tutorial, clique no ícone de download abaixo.

image

Conclusão

Seguindo todos os passos deste tutorial, vimos que não é tão difícil desenvolver um jogo simples, é claro escolhemos um jogo fácil para desenvolver. Mas, a idéia deste trabalho, é mostrar para vocês que qualquer pessoa pode desenvolver um jogo, desde que, entenda seu funcionamento. Espero que tenham gostado do tutorial, do joguinho que foi feito e tenham também aprendido muito com ele. Gostaria de agradecer a todos que seguiram os tutoriais, e pedir para que dêem seus comentários para que não só eu tenha esse feedback mas para todos que querem seguir o tutorial mas não sabem se valem ou não a pena. Você pode também abrir as imagens com programas que editem texturas (.png) e fazer imagens personalizadas para seu jogo.


Comentários (40)
  • odiney  - O MODO SINGLE PLAYER NÃO FUNCIONA
    avatar

    ola Kleber Andrade o modo single player nao funciona aqui.
    sempre que entro no jogo ele parece ir direto para multiplayer.
    e esses seus tutorias são muito bons
    abrços

  • Kleber Andrade
    avatar

    Olá Odiney, obrigado.

    Acabei de executar o código aqui e o modo Single Player funciona corretamente.

    Modo Single Player é caracterizado pelo segundo bastão (direita) se movimentar sozinho.

    Modo Multi Player é caracterizado pelo primeiro bastão sendo movimentado pelas teclas W e S e o segundo pelas setas CIMA e BAIXO.

    []s

  • Ricardo
    avatar

    e como eu faço para copilar para xbox e testar no proprio

  • Vinícius Godoy de Mendonça
    avatar

    Siga essas dicas da MS:
    http://msdn.microsoft.com/en-us/library/bb975643.aspx

  • Vítor Batista
    avatar

    Olá Kleber, gostei da finalização no tutorial!
    Só vi hoje mesmo. rsrs

    Durante o desenvolvimento aqui no meu micro, pensei em formas de criar "poderes" para os bastões. Como aumentar a área de impacto ou duplicar a bola.

    Me bateu essa dúvida. Como fazer para replicar uma classe dinamicamente?

    Abraços.

  • Kleber Andrade
    avatar

    Olá Vitor.

    Legal, uma vez eu pensei em um monte de possibilidades também, só não implementei por falta de tempo. Mas é interessante realmente aumentar ou diminuir os bastões, colocar mais de uma bola na tela, bola de fogo que diminui o bastão, bola de gelo que congela o bastão por alguns segundos, bola com curva, etc.

    Bom, sobre sua pergunta "Como fazer para duplicar uma classe dinamicamente?". Aqui você pode pensar em simplemente criar um novo objeto em tempo de execução dado algum acontecimento (se tal coisa aconteceu?) no jogo, você cria e adiciona em uma lista, só não pode esquecer de percorrer a lista para atualizar e desenhar todos os objetos, ou você pode simplemente modificar/agregar um parametro no objeto ja existente.

    Tudo isso que falei, dependerá muito do seu conhecimento em programação Orientada a Objetos de agora em diante.

    Abraços e bons estudos. Logo, faremos outra série de tutoriais de um novo jogo.

  • Vítor Batista
    avatar

    Vlw Kleber.

    Vou continuar olhando exemplos e ver como posso fazer essas mudanças.
    Cheguei a tentar de uma forma (ball02 = new ball(this, new Vector2(ball.Position.X + 50, ball.Position.Y))) mas deve estar faltando outras coisas para isso funcionar...

    Abraços,

  • Kleber Andrade
    avatar

    Certo Vitor, o problema é que você precisa chamar o método Update desta ball02 e o método Draw também, porém tudo que você fez, você fez pensando em uma única bola, então teria que colocar todas as bolas em uma lista (IList balls = new List();) e adicionar bolas dentro da lista. Percorrer a lista chamando o método Update e o método Draw, e não se esquecer de percorrer a lista também para verificar se houve colisões com os jogadores, com as paredes do campo ou se alguem marcou ponto

    Abraços,

  • Vítor Batista  - re:
    avatar
    Vítor Batista Escreveu:


    Como fazer para replicar uma classe dinamicamente?

    Abraços.

    Correção: Como fazer para duplicar o Objeto da Classe.

  • Carlos  - Algum erro
    avatar

    Quando a bola bate no bastão 1, dá certo, mas no bastão 2, a bola sempre vai pra cima ou pra baixo, nunca em reta?
    não identifiqueio erro ...

  • Carlos  - Algun Erro
    avatar

    Quando a bola bate no bastão 2, ela sempre sobe ou desce, nunca vai reto ..
    não encontrei o erro ...

  • Kleber Andrade
    avatar

    Olá Carlos, realmente tem este erro, bem observado.

    O problema esta la no código que inverte a posição da bola ao colidir com o segundo bastão.

    Esta assim: (Tem um menos no seno, isso faz algumas modificações e impede que a bola volte reto ao bater no centro do bastão)

    // Inverte a direção X da bola
    ball.Direction = new Vector2((float)Math.Cos(angDir), (float)-Math.Sin(angDir));

    Modificque assim: (Deixe positivo o valor, igual ao colidir com o bastão 1)
    // Inverte a direção X da bola
    ball.Direction = new Vector2((float)Math.Cos(angDir), (float)Math.Sin(angDir));

    Abraços,

  • Carlos  - Continuou cm um erro
    avatar

    obrigado por ter respondido
    mas mesmo alterando o sinal ainda não deu certo,
    mehorou um pouco, porém não vai reto ainda

    os tutoriais estão ótimos
    parabéns

  • Kleber Andrade
    avatar

    Por nada amigo, mas é bem estranho não estar indo reto, aqui foi... pois ele só vai reto se bater realmente no centro do bastão (o que é muito díficil).

    Abraços.

  • Diego Alves  - Tutorial
    avatar

    Ola Kleber parabens para o seu tutorial
    mais ta dando um erro no ISKEYDAWN
    e eu não consigo tirar esse erro vc poderia me ajudar

  • Kleber Andrade
    avatar

    Diego, obrigado.

    Você poderia postar o erro completo aqui para eu dar uma olhada?

    Sem isso é meio impossível, pois o código esta funcionando corretamente, então pode ser um pequeno detalhe que passou despecebido.

    Abraços.

  • Thwyster  - Dúvida
    avatar

    Bom Dia Kleber....

    Estou com um "probleminha"...ao implantar o método de colisão que foi demonstrado, as bolinhas estão travando.

    Ela ate é rebatida algumas vezes, mas na maioria delas elas travam na raquete....
    Chega a entrar para dentro da raquete, sabe me dizer o porque...

    coloquei também um tipo de dificuldade...
    a cada rebatida a velocidade é aumentada em 25.0f para que após algumas rebatidas o jogo fiquei mais interessante...porem esse problema da bola travar...realmente não sei como resolver.

    Abraços

  • Kleber Andrade
    avatar

    Olá amigo, esse pequenos problemas eu sei resolver sim. Eles são justamente os pequenos detalhes para ajustar o jogo que ficou como desafio para quem acompanhou os tutoriais. Mas é o seguinte:

    Como os valores são pontos flutuantes lembre-se que tem cadas decimais com 7 digitos de precisão, isso pode fazer que um dado momento de atualização ela tenha esteja a 0.01 para bater no bastão, depois esteja 0.875 para dentro do bastão, ai na terceira atualização como ela colidiu ela teria que voltar mas dado o tempo de atualização ele ficou ainda a 0.00023 do bastão continuando em colisão. Uma forma de você resolver isso, é toda vez que a bola colidir com o bastão você faze ela voltar a cima do bastão, evitando que continue em colisão caso os tempos de atualização sejam diferentes.

    A idéia do level é interessante! :D

  • Thwyster  - Ajuda
    avatar

    Boa noite Kleber, me desculpe a demora, mas acabei tento outros afazeres que me impossibilitaram de continuar com o jogo, e só estou retomando agora, gostaria de saber (por favor não quero parecer "folgado" :0 ), se você possui algum material o site indicado para que eu possa ler e entender melhor esse problema dos pontos flutuantes.

    Fico grato desde já.

  • Peterson  - Parabéns
    avatar

    Olá boa tarde,

    Rapaz, acompanhei esse tutorial na versão 3.0 no seu wordpress e agora a conversão para o 4.0 com adiçaõ de single player ficou muito boa.

    Essas aulas dão trabalho de serem elaboradas.

    Há previsões para tutoriais de plataformas (sidescroller)?

    Parabéns abraços.

  • Kleber Andrade
    avatar

    avatar

    Ola Peterson, muito obrigado.

    Sim existe uma previsão para o próximo mês começar um tutorial de um jogo usando o mouse que provavalmente será feito em 6 ou 7 passos por causa da introdução a animações de sprites (conceito muito utilizado no jogo de plataforma com sidescroller) e logo na sequencia acredito que em junho teremos o inicio de um sidescroller que introduzira conceitos de tilemap, parallaxscroller e camera.

    É um tempo considerado até lá, pois realmente da muito trabalho prepar um código simples e funcional, além de ter que escrever passo-a-passo de forma que fique bem compreendido pelos leitores.

    Abraços e continue lendo nossos tutoriais.

  • Caique L. Silva
    avatar

    Eai, Kleber Andrade!

    Esses dias venho tentando desenvolver um Pong com meus próprios métodos e algumas linhas de código com ajuda de seus tutoriais. Para checar a colisão da bola com os jogadores, uso retângulos e o método Intersects(). O problema é que a bola fica tremendo quando colide com os jogadores, e isso atrapalha muito.

    Queria a sua ajuda para solucionar este problema. Aqui vai o código-fonte do projeto :

    Link

    Até!

  • Thwyster  - re:
    avatar
    Caique L. Silva Escreveu:
    Eai, Kleber Andrade!

    Esses dias venho tentando desenvolver um Pong com meus próprios métodos e algumas linhas de código com ajuda de seus tutoriais. Para checar a colisão da bola com os jogadores, uso retângulos e o método Intersects(). O problema é que a bola fica tremendo quando colide com os jogadores, e isso atrapalha muito.

    Queria a sua ajuda para solucionar este problema. Aqui vai o código-fonte do projeto :

    Link



    Até!

    Estou com o mesmo problema, realmente não sei como resolver, se puder dar uma mão..

  • Kleber Andrade
    avatar

    Olá pessoal, desculpe por não responder antes. Seguinte:

    Por que em vez de quando a bola fica tremendo na quando colide com o bastão. Porque ela pode em um determinado momento colidir e o tempo de resposta ser menor na próxima iteração fazendo ela não sair completamente do bastão, acontecendo então uma nova colisão fazendo ela entrar novamente no bastão.

    Para resolver isso, você pode fazer que quando a bola colida com o jogador o bastão pare naquela posição e a bola sai primeiro para fora do bastão (mova a bola para o passo anterior da atualização - antes da colisão) e faça ela inverter a posiação.

    Essa é uma idéia para resolver isso.

    []s

  • Thwyster  - Duvidas
    avatar

    Kleber, existe alguma forma de debugar o jogo, eu entendo a lógica do erro, e até consigo pensar as formas de como poder resolver, o problema mesmo é o costume com a ferramente, tentei debugar o jogo, mas não tem como, o unico método de conseguir resolver o erro é no método tentativa e erro ?

  • Kleber Andrade
    avatar

    Não amigo, você pode debugar colocando break points no código, e executar em modo debug... assim você pode verificar os valores dos atributos e etc.

  • Peterson  - Sintaxe
    avatar

    Salve Kleber.

    Então, tava de olho nesse problema e tentei mudar a posição bola.posicao.Y para um ponto fora da raquete após a colisão, mas realmente não tive muito sucesso.

    Qual seria exatamente o metodo que deveriamos alterar, lembro que quando fazia com boulding.box e só invertia a posição da bola ao contato com a raquete, mas com esses angulos tudo ficou mais complicado.

    Poderia dar uma luz... ^^'

    Desde já agradeço a atenção e o tutorial novamente

  • Kleber Andrade  - re: Sintaxe
    avatar

    Peterson, vamos para um exemplo simples.

    Alterar a posição da bola antes da colisão (algoritmo)

    Vector2 oldPosBall = ball.Position;
    .
    .
    .

    Se acontece a colisão da bola com o bastão, você inverte a direção da bola e atribui a ela a posição anterior.

    ball.Position = oldPosBall;

  • Ronny  - Duvida
    avatar

    Boa Noite Kleber, Deixa eu ver se eu entendi,

    Criei a variavel Vector2D na classe ball/bola,
    após isso adiciono ela no metodo de colisão ?

    Seria nessa parte aqui Kleber...

    Código:

    //inverte a direção X da bola
    bola.Direcao *= new Vector2((float)Math.Cos(anguloDirecao), (float)Math.Sin(anguloDirecao));

    Abraços

  • Kleber Andrade
    avatar

    Não é bem isso. A variavel não é dentro da classe Ball, é uma variavel auxiliar só para armazenar a ultima posição.

    Exemplo no código:


    // Cria uma variavel auxiliar para armazenar a posição antiga da bola
    Vector2 oldPosBall = ball.Position;

    // Atualiza a posiação da bola
    ball.Update(gameTime);
    .
    .
    .

    // Checa a colisão da bola com as raquetes dos jogadores
    // Jogador 1 (Raquete da esquerda)
    if (ball.GetBounding().Intersects(bat1.GetBounding()))
    {
    // Centro da bola
    Vector2 cBall = new Vector2(ball.GetBounding().Center.X, ball.GetBounding().Center.Y);
    cBall.Normalize();
    // Centro do bastão a esquerda (jogador 1)
    Vector2 cBat = new Vector2(bat1.GetBounding().Center.X, bat1.GetBounding().Center.Y);
    cBat.Normalize();
    // Angulo de direção
    double angDir = Math.Atan2(cBall.Y - cBat.Y, cBall.X - cBat.X);
    // Inverte a direção X da bola
    ball.Direction = new Vector2((float)Math.Cos(angDir), (float)Math.Sin(angDir));
    // atirbui a posição antiga da bola
    ball.Position = oldPosBall;

    // Toca o efeito sonoro de colisão com a bola
    soundToc2.Play();
    }

    Faz a mesma coisa para o jogador 2.

    []s

  • Ronny  - Erro persiste
    avatar

    Vamos lá, estou deixando passar alguma coisa :silly:

    Código:

    //CHECA A COLISÃO DA BOLA COM AS RAQUETES
    //PLAYER 1 (RAQUETE DA ESQUERDA)
    if (bola.pegaTamanho().Intersects(raquete1.pegarTamanho()))
    {
    //Centro da Bola
    Vector2 centroBola = new Vector2(bola.pegaTamanho().Center.X, bola.pegaTamanho().Center.Y);
    centroBola.Normalize();

    //Centro do bastão da esquerda (Player 1)
    Vector2 centroRaquete1 = new Vector2(raquete1.pegarTamanho().Center.X, raquete1.pegarTamanho().Center.Y);
    centroRaquete1.Normalize();

    //Angulo de direção
    double anguloDirecao = Math.Atan2(centroBola.Y - centroRaquete1.Y, centroBola.X - centroRaquete1.X);

    //inverte a direção X da bola
    bola.Direcao *= new Vector2((float)Math.Cos(anguloDirecao), (float)Math.Sin(anguloDirecao));

    //Atribui posição antiga da bola
    bola.Direcao = AntigaPosBola;

    //Aumenta Velocidade da Bola
    bola.Velocidade += 25.0f;
    bola.Posicao += new Vector2(1.0f, 0.0f);

    //Toca som da bola quando bate na raquete
    somColRaquete1.Play();
    }

    Assim estaria correto ? não..?

    Adicioneio Vector2D AntigaPosBola na Classe Game1;

  • Kleber Andrade
    avatar

    Certo, mas onde vc estar armazendo a posição da bola? tem que ser antes do ball.Update(gameTime);

    Você esta atribuindo posição a direção, isso esta errado também!

  • Ronny
    avatar

    Eu tinha jogado o Variável junto lá em cima...

    Código:

    ///
    /// TELAS DO JOGO
    ///

    //VARIAVEL QUE ARMAZENA ESTADO DO JOGO
    //Iniciamos a variavel na tela inicial(MENU)
    EstatusdoJogo estatus = EstatusdoJogo.Menu;
    //Variavel para armazenar a imagem da tela inicial
    Texture2D Menu = null;
    //Variavel para armazenar a imagem da tela Final(GAMEOVER)
    Texture2D gameOver = null;
    //Pontuação maxima do jogo
    const int Contagem_Pontos = 5;

    [color=red]//Pega Posição da bola (Utilizado no metodo Intersects)
    public Vector2 AntigaPosBola { get; set; }[/color]
  • Kleber Andrade
    avatar

    Tudo bem, mas ela precisa armazenar sempre a posição da bola antes da colisão, para que quando colida você possa adicionar a posição anterior não deixando que ela continue colidindo com o bastão.

  • Ronny
    avatar

    Bem agora aconteceu o seguinte, após a bola colidir com o bastão o jogo acaba, é como se a bola rebatesse tao rapido que não fosse possivel ver, ja tirei a função que eu havia feito, para aumentar a velocidade da bola após cada reabatida, e mesmo assim o Bug persiste...

    Meu metodo da colisão com a raquete esta assim:

    Código:

    //CHECA A COLISÃO DA BOLA COM AS RAQUETES
    //PLAYER 1 (RAQUETE DA ESQUERDA)
    if (bola.pegaTamanho().Intersects(raquete1.pegarTamanho()))
    {
    //Centro da Bola
    Vector2 centroBola = new Vector2(bola.pegaTamanho().Center.X, bola.pegaTamanho().Center.Y);
    centroBola.Normalize();

    //Centro do bastão da esquerda (Player 1)
    Vector2 centroRaquete1 = new Vector2(raquete1.pegarTamanho().Center.X, raquete1.pegarTamanho().Center.Y);
    centroRaquete1.Normalize();

    //Angulo de direção
    double anguloDirecao = Math.Atan2(centroBola.Y - centroRaquete1.Y, centroBola.X - centroRaquete1.X);

    //inverte a direção X da bola
    bola.Direcao *= new Vector2((float)Math.Cos(anguloDirecao), (float)Math.Sin(anguloDirecao));

    //Atribui posição antiga da bola
    bola.Direcao = AntigaPosBola;

    //Aumenta Velocidade da Bola
    bola.Velocidade += 0.25f;
    bola.Posicao += new Vector2(1.0f, 0.0f);

    //Toca som da bola quando bate na raquete
    somColRaquete1.Play();
    }

    Estou pegando a posição da bola desta forma no gameupdate
    (antes do bola update como dito antes...)

    Código:

    //Verifica se a Bola colidiu embaixo
    if (bola.Posicao.Y + bola.Textura.Height > 715.0f)
    {
    //Inverte a direção do Y do vetor
    bola.Direcao *= new Vector2(1.0f, -1.0f);
    somColCampo1.Play();
    }

    //Verifica se a bola Colidiu embaixo
    if (bola.Posicao.Y < 53.0f)
    {
    //Inverte a direção do Y do Vetor
    bola.Direcao *= new Vector2(1.0f, -1.0f);
    somColCampo1.Play();
    }

    //Pegar Posição antiga da bola
    AntigaPosBola = bola.Posicao;

    //Atualiza a posicao da bola
    bola.Update(gameTime);

    Alguma idéia ?

  • Kleber Andrade
    avatar

    Então Ronny, tu esta adicionando a posição da bola a direçaõ

    //Atribui posição antiga da bola
    bola.Direcao = AntigaPosBola;

    Isto esta errado, uma coisa é direção que varia entre -1 e 1 e outra coisa é posição que varia de 0 ao tamanho da tela.

    []s

  • Ronny  - Dúvida
    avatar

    Pois eh Kleber, ficamos ontem até algumas horas da madrugada, mas realmente acabamos por não conseguir resolver este bug...

    Notei que na raquete da esquerda a bolinha esta entrando para dentro da raquete, tenho certeza que é esse o problema, e como você disse provavelmente quando o jogo atualiza a bolinha e a raquete ficam no mesmo espaço "físico/logico" gerando o Bug...
    Mas não conseguimos achar o erro...

  • Peterson
    avatar

    Enfim, meti o dedo ai para ajudar o ronny, e to tentando arrumar isso com ele via MSN e Team Viewer, e realmente mesmo arrumando isso o erro é o mesmo....

  • Kleber Andrade
    avatar

    Galera, vou resolver o problema então se tudo der certo no domingo a noite que é mais tranquilo, e coloco a solução para vocês, pode ser?

    []s

  • Ronny
    avatar

    Bom dia Kleber, desde já agradeço o empenho em nos ajudar, e realmente sei o quao dificil é organizar "tempo" para conciliar tudo.

    Ontem junto ao Peterson, verificamos mais um "bug" a raquete da esquerda sempre rebate a bola em linha reta, e a raquete da direita rebate a bola para baixo...sempre esse dessa mesma forma

    não conseguimos entender o porque disso também.
    se quiser podemos trocar contato, e marcar uma conferência via TeamViewer sem fins lucrativos...

    É uma ideia ;D
    Desde já
    Abraços

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