|
Creio que todos já estamos cansados de ouvir que o XNA é simples de aprender, que é poderoso, e vários “etc”. Quem já programou pelo menos um joguinho simples, sabe o quanto isso é verdade. Mas que tal demonstrarmos o quão fácil criar um jogo pode ser com um desafio: fazer um jogo em apenas 40 linhas!
O jogo escolhido – que precisa ser simples – foi um clone do clássico “breakout”, também conhecido como “arkanoid”.
Para este jogo, vamos aprender a criar uma classe derivada do tipo “gameComponent” do XNA, o que permite “cadastrar” os objetos do jogo para que o XNA invoque o método “Update” destes objetos automaticamente.
Para começar, crie um novo projeto do tipo “Windows Game (3.0)” e inclua uma nova classe, batizando-a de “clsSprite” e indicando que ela é derivada da classe “GameComponent” do XNA,
Dentro desta classe, vamos criar três propriedades (por razão de simplicidade, apenas como variáveis públicas dentro da classe) e um construtor que receba estas propriedades, além do objeto Game (que é obrigatório para classes derivadas de GameComponent.
O código fonte desses passos fica assim:
class clsSprite: GameComponent
{
public Texture2D textura;
public Vector2 posicao, velocidade;
public Boolean visivel = true;
public clsSprite(Game _game, Texture2D _textura, Vector2 _posicao, Vector2 _velocidade) : base(_game)
{
posicao = _posicao; textura = _textura; velocidade = _velocidade;
}
}
Nesta classe teremos 3 métodos:
- Update: chamado automaticamente pelo XNA, e que irá mover a sprite com base em sua velocidade, dentro dos limites da tela.
- Draw: que irá ser chamado do Draw da classe Game1, para desenhar a sprite;
- Colide: que irá verificar se a sprite colide com outra, recebida como parâmetro;
Confira o código destes métodos a seguir – nada muito sofisticado aqui, exceto pelo método Colide, que basicamente cria dois retângulos com a posição e tamanho das sprites, e depois utiliza o método Intersects do retângulo para verificar se eles se sobrepõe.
public override void Update(GameTime gameTime)
{
// Faz a sprite (32 pixels) quicar nas bordas da janela
if (posicao.Y > 600 - textura.Width || posicao.Y <0)
velocidade.Y *= -1;
if (posicao.X > 800 - textura.Width || posicao.X < 0)
velocidade.X *= -1;
posicao += velocidade;
}
public void Draw(SpriteBatch spriteBatch, Color cor)
{
spriteBatch.Draw(textura, posicao, cor);
}
public bool Colide(clsSprite outraSprite)
{
Rectangle esteRect = new Rectangle((int)posicao.X, (int)posicao.Y, textura.Width, textura.Height);
Rectangle outroRect = new Rectangle((int)outraSprite.posicao.X, (int)outraSprite.posicao.Y, outraSprite.textura.Width, outraSprite.textura.Height);
return esteRect.Intersects(outroRect);
}
Para criar as sprites, calcular as colisões e desenhar o jogo na tela, basta 5 passos, incluindo o código na classe Game1:
1. Definir as variáveis do tipo sprite
clsSprite bastao, bola; clsSprite[,] tijolos = new clsSprite[12,5];
Nada misterioso aqui: uma sprite para a bola, outra para o bastão, e um array de 5 linhas e 12 colunas para os tijolos.
2. No método Initialize(), criar os objetos do tipo sprite
bastao= new clsSprite(this, Content.Load < Texture2D>("bat"), new Vector2(20f, 560f), Vector2.Zero);
bola = new clsSprite(this, Content.Load < Texture2D>("ball"), new Vector2(400f, 300f), new Vector2(3, -3));
this.Components.Add(bola);
this.Components.Add(bastao);
for (int x = 0; x < 12; x++)
for (int y = 0; y < 5; y++)
tijolos[x,y] = new clsSprite(this, Content.Load < Texture2D>("WhiteBrick"),
new Vector2(x * 67f, 50f + 34f * y), Vector2.Zero);
A classe sprite espera: o jogo corrente (é o parâmetro this), a imagem/textura a ser usada, a posição inicial e a velocidade inicial do objeto a ser criado. No caso da bola e do bastão, para que o XNA controle a sua atualização, precisamos incluir os objetos como componentes do jogo.
3. No método Update(), incluir código para movimentar o bastão e testar as colisões
bastao.velocidade = new Vector2(GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X * -10, 0f);
if (bola.Colide(bastao))
bola.velocidade.Y *= -1;
for (int x = 0; x < 12; x++)
for (int y = 0; y < 5; y++)
if (tijolos[x,y].visivel && bola.Colide(tijolos[x,y]))
{
tijolos[x,y].visivel = false;
bola.velocidade.Y *= -1f;
}
A primeira linha deste código move o bastão com o gamepad do Xbox – basta ligar no PC que funciona. Para usar o teclado, utilize o objeto Keyboard, por exemplo: Keyboard.GetState().IsKeyDown(Keys.Left).
A seguir, o código inverte a velocidade Y (vertical) da bola se ela colide com o bastão, e faz o mesmo, além de tornar o tijolo invisível, caso a bola colida com algum tijolo.
4. Por fim, basta incluir o código de desenho no método Draw()!
spriteBatch.Begin();
bastao.Draw(spriteBatch, Color.White);
bola.Draw(spriteBatch, Color.White);
for (int x = 0; x < 12; x++)
for (int y = 0; y < 5; y++)
if (tijolos[x,y].visivel)
tijolos[x, y].Draw(spriteBatch, new Color(
Convert.ToByte((x * 15 + y *20) % 250),
Convert.ToByte((x * 20 + y *40 ) % 250),
Convert.ToByte((x * 25 + y * 50) % 250 )));
spriteBatch.End();
O método Draw() do objeto sprite espera o spriteBatch (objeto do XNA usado para desenhar gráficos 2D) que irá desenhá-lo e a cor que ele deve usar para blending, ou seja, qual cor será sobreposta ao objeto. No caso do bastão e da bola, queremos desenhá-los com cor branca, para manter suas cores originais. No caso dos tijolos, criamos uma cor a partir de 3 bytes, usando uma fórmula maluca qualquer para cada elemento RGB da cor. Modifique esta fórmula à vontade para ter diferentes efeitos, só não esqueça de, ao fim de cada cálculo, incluir “% 256”, ou seja, “o resto em relação a 256”, pois isso garante que o resultado da conta ficará sempre entre 0 e 256 , valor máximo para um byte.
5. Jogar!
Isso mesmo, seu jogo já está pronto! Pode jogar. É um jogo simples, com física de colisão muito básica, mas funciona bastante bem para 40 linhas! Baixe o código fonte completo clicando aqui.
E agora? Use a classe sprite para inventar seus jogos, ou altere este jogo para torná-lo mais interessante! E uma chamada geral aos leitores: Enviem suas dúvidas e sugestões sobre o que gostariam de ver aqui na coluna – sua palavra é lei! Happy Coding!
-
22/03/2010 09:37:46 | Alexandre Lobão

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

Entendi...
Eu pensava que quando era feito begin/base.draw/end no método Draw do Game1 o XNA não executava um begin/end para cada DrawableGameComponent.
É melhor então usar GameComponent ou DrawableGameComponent apenas como "managers".
-
06/05/2010 23:27:17 |201.68.152.xxx| Kleber de Oliveira Andrade

Não necessariamente, se você adicionar o spriteBatch da classe game ao serviços do jogo, você pode recuperar sua referência na classe tijolos e não precisaria de Begin() e End() pois a referência de desenho seria da classe Game1.cs
-
07/05/2010 09:39:48 | Alexandre Lobão

Beleza, Kleber, concordo!
Com certeza há diversas formas de resolver a questão; algumas mais sofisticadas, outras mais simples, outras simplesmente diferentes.
Como os artigos são para iniciantes, eu busco uma abordagem que simplifique a abordagem, o que não necessariamente passa por uma solução mais elegante ou mais otimizada.
Vale a dica para todos os outros: usem o código como uma base para começar a trabalhar, mas semore procurem formas criativas de modificá-lo, pois é nisso que reside a arte da programação!
-
11/08/2010 14:22:12 |189.107.114.xxx| Luis Filipe de Oliveira - Considerações sobre o jogo

Muito legal.. mas eu consegui apenas com o codigo fonte baixado aqui e com um recurso. Esse arquivo AssemblyInfo.cs nao tinha, fiz um txt vazio e renomei ele e funfo o jogo. Mas pelo tutorial eu não consegui. Porque tem alguns coisas que não fala no tutorial como, a bolinha tem q ter no projeto e a barrinha tbm né?
Mas vlws mesmo.. para tentar comecar já é algo..
só uma duvida: Porque a bolinha quica no fundo?(É porque ele bate em todos os lados.. temos que fazer no fundo cair?) Porque a barrinha some quando colocado do lado?
Mesmo assim.. Parabéns
-
25/12/2011 21:05:12 |2.80.1.xxx| Sérgio Matos - Erro

Olá gostei do tutorial mas não consigo compilar o código mesmo usando o código fonte dá erro aqui nesta linha de código
Invalid expression term '
-
25/12/2011 21:08:11 |2.80.1.xxx| Sérgio Matos - Erro

Não consigo compilar o código fonte dá erro na Linha 49
Invalid term expression '
-
25/12/2011 21:45:37 | Bruno Crivelari Sanches

bom, aqui continua compilando sem problemas. Agora erro na linha 49 de qual arquivo? Aqui o único que tem mais de 49 linhas (o Game1.cs) não possui nada na linha 49.
T+
-
26/12/2011 19:11:49 |2.80.50.xxx| sergio matos

essa instruçao na linha 49 arquivo game1.cs não deixa compilar
-
26/12/2011 19:12:50 |2.80.50.xxx| sergio matos

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











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