|
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.
![]()
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:
Download
Para fazer download do código criado neste tutorial, clique no ícone de download abaixo.
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.
-
07/12/2011 14:05:03 | Kleber Andrade

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
-
05/01/2012 10:57:55 |187.64.245.xxx| Ricardo

e como eu faço para copilar para xbox e testar no proprio
-
05/01/2012 11:22:50 | Vinícius Godoy de Mendonça

Siga essas dicas da MS:
http://msdn.microsoft.com/en-us/library/bb975643.aspx
-
09/01/2012 20:13:10 |187.24.149.xxx| Vítor Batista

Olá Kleber, gostei da finalização no tutorial!
Só vi hoje mesmo. rsrsDurante 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.
-
10/01/2012 09:56:36 | Kleber Andrade

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.
-
10/01/2012 19:31:52 |187.24.236.xxx| Vítor Batista

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,
-
10/01/2012 19:35:24 | Kleber Andrade

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 pontoAbraços,
-
10/01/2012 08:56:23 |189.93.147.xxx| Vítor Batista - re:
Vítor Batista Escreveu:
Como fazer para replicar uma classe dinamicamente?
Abraços.Correção: Como fazer para duplicar o Objeto da Classe.
-
10/01/2012 13:47:05 |187.79.49.xxx| Carlos - Algum erro

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 ...
-
10/01/2012 13:48:30 |187.79.49.xxx| Carlos - Algun Erro

Quando a bola bate no bastão 2, ela sempre sobe ou desce, nunca vai reto ..
não encontrei o erro ...
-
10/01/2012 15:42:56 | Kleber Andrade

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,
-
12/01/2012 16:02:53 |187.79.87.xxx| Carlos - Continuou cm um erro

obrigado por ter respondido
mas mesmo alterando o sinal ainda não deu certo,
mehorou um pouco, porém não vai reto aindaos tutoriais estão ótimos
parabéns
-
12/01/2012 18:03:30 | Kleber Andrade

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.
-
04/02/2012 23:27:21 |187.126.233.xxx| Diego Alves - Tutorial

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
-
04/02/2012 23:29:38 | Kleber Andrade

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.
-
01/04/2012 05:25:59 |177.16.32.xxx| Thwyster - Dúvida

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
-
02/04/2012 10:34:43 | Kleber Andrade

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!

-
26/04/2012 18:50:53 |189.58.131.xxx| Thwyster - Ajuda

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"
), 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á.
-
02/04/2012 12:50:18 |177.43.57.xxx| Peterson - Parabéns

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.
-
02/04/2012 12:56:10 | Kleber Andrade

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.
-
27/04/2012 19:19:48 |187.10.89.xxx| Caique L. Silva

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 :
Até!
-
16/05/2012 22:48:30 |177.96.31.xxx| Thwyster - re:
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..
-
16/05/2012 23:14:20 | Kleber Andrade

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
-
16/05/2012 23:32:42 |177.96.31.xxx| Thwyster - Duvidas

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 ?
-
16/05/2012 23:34:48 | Kleber Andrade

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.
-
16/05/2012 23:40:47 |177.40.101.xxx| Peterson - Sintaxe

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
-
16/05/2012 23:50:52 | Kleber Andrade - re: Sintaxe

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;
-
17/05/2012 00:10:58 |177.96.31.xxx| Ronny - Duvida

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
-
17/05/2012 00:25:54 | Kleber Andrade

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
-
17/05/2012 00:25:46 |177.96.31.xxx| Ronny - Erro persiste

Vamos lá, estou deixando passar alguma coisa
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;
-
17/05/2012 00:28:26 | Kleber Andrade

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!
-
17/05/2012 00:33:27 |177.96.31.xxx| Ronny

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]
-
17/05/2012 00:35:29 | Kleber Andrade

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.
-
17/05/2012 00:47:24 |177.96.31.xxx| Ronny

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 ?
-
17/05/2012 00:49:30 | Kleber Andrade

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
-
17/05/2012 18:48:22 |189.115.81.xxx| Ronny - Dúvida

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...
-
17/05/2012 01:05:44 |177.40.101.xxx| Peterson

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....
-
17/05/2012 22:19:07 | Kleber Andrade

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
-
18/05/2012 09:02:39 |177.19.236.xxx| Ronny

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












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