38
Tutorial de XNA - Parte I Criado em 22/10/2007 16:03 Modificado em 17/12/2007 10:19 Tópicos Xna Primeira parte do tutorial de XNA, mostrando como é a estrutura de um jogo no XNA :-) Para o melhor entendimento deste material é necessário que o leitor tenha conhecimentos prévios de C# e de orientação a objetos. Como bom “RPGista” que sou, sempre gostei de jogos e ainda tenho a vontade de criar os meus, para tanto já tinha até iniciado meus estudos em Allegro1[1] , o que não foi assim tão amigável. Eu não tive tempo de concluir o meu primeiro experimento em allegro por causa da semana de provas na universidade, sem falar do retorno do grupo de estudos de .NET e do trabalho, resolvi então retomar o estudo dos jogos unindo o útil ao agradável, o grupo de .NET e o Framework para desenvolvimento de jogos da Microsoft, o XNA. Comecei então a pesquisar a respeito, primeiro as definições formais, as novidades, porque usar e por fim, a parte mais prazerosa que é montar um jogo de exemplo. Então, sem mais delongas, vamos a introdução ao XNA, espero que este texto seja útil para outras pessoas. O que é XNA? Bom, como todo framework, o XNA é uma plataforma para desenvolvimento que visa facilitar em algum nível o desenvolvimento de aplicações, no caso os jogos. Por exemplo, ao desenvolver jogos, o programador precisa criar, dentre outras coisas, um loop principal, onde ficam as etapas básicas que todo jogo deve ter, como carregamento de recursos, atualização dos dados do “mundo” do jogo, desenho das cenas com as modificações e descarga de recursos, isso o XNA já traz para você. Sem falar que dependendo dos recursos usados o mesmo jogo produzido para PC pode rodar em outras plataformas (como o XBOX). Resumindo, como muitas iniciativas da Microsoft o XNA visa facilitar em muita coisa o desenvolvimento de jogos. Mas, deixemos de “bla-bla-bla” e vamos à prática. Você pode encontrar maiores informações no endereço: http://msdn2.microsoft.com/en-us/xna/default.aspx O software necessário para reproduzir este tutorial: - Visual C# Express - Visual Game Studio 1.0 Você pode baixar ambos do site da Microsoft. Começando o projeto. Para iniciar o estudo, vamos criar um projeto do tipo “Windows Game”, quando aparecer o código do “template” nós vamos apagar e recomeçar o nosso código, assim vamos entender melhor o que cada parte do código faz.

Tutorial XNA

Embed Size (px)

Citation preview

Page 1: Tutorial XNA

Tutorial de XNA - Parte I

Criado em 22/10/2007 16:03 Modificado em 17/12/2007 10:19 Tópicos Xna

Primeira parte do tutorial de XNA, mostrando como é a estrutura de um jogo no XNA :-)

Para o melhor entendimento deste material é necessário que o leitor tenha conhecimentos prévios de C# e de

orientação a objetos.

Como bom “RPGista” que sou, sempre gostei de jogos e ainda tenho a vontade de criar os meus, para tanto

já tinha até iniciado meus estudos em Allegro1[1] , o que não foi assim tão amigável. Eu não tive tempo de

concluir o meu primeiro experimento em allegro por causa da semana de provas na universidade, sem falar

do retorno do grupo de estudos de .NET e do trabalho, resolvi então retomar o estudo dos jogos unindo o útil

ao agradável, o grupo de .NET e o Framework para desenvolvimento de jogos da Microsoft, o XNA.

Comecei então a pesquisar a respeito, primeiro as definições formais, as novidades, porque usar e por fim, a

parte mais prazerosa que é montar um jogo de exemplo. Então, sem mais delongas, vamos a introdução ao

XNA, espero que este texto seja útil para outras pessoas.

O que é XNA?

Bom, como todo framework, o XNA é uma plataforma para desenvolvimento que visa facilitar em algum

nível o desenvolvimento de aplicações, no caso os jogos. Por exemplo, ao desenvolver jogos, o programador

precisa criar, dentre outras coisas, um loop principal, onde ficam as etapas básicas que todo jogo deve ter,

como carregamento de recursos, atualização dos dados do “mundo” do jogo, desenho das cenas com as

modificações e descarga de recursos, isso o XNA já traz para você. Sem falar que dependendo dos recursos

usados o mesmo jogo produzido para PC pode rodar em outras plataformas (como o XBOX). Resumindo,

como muitas iniciativas da Microsoft o XNA visa facilitar em muita coisa o desenvolvimento de jogos.

Mas, deixemos de “bla-bla-bla” e vamos à prática. Você pode encontrar maiores informações no endereço:

http://msdn2.microsoft.com/en-us/xna/default.aspx

O software necessário para reproduzir este tutorial:

- Visual C# Express

- Visual Game Studio 1.0

Você pode baixar ambos do site da Microsoft.

Começando o projeto.

Para iniciar o estudo, vamos criar um projeto do tipo “Windows Game”, quando aparecer o código do

“template” nós vamos apagar e recomeçar o nosso código, assim vamos entender melhor o que cada parte do

código faz.

Page 2: Tutorial XNA

Para tanto vá até “File -> New Project” e depois escolha “Windows Game”.

No “Solution Explorer” (onde aparecem os componentes do projeto) vão aparecer dois arquivos

“Program.cs” e “Game1.cs”, apague os dois, eles contem código pronto para uma estrutura básica do jogo,

nós vamos criar o nosso código.

Agora adicione um arquivo em branco onde nós vamos começar a criar o nosso projeto, para tanto, clique

com o botão direito dentro do “Solution Explorer” no nome do projeto e vá até a opção “Add” e depois

“New Item” e selecione “Class”, dê um nome ao arquivo e pronto.

A estrutura do jogo.

Como eu já havia citado antes, a maioria dos jogos possui alguns elementos comuns em suas estruturas de

controle, como um loop “geral”, um local para carregamento de recursos, um para atualização do “mundo”,

um para descarga e outro para desenho. No XNA a classe “Game” fornece essa estrutura.

Vamos ao código:

Primeiro vamos adicionar os namespaces necessários para a estrutura do jogo, no caso para poder usar a

classe Game do XNA. usingSystem; usingMicrosoft.Xna.Framework;

Agora vamos herdar da classe Game uma nova classe que vai conter a estrutura do nosso jogo:

//ocódigo da classe jogo, herdado da classe pai "Game" classjogo:Game { //construtor daclasse publicjogo() {

} }

Repare que eu apenas criei a classe, herdando os membros da classe Game e depois criei um construtor que

ainda está vazio. Agora, o código da rotina principal da aplicação:

classnovo_jogo { staticpublicvoidMain()//método principal daaplicação { jogoteste =newjogo(); teste.Run(); }

Page 3: Tutorial XNA

}

Com esse código já será criada uma janela para a nossa aplicação, mas, nada foi feito ainda. Vale ressaltar

que agora usaremos a estrutura herdada da classe game na nossa aplicação, ela nos fornece um loop

principal para o jogo, cuida da inicialização dos dispositivos e provê alguns métodos que nós iremos

sobrescrever para criar a lógica do nosso jogo.

Agora vamos aos métodos da classe game que serão utilizados na nossa estrutura:

Draw Chamado quando o jogo determina seu tempo específico de renderização.

Iremos sobrescrever esse método para criar nossas rotinas de desenho das

imagens.

Initialize Chamado depois que “Game” e GraphicsDevice são criados, mas, antes de

LoadGraphicsContent.

LoadGraphicsContent Chamado quando os recursos gráficos precisam ser carregados, iremos

sobrescrever esse método para carregar nossas imagens.

UnloadGraphicsContent Chamado quando os recursos gráficos precisam ser descarregados.

Update Chamado quando a lógica do jogo precisa ser atualizada, aqui criaremos as

rotinas que manterão nosso “mundo” atualizado.

Eu fiz uma tradução rasteira da documentação do XNA no Msdn, portanto perdoem os erros de tradução.

Logicamente, fiz uma “reescrita” no texto explicativo para adapta-lo as necessidades deste documento.

Configurando o ambiente.

Agora precisamos configurar o nosso jogo de acordo com o ambiente onde ele está rodando, coisas como

resolução, cores, etc. Para fazer isso, vamos modificar o código da nossa classe jogo, deixando-o da seguinte

forma: classjogo:Game { //permite aconfiguração do ambiente GraphicsDeviceManagerconfig_am; //construtor daclasse publicjogo() { config_am =newGraphicsDeviceManager(this); } }

Page 4: Tutorial XNA

Repare que depois disso até a cor de fundo da nossa aplicação já recebe um valor “default”. :-D

Agora adicione os seguintes namespaces: usingSystem; usingMicrosoft.Xna.Framework; usingMicrosoft.Xna.Framework.Content; usingMicrosoft.Xna.Framework.Graphics;

E finalmente podemos cuidar do código para carregar e exibir a imagem.

Primeiro, vamos adicionar um ContentManager que é usado para gerenciar nossos recursos (os arquivos de

texturas, por exemplo) e depois vamos instancia-lo no nosso construtor. classjogo:Game { //permite aconfiguração do ambiente GraphicsDeviceManagerconfig_am; //gerenciadordos recursos ContentManagerrecursos; //construtor daclasse publicjogo() { config_am =newGraphicsDeviceManager(this); recursos=newContentManager(Services); } }

Page 5: Tutorial XNA

Agora, antes de carregar propriamente os recursos, vamos adicionar a imagem da textura ao nosso projeto,

para tanto, criamos uma pasta no Solution Explorer com o nome “imagens” e depois adicionamos o arquivo

contendo a textura aqui chamado de “bola.jpg”

Agora clique com o botão direito na imagem e depois em “Properties” e procure pela propriedade “Asset

Name”, ela será usada para referenciar a imagem dentro do projeto.

No caso desse documento, o Asset Name usado é “bola”.

A Classe “Game” do XNA chama automaticamente o método “LoadGraphicsContent” para carregar os

recursos, como estamos herdando dessa classe, vamos aproveitar esse método para carregar as nossas

texturas, para fazer isso, vamos sobrescreve-lo.

//método paracarregar a textura. protectedoverridevoidLoadGraphicsContent(boolloadAllContent) { if(loadAllContent) { textura =recursos.Load<Texture2D>(@"imagens\bola"); } }

Nele testamos se os recursos já foram carregados através do parâmetro “LoadAllContent” e depois

carregamos o que for necessário. Repare que usamos o objeto “recursos” que é o nosso gerenciador de

recursos e que foi instanciado no construtor da classe, nele chamamos o método Load que informando o tipo

de objeto que vamos carregar (no caso uma textura 2D) e por final passamos uma string com o asset name

da imagem, como criamos uma pasta para a imagem, devemos preceder o nome com a pasta, o arroba é

usado para passar a string do “jeito que está” desprezando os caracteres especiais.

Pronto, agora passaremos a imagem para um sprite e depois mostraremos ela na tela.

A Classe Game do XNA também possui um método DRAW que é chamado automaticamente e que serve

para incluir o código onde os objetos são desenhados, iremos sobrescrever esse método na nossa classe, para

desenhar a nossa imagem. Mas, antes vamos adicionar um objeto “SpriteBatch” a nossa classe que vai servir

para desenhar de fato a imagem e depois vamos instancia-lo, também vamos adicionar um objeto vector2

que chamaremos de “posicao” e que será a posição do desenho na tela. O código da nossa classe ficará

assim: //ocódigo da classe jogo, herdado da classe pai "Game" classjogo:Game { //permite aconfiguração do ambiente GraphicsDeviceManagerconfig_am; ContentManagerrecursos; //texturas

Page 6: Tutorial XNA

Texture2Dtextura; //sprite SpriteBatchsprite; //struct paraposição, inicializado no zero (x = 0 e y =0) Vector2posicao =Vector2.Zero; //construtor daclasse publicjogo() { config_am =newGraphicsDeviceManager(this); recursos=newContentManager(Services); } //método paracarregar a textura. protectedoverridevoidLoadGraphicsContent(boolloadAllContent) { if(loadAllContent) { textura = recursos.Load<Texture2D>(@"imagens\bola"); sprite =newSpriteBatch(config_am.GraphicsDevice); } } protectedoverridevoidDraw(GameTimegameTime) { sprite.Begin(); sprite.Draw(textura, posicao,Color.White); sprite.End(); } }

Page 7: Tutorial XNA

Repare nas linhas em que instanciamos o objeto “sprite” e depois no método Draw onde desenhamos

efetivamente o sprite na tela.

Pronto, por agora já é o bastante, na próxima etapa do tutorial eu mostrarei como movimentar os objetos.

Page 8: Tutorial XNA

Tutorial de XNA - Parte II

Criado em 17/12/2007 10:00 Modificado em 21/12/2007 13:49 Tópicos Csharp Xna

Continuação do Tutorial de XNA para inciantes (segunda parte) - republicado conforme pedidos :-)

Para o melhor entendimento deste material é necessário que o leitor tenha conhecimentos prévios de C# e de

orientação a objetos e que tenha lido a primeira parte do tutorial.

Dando seqüência ao meu pequeno tutorial, vamos revisar os conceitos estudados no primeiro texto para em

seguida aprender algo novo. Na “aula” anterior, descobrimos o XNA, vimos o que é, qual a sua idéia e como

é a estrutura básica de um jogo. Baseado nisso, criamos a nossa primeira janela do jogo e desenhamos uma

imagem na tela.

Agora, vamos ver como movimentar a imagem, testando o recebimento de teclas e encerrando o jogo caso o

ESC seja pressionado.

Revisando a estrutura básica do jogo:

- Primeiro carregamos os elementos necessários ao jogo (método LoadGraphicsContent)

- Atualizamos os dados dos objetos do jogo (método Update)

- Desenhamos as imagens na tela. (método Draw)

Mas, de nada adianta desenhar a imagem no mesmo lugar, ou seja, ficar sem movimentação.

A nossa nova lógica vai ser bem simples, na rotina de atualização (Update) nós verificamos o status do

teclado (teclas pressionadas) e tomamos alguma atitude de acordo com o nosso novo status.

Para guardar o status do teclado, nós vamos usar a estrutura KeyboardState, ela vai nos informar as teclas

que foram pressionadas pelo usuário. Para obter as teclas usaremos o método GetState da classe Keyboard,

para tanto, usamos a seguinte sintaxe:

KeyboardState teclado = Keyboard.GetState();

Aqui nós guardamos o status do teclado, falta apenas testar se alguma das teclas pressionadas corresponde a

um de nossos comandos, para tanto, usaremos o método “IsKeyDown” da classe KeyboardState.

A enumeração “Keys” nos fornece a listagem das teclas para que você possa testar o status de cada uma

delas (caso seja necessário). Nesse caso testaremos a tecla “Right” (seta para a direita).

teclado.IsKeyDown(Keys.Right)

O resultado desse comando é do tipo “booleano” (true ou false) e informa se a tecla foi pressionada ou não.

Para utilizar essas classes, precisamos incluir o namespace Microsoft.Xna.Framework.Input.

Com esses recursos já podemos movimentar o sprite na tela, testando qual foi a tecla pressionada e

incrementado as posições X e Y do vetor “Posicao”. Repare na rotina de desenho do nosso sprite:

sprite.Draw(textura, posicao, Color.White);

“Posicao” é um objeto do tipo vector2 e possui dois atributos “x” e “y” do tipo float, que serão usados para

representar as posições do nosso sprite na tela, o que faremos aqui é, a cada pressionamento das teclas

direcionais, modificaremos os valores dessas coordenadas.

Page 9: Tutorial XNA

Devemos lembrar que as coordenadas da tela iniciam com a posição “zero” (ponto (0,0)) no canto superior

esquerdo da tela, ao contrário do que estamos acostumados quando estudamos o plano cartesiano, então

nossa tela tem as coordenadas assim:

Então, se incrementamos os valores de Y, na verdade, estamos fazendo com o que o Sprite se movimente

para baixo, os limites para desenhar o sprite, são as resoluções, ou seja, para os valores de X temos o

tamanho horizontal da tela e para Y o tamanho vertical.

Então o que fazemos é, na rotina Update, testamos as teclas pressionadas e atualizamos os valores de X e Y

do vetor posição, quando a rotina Draw for chamada, o sprite será desenhado no local correto, com as

posições atualizadas.

Por exemplo, para testar se o direcional para cima foi pressionado e movimentar o sprite para cima nós

faríamos:

if (teclado.IsKeyDown(Keys.Up)) { posicao.Y--; }

Podemos fazer o mesmo com as outras teclas que desejamos testar, assim, chegamos ao nosso novo código: using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; //executando o jogo namespace meu_jogo { class novo_jogo { static public void Main() //método principal da aplicação

Page 10: Tutorial XNA

{ jogo teste = new jogo(); teste.Run(); } } } //o código da classe jogo, herdado da classe pai "Game" class jogo : Game { //permite a configuração do ambiente GraphicsDeviceManager config_am; ContentManager recursos; //texturas Texture2D textura; //sprite SpriteBatch sprite; //struct para posição Vector2 posicao = Vector2.Zero; //construtor da classe public jogo() { config_am = new GraphicsDeviceManager(this); recursos = new ContentManager(Services); } //método para carregar a textura. protected override void LoadGraphicsContent(bool loadAllContent) { if (loadAllContent) { textura = recursos.Load<Texture2D>(@"imagens\bola"); sprite = new SpriteBatch(config_am.GraphicsDevice); } } protected override void Update(GameTime gameTime) { //um objeto para guardar o status do teclado KeyboardState teclado = Keyboard.GetState(); //testando a qual tecla foi pressionada e //incrementando a coordenada correta if (teclado.IsKeyDown(Keys.Right)) { posicao.X++; //mover para a direita } else if (teclado.IsKeyDown(Keys.Left)) { posicao.X--; //mover para a esquerda } else if (teclado.IsKeyDown(Keys.Down)) { posicao.Y++; //mover para baixo } else if (teclado.IsKeyDown(Keys.Up)) { posicao.Y--; //mover para cima } else if (teclado.IsKeyDown(Keys.Escape))

Page 11: Tutorial XNA

{ this.Exit(); //sair da aplicação } } protected override void Draw(GameTime gameTime) { config_am.GraphicsDevice.Clear(Color.Black); sprite.Begin(); sprite.Draw(textura, posicao, Color.White); sprite.End(); } }

Por hoje é só, aprendemos como mover o sprite na tela e como verificar o pressionamento de teclas, espero

que este texto seja útil a vocês.

Até a próxima.

Tutorial de XNA - Parte III

Criado em 19/12/2007 13:44 Modificado em 20/12/2007 14:06 Tópicos Csharp Xna

Continuação do meu tutorial de XNA para iniciantes - republicado :-)

Para o melhor entendimento deste material é necessário que o leitor tenha conhecimentos prévios de C# e

de orientação a objetos e que tenha lido a primeira e a segunda parte do tutorial.

Até agora nós descobrimos como é a estrutura de um jogo no XNA, como movimentamos os nossos

desenhos e como recebemos as teclas pressionadas no teclado. Nesta parte do tutorial eu vou falar sobre

um tópico muito importante dentro da área de jogos, a colisão.

Antes de falar da colisão usando o XNA propriamente dito, vou explicar a teoria por traz desse método de

colisão, ai sim, eu mostrarei um exemplo desse tipo de colisão usando o XNA.

Mas, o que é “colisão”?

Dentro dos jogos, você precisa muitas vezes testar se dois objetos estão colidindo, como no mundo real.

Por exemplo, se um carro do seu jogo bateu na parede, se um dos jogadores foi atingido por uma bala, se a

sua espada “ultra-mega-hiper-poderosa” atingiu o inimigo, ou simplesmente se sua bolinha atingiu o chão.

Mas, lembre, nos jogos 2D todos esses objetos são imagens e para testar se eles colidiram, você não pode

testar apenas as suas coordenadas de desenho. Ai é que entram as técnicas de colisão.

Um ponto importante é pensar primeiro que para cada caso existe uma técnica mais adequada, então o que

eu estou mostrando aqui, pode não ser adequado ao seu jogo simplesmente pelo modo como você o

implementou, ou então porque torna jogo mais lento, geralmente não há uma regra, cada caso é um caso.

Page 12: Tutorial XNA

Bounding Box (caixa delimitadora)

A idéia é bastante simples, nós vamos associar a cada objeto da tela uma área retangular, dada pelos

limites horizontais e verticais da sua imagem. Daí, testaremos se a intersecção desses limites, resultando ou

não na colisão. Ai você diz: “hein??!!” e eu digo, “ta bom vou explicar melhor :-)”

Repare na imagem abaixo, onde temos dois retângulos desenhados na tela:

Cada um deles ocupa uma área da tela delimitada por suas linhas de contorno, que são dadas pelos limites

horizontais e verticais, veja a imagem abaixo:

No caso do retângulo azul, ele é determinado pelas coordenadas X1, X2, Y1 e Y2. Sendo as outras

coordenadas pertencentes ao retângulo verde. Para testar a colisão basta verificar essas coordenadas.

Portanto usaremos essas linhas para determinar se um retângulo colidiu com o outro.

Vamos pegar o mesmo caso da figura anterior e estudar as coordenadas, na imagem anterior X1 e X2 são

menores que X3 o que significa que o segundo retângulo está ao lado direito do primeiro. Caso X1 e X2

fossem maiores que X3 o retângulo verde estaria à esquerda do azul.

A mesma comparação pode ser feita com as coordenadas Y das imagens, como Y1 e Y2 são menores que

Y3, então o retângulo azul está acima do verde, em caso contrário, estaria abaixo.

Mas, para nós, o mais importante é detectar a colisão e não a localização. Essa colisão acontece quando as

linhas que determinam nossos retângulos formam uma área em comum aos dois retângulos. Por exemplo:

Page 13: Tutorial XNA

Nesse caso, a área amarela é formada pela intersecção dos dois retângulos e nesse caso temos certeza de

que os dois objetos colidiram. No exemplo temos “X1 < X3 < X2 e Y1 < Y3 < Y2” o que indica uma

colisão por baixo e do lado direito.

Então, para testar se o objeto colidiu por baixo e pelo lado direito, precisamos apenas checar se as

coordenadas dele atendem a essa condição. Mas, isso não seria suficiente para nós que queremos uma

colisão mais genérica, então precisamos testar outros casos.

Bom, no meu caso vou criar um método muito simples que testa se existe uma intersecção entre as “caixas

delimitadoras” formadas pelas linhas do exemplo anterior, ele vai informar apenas se os objetos colidem

ou não, não me preocupei com a localização relativa dos objetos (acima, abaixo, etc).

Ai vai o código do método “testar_colisao” que retorna true em caso de colisão e false em caso contrário. protected bool testar_colisao(Texture2D box1, Vector2 posi1, Texture2D box2, Vector2 posi2) { //por padrão os objetos não colidem bool status = false; //coloque as posições em nomes mais fáceis :-) float x1 = posi1.X; float x2 = posi1.X + box1.Width; float x3 = posi2.X; float x4 = posi2.X + box2.Width; float y1 = posi1.Y; float y2 = posi1.Y + box1. Height; float y3 = posi2.Y; float y4 = posi2.Y + box2.Height; //teste os limites e veja se os objetos colidiram if ((((x3 <= x1) && (x1 <= x4)) || ((x3 <= x2) && (x2 <= x4))) && (((y3 <= y1) && (y1 <= y4)) || ((y3 <= y2) && (y2 <= y4)))) { //achei uma colisão!! status = true; } return status; //devolva o resultado }

Agora que já sabemos como testar a colisão, vamos ver um exemplo de código que usa esse método para

testar a colisão. O nosso exemplo vai usar todo o código dos tutoriais anteiores, só que no lugar de uma

bola, teremos os dois retângulos que usei nos exemplos deste tutorial para explicar como funciona a

colisão. Repare que para achar as coordenadas eu usei as propriedade Width (largura) e Heigth (altura)

Page 14: Tutorial XNA

das texturas.

O software é simples, temos dois retângulos, um fixo e outro que vai se mover de acordo com os as setas

do teclado, quando esses dois retângulos colidirem, um deles ficará transparente.

Inclui duas texturas, uma para cada retângulo:

//texturas Texture2D retanguloazul; Texture2D retanguloverde;

Depois incluímos os vetores de posição para cada um:

//vetor para posição Vector2 posicao_verde = Vector2.Zero; Vector2 posicao_azul; //vai ficar parado

Preciso de um lugar para guardar o resultado os testes de colisão:

//objeto para guardar o resultado dos testes de colisao public bool resultado_colid;

Além disso, para deixar um dos retângulos transparentes, eu preciso definir uma cor e determinar o Alpha

dela, usando um objeto do tipo “color” que recebe 4 argumentos, os índices R (red), G (green), B (blue) e o

A(alpha) que determina justamente os valores de transparência.

Esses valores variam de 0 a 255 e a mistura deles gera as cores no padrão RGB. Quando você aplica o

valor 0 ao A, indica transparência total e 255 indica sem transparência.

//cor transparente //branco com transparencia Color my_color = new Color(255, 255, 255, 150);

Para saber mais sobre isso, leia o artigo de André Furtado no SharpGames:

http://www.sharpgames.net/Artigos/Realizando+alpha+blending+-+ou+transparencia+-

+em+texturas+e+primitivas+graficas+do+XNA.xna

Agora, vamos ver como fica o código:

using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; //executando o jogo namespace meu_jogo { class novo_jogo { static public void Main() //método principal da aplicação { jogo teste = new jogo(); teste.Run(); } } }

Page 15: Tutorial XNA

//o código da classe jogo, herdado da classe pai "Game" class jogo : Game { //permite a configuração do ambiente GraphicsDeviceManager config_am; ContentManager recursos; //texturas Texture2D retanguloazul; Texture2D retanguloverde; //sprite SpriteBatch s_azul; SpriteBatch s_verde; //vetor para posição Vector2 posicao_verde = Vector2.Zero; Vector2 posicao_azul; //vai ficar parado //objeto para guardar o resultado dos testes de colisao public bool resultado_colid; //branco com transparencia Color my_color = new Color(255, 255, 255, 150); //construtor da classe public jogo() { config_am = new GraphicsDeviceManager(this); recursos = new ContentManager(Services); } //inicializa outros itens que não requerem a inicalização do dispositivo gráfico protected override void Initialize() { posicao_azul.X = 200; posicao_azul.Y = 200; base.Initialize(); } //método para carregar a textura. protected override void LoadGraphicsContent(bool loadAllContent) { if (loadAllContent) { retanguloazul = recursos.Load<Texture2D>(@"imagens\rect_azul"); retanguloverde = recursos.Load<Texture2D>(@"imagens\rect_verde"); s_azul = new SpriteBatch(config_am.GraphicsDevice); s_verde = new SpriteBatch(config_am.GraphicsDevice); //preciso inicializar as texturas antes de usar a colisão resultado_colid = testar_colisao(retanguloazul, posicao_azul,

retanguloverde, posicao_verde); } } protected override void Update(GameTime gameTime) { //um objeto para guardar o status do teclado KeyboardState teclado = Keyboard.GetState(); //veja se algum objeto colidiu resultado_colid = testar_colisao(retanguloazul, posicao_azul, retanguloverde,

posicao_verde);

Page 16: Tutorial XNA

//testando a qual tecla foi pressionada e //incrementando a coordenada correta if (teclado.IsKeyDown(Keys.Right)) { posicao_verde.X++; //mover para a direita } else if (teclado.IsKeyDown(Keys.Left)) { posicao_verde.X--; //mover para a esquerda } else if (teclado.IsKeyDown(Keys.Down)) { posicao_verde.Y++; //mover para baixo } else if (teclado.IsKeyDown(Keys.Up)) { posicao_verde.Y--; //mover para cima } else if (teclado.IsKeyDown(Keys.Escape)) { this.Exit(); //sair da aplicação } } protected override void Draw(GameTime gameTime) { config_am.GraphicsDevice.Clear(Color.Black);

//desenhe o retangulo azul em uma posicão fixa e sempre com a mesma cor. s_azul.Begin(); s_azul.Draw(retanguloazul, posicao_azul, Color.White); s_azul.End(); if (resultado_colid) //se colidiu, desenhe com transparencia { s_azul.Begin(); s_azul.Draw(retanguloverde, posicao_verde, my_color); s_azul.End(); } else //em caso contrário desenhe normal { s_azul.Begin(); s_azul.Draw(retanguloverde, posicao_verde, Color.White); s_azul.End(); } } //testa a colisao entre dois objetos //recebe como argumentos as texturas e os vetores de posições de cada objeto protected bool testar_colisao(Texture2D box1, Vector2 posi1, Texture2D box2,

Vector2 posi2) { //por padrão os objetos não colidem bool status = false; //coloque as posições em nomes mais fáceis :-) float x1 = posi1.X; float x2 = posi1.X + box1.Width; float x3 = posi2.X; float x4 = posi2.X + box2.Width;

Page 17: Tutorial XNA

float y1 = posi1.Y; float y2 = posi1.Y + box1.Height; float y3 = posi2.Y; float y4 = posi2.Y + box2.Height; //teste os limites e veja se os objetos colidiram if ((((x3 <= x1) && (x1 <= x4)) || ((x3 <= x2) && (x2 <= x4))) && (((y3 <= y1) && (y1 <= y4)) || ((y3 <= y2) && (y2 <= y4)))) { //achei uma colisão!! status = true; }

return status; //devolva o resultado } }

Agora vamos aos pontos fortes e fracos desse método de colisão, primeiro os pontos fortes:

- Fácil de implementar para objetos que estejam alinhados aos eixos X e Y.

- Tem bom desempenho para objetos alinhados com os eixos, se comparado a outros métodos.

Depois os pontos fracos:

- Não atende a maioria dos casos, visto que os objetos nem sempre são retangulares.

- Em objetos de formas não retangulares, provoca “falsas colisões” visto que as texturas nem sempre

preenchem toda a área das “caixas delimitadoras”.

Um exemplo simples, onde as texturas provocariam “falsas” colisões, seria o mesmo código, usando

“círculos no lugar de retângulos”.

Agora vou dar umas dicas de utilização desse método nos seus jogos:

- Se for usar esse método, desenhe os objetos de forma que eles ocupem o máximo da área da imagem,

isso vai reduzir os espaços em branco na imagem e por tabela reduz os erros nas colisões.

- Divida as imagens em imagens menores e anime via software, na hora de testar a colisão, você testa as

partes individualmente, isso melhora a aproximação e diminui muito o erro nas colisões por “bounding

box”. Por exemplo, se você tem um personagem do tipo medieval, um guerreiro com uma espada e quer

testar a colisão do sprite de ataque do guerreiro com um monstro, você tem duas opções, veja o exemplo

abaixo.

A primeira é desenhar o boneco parado e em um outro sprite, desenhar o ataque.

Se você fizer isso, vai testar toda a área do sprite de ataque e isso inclui as áreas abaixo e acima da espada

que não devem provocar colisão. Isso geraria falsos ataques e seu boneco seria “invencível” XD

Outra idéia, mais realista, é criar três sprites, um parado, outro de ataque e em separado, criar o sprite da

espada que será desenhado na posição do braço do seu guerreiro. Quando for necessário testar a colisão,

você testa apenas o sprite da espada e como a espada é pequena, a aproximação da colisão é boa.

Page 18: Tutorial XNA

Quando você for desenhar os objetos, mande desenhar a espada no local do braço e depois teste a colisão

apenas na espada, repare que o sprite da espada sozinho ocupa quase toda a área da imagem (pontilhado

vermelho), melhorando bastante a aproximação da colisão.

Agora, já sabemos como criar a estrutura básica de um jogo me XNA, sabemos desenhar na tela, mover,

usar transparência nos nossos sprites e por fim aprendemos um método de colisão. Com o que mostrei nas

três partes desse tutorial, você já pode criar diversos joguinhos, espero que a leitura tenha valido a pena,

por hoje é só e até o próximo tutorial ;-)

Page 19: Tutorial XNA

Tutorial de XNA - Parte IV

Criado em 02/01/2008 13:36 Modificado em 02/01/2008 14:24 Tópicos Csharp Xna Xna+Game+Studio

Continuação do tutorial de XNA para iniciantes, aborda desenho de imagens com áreas transparentes e colisão por pixel.

Até agora, nós descobrimos como é a estrutura de um jogo no XNA, como movimentamos os nossos

desenhos, como recebemos as teclas pressionadas no teclado e como funciona um método de colisão. Porém,

o método de colisão das “caixas delimitadoras” não é dos melhores, por causa das falhas que ele provoca

quando as imagens não preenchem todo o espaço retangular, assim, vamos tratar de um outro método de

colisão, a colisão por pixel.

Para o melhor entendimento deste material é necessário que o leitor tenha conhecimentos prévios de C#, de orientação a objetos e que tenha lido as outras 3 partes desse tutorial.

Mas, antes que alguém grite: “Espere ai!! O que é um pixel?? Como assim bitmap??”, eu farei uma pequena

explanação sobre os tipos de imagens e suas peculiaridades, assim o leitor vai entender melhor o método de

colisão por pixels e a teoria por traz dele.

Atualmente dois tipos de imagens são usados com mais freqüência nas aplicações, as imagens de bitmap e as

imagens vetoriais. No parágrafos abaixo, falarei sobre as duas, mas, só utilizaremos um tipo nesse tutorial,

os bitmaps.

O que são imagens de bitmap?

Pense em uma tabela enorme, com umas 200 linhas e 200 colunas, agora em cada quadrinho da tabela você

coloca um ponto com uma cor diferente, dependendo da disposição das cores dentro da sua tabela, você

pode até formar desenhos, essa é a idéia dos bitmaps.

Um bitmap é basicamente uma matriz de pontos coloridos onde cada ponto da matriz é chamado de pixel.

Podemos perceber isso se aumentarmos o zoom na visualização de uma foto, você perceberá os “quadrados”

que formam a imagem, inclusive verá que as curvas ficam “serrilhadas”, já que também são formadas por

pontos. Veja o exemplo na foto abaixo:

Esse tipo de imagem é usado em diversas aplicações, como o Paint2[1], por permitir a criação de efeitos

mais realistas, porém, mais pesados. Um dos problemas que acontece com mais freqüência quando se

trabalha com esse tipo de imagem, ocorre quando o usuário tenta redimensionar a imagem, principalmente

quando ele tenta aumentá-la.

Page 20: Tutorial XNA

Como as imagens de bitmap são formadas por pontos, quando se diminui a imagem apenas retiramos pontos,

tentando manter o padrão, com isso perdemos informação e por conseqüência perdemos qualidade na

imagem, no entanto, a visualização não é tão afetada.

Um problema maior acontece quando tentamos aumentar uma imagem, o software vai tentar manter o

padrão da imagem inserindo pontos com as cores mais próximas dos já existentes, isso causa um

“serrilhamento” e o resultado na maioria das vezes não é satisfatório.

O que são imagens vetoriais?

Bom, vocês já viram os problemas encontrados nas imagens de bitmap, agora imaginem uma gráfica, onde

as imagens são redimensionadas várias vezes para reproduzir a mesma imagem em vários tipos de

documentos e onde geralmente se precisa do melhor resultado possível na apresentação de um trabalho.

Lógico que se fossem usados somente bitmaps nesse tipo de ambiente, diversos problemas iriam surgir, por

isso se usa um outro tipo de imagem, as imagens vetoriais.

As imagens vetoriais são formadas por equações matemáticas que formam os objetos desenhados, quando

alguém tenta redimensionar uma imagem dessas, os pontos são recalculados e a imagem é refeita sem perder

qualidade. Esse tipo de imagem é ideal para desenhos que são redimensionados diversas vezes, como no

caso da gráfica. Um software bastante conhecido que utiliza imagens vetoriais é o Corel Draw.

Nos jogos 2D, em quase toda a sua totalidade, são usadas imagens de bitmap e agora que já sabemos o

básico sobre os tipos de imagem já podemos falar sobre as colisões por pixel.

Como funciona a colisão por pixel?

A idéia é bem simples, se duas imagens estão colidindo, as suas áreas retangulares possuem uma interseção,

uma área comum formada por suas “caixas delimitadoras” (lembre dos conceitos vistos na outra parte do

tutorial sobre colisão por “bounding box”), como as imagens usadas são duas imagens de bitmap essa

interseção, na verdade, é formada por duas áreas sobrepostas e essas áreas também possuem seus respectivos

pixels. Veja o exemplo:

Aqui temos duas imagens, um cavaleiro desferindo um golpe e um dragão, as imagens estão sobrepostas de

modo a formar uma área comum, destacada em amarelo. Se estivéssemos usando o modo de colisão por

Bounding Box, o dragão já teria sido atingido e isso seria mentira, pois o cavaleiro está a uma certa

distância do dragão, mas, como a área da imagem é retangular, a colisão seria positiva, ou seja, se a espada

do cavaleiro não emitir nenhuma aura mágica que ataque a distância, estaríamos apelando e o pobre dragão

sofreria um dano injusto. :-)

No nosso caso, a espada do cavaleiro desfere apenas golpes físicos e precisamos testar se ele acertou o

dragão ou não, então, o método que já conhecemos não é adequado e precisamos usar colisões por pixel.

Vejamos mais alguns exemplos sobre colisões destacando áreas comuns em algumas imagens, é a partir

dessa área que vamos descobrir se existe colisão ou não, repare que se não existir área comum, a colisão já

está descartada. Considere a área branca dentro dos retângulos pontilhados como transparente.

Page 21: Tutorial XNA

Repare que nos três exemplos, se estivéssemos usando o método de colisão anterior (bounding box) os três

casos resultariam em uma colisão positiva. Vale ressaltar que os desenhos são muito simples.

Nas imagens que possuem uma área transparente algumas áreas da imagem não são desenhadas na tela.

Essas áreas possuem pixels e nesses pixels as cores armazenadas possuem o valor Alpha (responsável pela

transparência) com o valor 0 (zero), esse pequeno detalhe é a chave da colisão por pixel. Basta testar as

cores dos pixels das duas áreas dessa intersecção, se uma das cores for transparente então não há nada

desenhado ali e por tabela, não há colisão, a colisão só vai existir se os pixels presentes na interseção não

forem preenchidos, em ambas as imagens, com a cor de transparência.

Pronto, agora já sabemos como funciona a colisão por pixels, agora, veremos como implementar esse

método no seu jogo.

A implementação.

Aqui, vou comentar somente sobre os itens que não conhecemos ainda, se você não leu os outros tutoriais,

talvez seja uma boa hora para procurar neles algum item que você não conhece. Depois de fazer os

comentários eu vou colocar o código por partes (com mais comentários) e depois colocarei o código

completo, serão poucas modificações e faremos poucas “continhas”, o mais importante é a lógica usada para

resolver o problema.

Antes vamos cuidar de um detalhe nas imagens, até agora, todos os nossos desenhos ocupavam toda a área

da imagem e nunca nos preocupamos em desenhar uma área da imagem transparente, sempre desenhávamos

da mesma cor do fundo. Agora o nosso cavaleiro medieval não ocupa toda a área, inclusive esse é um dos

motivos para usarmos colisão por pixel, e para desenhar uma área da imagem transparente precisaremos usar

um formato de imagem que suporte transparência, um desses formatos é o PNG.

Nesse tipo de imagem são salvos no arquivo informações extras que vão dizer as aplicações quais áreas da

imagem são transparentes.

Um software gratuito que você pode usar para desenhar as imagens, com suporte a arquivos no formato

PNG, é o Paint.NET que pode ser baixado nesse endereço: http://baixaki.ig.com.br/download/Paint-

NET.htm

Uma imagem com fundo branco não é o mesmo que uma imagem com o fundo transparente, veja a

diferença nesse exemplo:

Page 22: Tutorial XNA

O fundo quadriculado na imagem representa as área transparente e essa parte da imagem não será mostrada

na tela quando estivermos desenhando as nossas imagens no jogo, já as imagens com fundo branco, terão a

cor branca desenhada na tela já que pixels brancos também são pixels :-)

Você pode encontrar um texto explicando os formatos de imagem suportados pelo XNA no blog da equipe:

http://blogs.msdn.com/xna/archive/2006/08/29/730168.aspx

Vamos relembrar a nossa idéia geral, achar a interseção das imagens (isso nós já sabemos fazer), descobrir

quais cores estão armazenadas naquela área de interseção (ainda vamos descobrir como) e depois testar se as

cores guardadas naqueles locais são transparentes ou não.

Manterei muita coisa do exemplo anterior, mudando só o que for necessário:

//texturas Texture2D TexCavaleiro; Texture2D TexDragao; //sprites SpriteBatch SpCavaleiro; SpriteBatch SpDragao; //vetores para as posições Vector2 posiCavaleiro = Vector2.Zero; Vector2 posiDragao = Vector2.Zero; //vai ficar parado //objeto para guardar o resultado dos testes de colisao bool resultado_colid = false; //cor semi-transparente Color my_color = new Color(255, 255, 255, 150); //branco com transparencia!!

Esses itens já são conhecidos, se você tem alguma dúvida na utilização desses itens consulte as outras partes

do tutorial.

Também precisamos de lugar para guardar as cores que estão presentes nas nossas texturas, para isso vamos

usar um Array do tipo Color:

//array com as cores das texturas Color[] coresDrag; Color[] coresCav;

No exemplo anterior, o nosso cavaleiro teria velocidade 1, ou seja, ele seria movimentado de pixel em pixel

incrementando suas coordenadas de 1 em 1, nesse exemplo eu vou incluir um item que vai controlar sua

velocidade, que inicialmente será 5:

//velocidade do cavaleiro int velocidade = 5;

Agora vamos às inicializações, nosso dragão ficará sempre parado na posição 200x200: //inicializa outros itens que não requerem a inicalização do dispositivo gráfico protected override void Initialize() { //dragão na posição (200,200), cuidado!! posiDragao.X = 200; posiDragao.Y = 200; base.Initialize();

Page 23: Tutorial XNA

}

Depois carregamos nossas texturas e aproveitamos para pegar as cores das texturas e armazenar nos nossos

arrays de cores:

//método para carregar as texturas. protected override void LoadGraphicsContent(bool loadAllContent) { if (loadAllContent) { //carregando texturas TexCavaleiro = recursos.Load<Texture2D>(@"imagens\cavaleiro"); TexDragao = recursos.Load<Texture2D>(@"imagens\dragao"); SpCavaleiro = new SpriteBatch(config_am.GraphicsDevice); SpDragao = new SpriteBatch(config_am.GraphicsDevice); //determinando o tamanho do meu array coresCav = new Color[TexCavaleiro.Width * TexCavaleiro.Height]; coresDrag = new Color[TexDragao.Width * TexDragao.Height]; //armazenando as cores das texturas em um array TexCavaleiro.GetData(coresCav); TexDragao.GetData(coresDrag); } }

Repare que o array de cores é criado com o tamanho de cores que tiver na textura, por exemplo, se minha

textura do cavaleiro tiver 50x50 (pixels), então o meu array de cores terá de ter um tamanho de 2500 cores,

ou seja, a altura vezes a largura da imagem (50x50).

//determinando o tamanho do meu array coresCav = new Color[TexCavaleiro.Width * TexCavaleiro.Height];

Agora que eu já sei o tamanho, preciso preencher o array de cores com as cores da textura usando o método

GetData da classe Texture2D que copia as cores da textura no array.

//armazenando as cores das texturas em um array TexCavaleiro.GetData(coresCav);

Um detalhe importante e um pouco óbvio é que eu só posso usar esse método depois de carregar a textura,

caso contrário eu não teria o que colocar no array.

Depois que carregarmos as texturas e tivermos as cores delas em um array, podemos tratar do método que

irá controlar a atualização do nosso “mundo”, o método UpDate, nele iremos testar a colisão e usar a nossa

variável que controla a velocidade:

protected override void Update(GameTime gameTime) { //um objeto para guardar o status do teclado KeyboardState teclado = Keyboard.GetState(); //os retangulos que contem as coordenadas precisam ser //atualizados a cada loop, por isso estão aqui. Rectangle retanguloCav = new Rectangle((int)posiCavaleiro.X, (int)posiCavaleiro.Y, TexCavaleiro.Width, TexCavaleiro.Height); Rectangle retanguloDrag = new Rectangle((int)posiDragao.X, (int)posiDragao.Y, TexDragao.Width, TexDragao.Height); //veja se algum objeto colidiu

Page 24: Tutorial XNA

resultado_colid = testar_colisao_pixel(retanguloCav, retanguloDrag, coresCav, coresDrag); //testando a qual tecla foi pressionada e //incrementando a coordenada correta if (teclado.IsKeyDown(Keys.Right)) { posiCavaleiro.X += velocidade; //mover para a direita } else if (teclado.IsKeyDown(Keys.Left)) { posiCavaleiro.X -= velocidade; //mover para a esquerda } else if (teclado.IsKeyDown(Keys.Down)) { posiCavaleiro.Y += velocidade; //mover para baixo } else if (teclado.IsKeyDown(Keys.Up)) { posiCavaleiro.Y -= velocidade; //mover para cima } else if (teclado.IsKeyDown(Keys.Escape)) { this.Exit(); //sair da aplicação } }

O método UpDate funciona da seguinte forma, primeiro precisamos checar o status do teclado, depois

instanciamos dois “Retangulos”, eles irão representar as coordenadas das imagens e serão usados na nossa

rotina de colisão, para criar um retângulo basta fornecer dois pontos, um com as coordenadas iniciais e outro

com as coordenadas finais, no nosso caso a posição da imagem e as coordenadas de final da imagem

(baseadas no tamanho dela), veja o exemplo:

Rectangle retanguloCav = new Rectangle((int)posiCavaleiro.X, (int)posiCavaleiro.Y, TexCavaleiro.Width, TexCavaleiro.Height);

Esse retângulo terá o mesmo tamanho que a imagem e servirá para encontrarmos a interseção entre as duas

imagens. Lembre que a cada movimentação as coordenadas mudam e é por isso que temos de criar o

retângulo a medida que precisamos testar novas colisões.

Agora que já temos as coordenadas, podemos testar se houve colisão, mais a frente eu vou detalhar o método

de colisão.

//veja se algum objeto colidiu resultado_colid = testar_colisao_pixel(retanguloCav, retanguloDrag, coresCav,

coresDrag);

Depois testamos qual a tecla que foi pressionada e realizamos a ação adequada, aqui eu testarei se a tecla

pressionada foi uma das setas direcionais e incremento a posição do personagem usando a nossa variável

“velocidade”, por exemplo:

//testando a qual tecla foi pressionada e //incrementando a coordenada correta if (teclado.IsKeyDown(Keys.Right)) { posiCavaleiro.X += velocidade; //mover para a direita }

Agora o nosso exemplo está praticamente pronto, só falta explicar o método que testa a colisão, ele receberá

como argumentos, as cores das texturas e as coordenadas (através dos retângulos):

Page 25: Tutorial XNA

static bool testar_colisao_pixel(Rectangle retanguloA, Rectangle retanguloB, Color[]

coresA, Color[] coresB)

Depois nós vamos encontrar a interseção das imagens usando as suas coordenadas: //encontrando as coordenadas da interseção das imagens int topo = Math.Max(retanguloA.Top, retanguloB.Top); int inferior = Math.Min(retanguloA.Bottom, retanguloB.Bottom); int esquerda = Math.Max(retanguloA.Left, retanguloB.Left); int direita = Math.Min(retanguloA.Right, retanguloB.Right);

Agora, baseado nessas coordenadas da interseção, vamos percorrer os arrays de cores para testar se houve

colisão, a cada loop, testamos as cores das imagens para verificar se ambas são diferentes da transparência, o

que caracterizaria a colisão, como o array só tem uma dimensão, precisamos fazer umas continhas para

achar a cor correspondente a área de interseção das imagens e depois testar cada cor dessa área:

for (int y = topo; y < inferior; y++) { //enquanto existirem colunas na interseção, continue procurando pontos for (int x = esquerda; x < direita; x++) { //o array de cores só tem uma dimensão, calcule as coordenadas e //retorne a cor daquela posição Color corA = coresA[(y - retanguloA.Top) * retanguloA.Width + (x - retanguloA.Left)]; //ache as coordenadas para o outro objeto Color corB = coresB[(y - retanguloB.Top) * retanguloB.Width + (x - retanguloB.Left)]; //agora que já achou as cores, teste se alguma delas é transparente //se nenhuma das cores for transparente, colidiu! if (corA.A != 0 && corB.A != 0) { //encontrou colisão return true; } } }

Agora, já podemos ir para o código inteiro:

#region referencias using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; #endregion //executando o jogo namespace meu_jogo { class novo_jogo { static public void Main() //método principal da aplicação { jogo teste = new jogo(); teste.Run(); } } }

Page 26: Tutorial XNA

//o código da classe jogo, herdado da classe pai "Game" class jogo : Game { //permite a configuração do ambiente GraphicsDeviceManager config_am; ContentManager recursos; //texturas Texture2D TexCavaleiro; Texture2D TexDragao; //sprites SpriteBatch SpCavaleiro; SpriteBatch SpDragao; //vetores para as posições Vector2 posiCavaleiro = Vector2.Zero; Vector2 posiDragao = Vector2.Zero; //vai ficar parado //array com as cores das texturas Color[] coresDrag; Color[] coresCav;

//velocidade do cavaleiro int velocidade = 5; //objeto para guardar o resultado dos testes de colisao public bool resultado_colid = false; //cor semi-transparente Color my_color = new Color(255, 255, 255, 150); //branco com transparencia!! //construtor da classe public jogo() { config_am = new GraphicsDeviceManager(this); recursos = new ContentManager(Services); } //inicializa outros itens que não requerem a inicalização do //dispositivo gráfico protected override void Initialize() { //dragão na posição (200,200), cuidado!! posiDragao.X = 200; posiDragao.Y = 200; base.Initialize(); } //método para carregar as texturas. protected override void LoadGraphicsContent(bool loadAllContent) { if (loadAllContent) { //carregando texturas TexCavaleiro = recursos.Load<Texture2D>(@"imagens\cavaleiro"); TexDragao = recursos.Load<Texture2D>(@"imagens\dragao"); SpCavaleiro = new SpriteBatch(config_am.GraphicsDevice); SpDragao = new SpriteBatch(config_am.GraphicsDevice); //determinando o tamanho do meu array coresCav = new Color[TexCavaleiro.Width * TexCavaleiro.Height];

Page 27: Tutorial XNA

coresDrag = new Color[TexDragao.Width * TexDragao.Height]; //armazenando as cores das texturas em um array TexCavaleiro.GetData(coresCav); TexDragao.GetData(coresDrag); } } protected override void Update(GameTime gameTime) { //um objeto para guardar o status do teclado KeyboardState teclado = Keyboard.GetState(); //os retangulos que contem as coordenadas precisam ser //atualizados a cada loop, por isso estão aqui. Rectangle retanguloCav = new Rectangle((int)posiCavaleiro.X, (int)posiCavaleiro.Y, TexCavaleiro.Width, TexCavaleiro.Height); Rectangle retanguloDrag = new Rectangle((int)posiDragao.X, (int)posiDragao.Y, TexDragao.Width, TexDragao.Height); //veja se algum objeto colidiu resultado_colid = testar_colisao_pixel(retanguloCav, retanguloDrag, coresCav, coresDrag); //testando a qual tecla foi pressionada e //incrementando a coordenada correta if (teclado.IsKeyDown(Keys.Right)) { posiCavaleiro.X += velocidade; //mover para a direita } else if (teclado.IsKeyDown(Keys.Left)) { posiCavaleiro.X -= velocidade; //mover para a esquerda } else if (teclado.IsKeyDown(Keys.Down)) { posiCavaleiro.Y += velocidade; //mover para baixo } else if (teclado.IsKeyDown(Keys.Up)) { posiCavaleiro.Y -= velocidade; //mover para cima } else if (teclado.IsKeyDown(Keys.Escape)) { this.Exit(); //sair da aplicação } } protected override void Draw(GameTime gameTime) { config_am.GraphicsDevice.Clear(Color.Black); //desenhe o retangulo azul em uma posicão fixa e sempre //com a mesma cor. SpDragao.Begin(); SpDragao.Draw(TexDragao, posiDragao, Color.White); SpDragao.End(); if (resultado_colid) //se colidiu, desenhe com transparencia { SpCavaleiro.Begin(); SpCavaleiro.Draw(TexCavaleiro, posiCavaleiro, my_color); SpCavaleiro.End();

Page 28: Tutorial XNA

} else //em caso contrário desenhe normal { SpCavaleiro.Begin(); SpCavaleiro.Draw(TexCavaleiro, posiCavaleiro, Color.White); SpCavaleiro.End(); } } //testa a colisao por pixel entre dois objetos //recebe como argumentos dois retangulos que formam a interseção //das imagens e os arrays de cores das imagens static bool testar_colisao_pixel(Rectangle retanguloA, Rectangle retanguloB, Color[] coresA, Color[] coresB) { //encontrando as coordenadas da interseção das imagens int topo = Math.Max(retanguloA.Top, retanguloB.Top); int inferior = Math.Min(retanguloA.Bottom, retanguloB.Bottom); int esquerda = Math.Max(retanguloA.Left, retanguloB.Left); int direita = Math.Min(retanguloA.Right, retanguloB.Right); //agora resta achar as cores de cada ponto da interseção, //dentro dos arrays das texturas. //enquanto não chegar a coordenada y final da interseção, encontre //uma cor para testar. for (int y = topo; y < inferior; y++) { //enquanto existirem colunas na interseção, //continue procurando pontos for (int x = esquerda; x < direita; x++) { //o array de cores só tem uma dimensão, calcule as //coordenadas e retorne a cor //daquela posição Color corA = coresA[(y - retanguloA.Top) * retanguloA.Width + (x - retanguloA.Left)]; //ache as coordenadas para o outro objeto Color corB = coresB[(y - retanguloB.Top) * retanguloB.Width + (x - retanguloB.Left)]; //agora que já achou as cores, teste se alguma delas é //transparente //se nenhuma das cores for transparente, colidiu! if (corA.A != 0 && corB.A != 0) { //encontrou colisão return true; } } } return false; //não colidiram. } }

Esse código é bem básico, mas, já deve servir como base para um jogo simples, mostra como desenhar uma

imagem com uma área transparente e ainda exemplifica a colisão por pixels. Também usei boa parte do

código do exemplo anterior, possibilitando uma revisão dos conceitos e ainda aproveitei para falar sobre os

tipos de imagens (vetoriais e de bitmap), espero que tenha sido útil e até a próxima parte deste tutorial.

Page 29: Tutorial XNA

Tutorial de XNA - Parte V

Criado em 09/01/2008 10:02 Modificado em 09/01/2008 10:39 Tópicos Csharp Xna Xna+Game+Studio

Continuação do tutorial de XNA para iniciantes, mostra como desenhar textos na tela e como realizar um evento de tempos em tempos.

Para o melhor entendimento deste material é necessário que o leitor tenha conhecimentos prévios de C#, de

orientação a objetos e que tenha lido as outras 4 partes desse tutorial.

O que já foi visto nas outras partes do tutorial permite a criação de vários joguinhos, se você ainda não criou

o seu, talvez esteja na hora de criar algo, mesmo que seja para praticar os conhecimentos adquiridos.

Para chegar até aqui, passamos pela estrutura básica de um jogo no XNA, vimos como receber a entrada do

teclado, desenhamos nossas imagens na tela, movimentamos o personagem e aprendemos dois métodos de

colisão, além de utilizar áreas transparentes em nossas imagens para melhorar os nossos gráficos :-)

Nesta parte do tutorial mostrarei como desenhar textos na tela, como formatar a fonte e aproveitarei para dar

um exemplo de como realizar uma tarefa de tempos em tempos no seu jogo.

Cuidado ao utilizar fontes de terceiros no seu jogo.

Antes de mostrar como utilizar os textos no seu jogo, devo tocar em um ponto importante das fontes,

chamado licença. Nem todas as fontes que você usa permitem que você as utilize em seus projetos, portanto,

conheça bem a licença das fontes que pretende usar antes de começar a projetar o visual do seu jogo, porque

não seria agradável perceber ao final do projeto que sua fonte não pode ser usada porque a licença não

permite.

A primeira parte do projeto, uma nova estrutura.

Até agora nós sempre adicionamos novos itens a estrutura já existente do nosso projeto, porém, como o

assunto desta parte do tutorial é relativamente simples, eu resolvi criar um outro projeto para mostrar como

desenhar as fontes.

O nosso novo projeto será bem simples, ele mostrará na tela o tempo gasto no jogo (centralizado). Também

usarei alguns outros itens já vistos, como transparência, para mostrar as possibilidades de uso nos textos.

Eu resolvi criar este exemplo mais simples porque o código estava ficando “didaticamente” grande e eu

preferi isolar os temas para tornar a coisa mais modular, permitindo que as pessoas estudem as partes do

tutorial em separado. Aproveite para revisar a estrutura de um jogo no XNA.

Vamos criar um projeto do tipo “windows game” e depois apagar o código pronto, se você preferir adaptar o

código ao meu exemplo, fique a vontade, depois do que já foi visto nas outras partes do tutorial você já deve

ser capaz de fazer isso sozinho.

Depois de ter o projeto básico pronto vamos adicionar os seguintes itens ao nosso código, dentro da nossa

classe herdada da classe game (vide outros tutoriais):

//string para mostrar texto na tela. string meu_texto; string resolucao; //posicao do texto na tela Vector2 posicao;

Page 30: Tutorial XNA

//nova linha para o tempo com transparência Vector2 posicao2; //posicao do texto da resolucao na tela Vector2 posi_resolucao = Vector2.Zero; //vai informar o ultimo multiplo de cinco, para controlar os eventos int segundo_anterior = 0; //contador dos multiplos de cinco, será incrementado a cada cinco segundos int total = 0; //cor para desenhar os textos com transparência Color teste = new Color(Color.Yellow.R, Color.Yellow.G, Color.Yellow.B, 150);

Até aqui não temos nenhuma novidade, só o contador e o “segundo_anterior” que indicará o ultimo múltiplo

de 5 (cinco) que passou, isso vai ser explicado melhor mais a frente.

Agora temos os itens principais do tutorial, o SpriteFont e o nosso já conhecido SpriteBatch que vão ser

responsáveis por desenhar nossos textos na tela.

//representa a fonte SpriteFont minhafont; //vai servir para desenhar a fonte na tela SpriteBatch drawfont;

Pronto, agora só precisamos importar a fonte que vai ser usada no nosso pequeno exemplo, por propósitos

práticos escolhi a fonte Arial, que é simples e existe na maioria dos PCs com Microsoft Windows.

Para importar a fonte precisamos adicionar ao nosso projeto um arquivo que vai descrever a fonte usada, sua

formatação e o conjunto de caracteres que vai ficar disponível para utilização, para adicionar esse arquivo ao

projeto vá ao “Solution Explorer” clique com o botão direito sobre o nome do projeto, procure a opção

“Add” e depois clique em “New Item”.

Na janela que vai aparecer, procure pelo item “Sprite Font”, dê um nome a ele (no meu projeto eu coloquei

“arial.spritefont”) e depois clique em “Add”, o arquivo será adicionado ao projeto. Esse arquivo vai

descrever a fonte e sua formatação através de XML3[1].

Esse arquivo também possui um “Asset Name” (um nome que vai ser usado dentro do código), para mudar

esse nome clique com o botão direito sobre o arquivo, e depois clique em Properties, procure a propriedade

Asset Name e coloque o nome que você quiser, eu resolvi colocar o nome “arial”, por agora, acompanhe a

minha escolha.

Agora vamos modificar a formatação da fonte, dê um clique duplo no arquivo “arial.spritefont”, ele será

aberto e contém os campos responsáveis pela formatação da fonte. Perceba que o arquivo já vem comentado

em inglês, para facilitar e tornar as coisas mais didáticas eu fiz uma “tradução/adaptação” dos comentários,

ou seja, traduzi fazendo umas modificações.

Veja como ficou o meu arquivo, os comentários próximos a cada “tag” são alto explicativos, quando não

forem eu falarei a respeito:

<?xml version="1.0" encoding="utf-8"?> <!--

Page 31: Tutorial XNA

Este arquivo em XML contém a descrição da fonte e será lido pelo XNA Framework content

pipeline. Siga os comentários para personalizar a aparência da fonte no seu jogo e para

selecionar os caracteres que estão disponíveis para utilzação. --> <XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics"> <Asset Type="Graphics:FontDescription"> <!-- Modifique esta string para selecionar a fonte que será importada. O nome da fonte deve

ser o mesmo que é usado no Windows. --> <FontName>Arial</FontName> <!-- O tamanho é um valor de ponto flutuante (float) e é medido em pontos, modifique este

valor para alterar o tamanho da fonte. --> <Size>24</Size> <!-- O espaço entre as letras é um valor em ponto flutuante e é medido em pixels. Modifique

esse valor para aumentar o espaço entre os caracteres. --> <Spacing>2</Spacing> <!-- A tag Style controla o estilo da fonte. As opções válidas são "Regular", "Bold",

"Italic", e "Bold, Italic", e são "case sensitive" (sensíveis a maiúscula e minúscula). --> <Style>Regular</Style> <!-- A tag CharacterRegions controla a faixa de caracteres disponíveis na descrição da

fonte. O padrão para a faixa de caracteres é iniciar no valor 32 (espaço no ASCII) e

terminar no 126 (o til), os caracteres que estiverem nessa faixa serão montados e

disponibilizados para utilização no seu jogo. Esse intervalo padrão cobre o conjunto básico de caracteres latino e está ordenado de

acordo com o padrão Unicode. --> <CharacterRegions> <CharacterRegion> <Start>&#32;</Start> <End>&#126;</End> </CharacterRegion> </CharacterRegions> </Asset> </XnaContent> Para saber o nome da fonte que deve ser usado no seu jogo, consulte a pasta Fontes dentro do Painel de

Controle do Microsoft Windows, lá você encontra todas as fontes que estão instaladas na sua máquina.

O XNA suporta os mesmos formatos de fontes que a classe System.Drawing.Font do .NET Framework,

possuindo suporte a fontes “True Type”, OpenType (limitado) mas, não suportando bitmap fonts (.fon).

A minha formatação foi simples, usei a fonte Arial (True Type), tamanho 24, 2 de espaço entre as letras e

estilo Regular, já o campo “CharacterRegions” exige uma explicação mais detalhada.

Esse campo informa quais caracteres serão montados e disponibilizados para utilização no seu projeto, como

já foi dito nos comentários o intervalo padrão fornecido com o arquivo é 32-126 que cobre todos os

caracteres latinos básicos, repare que você pode aumentar ou diminuir esse intervalo, aumentando ou

diminuindo respectivamente o numero de caracteres que serão montados e disponibilizados no seu jogo.

Page 32: Tutorial XNA

Vale ressaltar que o número de caracteres necessários a determinados projetos vai variar de região para

região, na china, por exemplo, o número de caracteres é muito maior e usar todos os caracteres em um

projeto iria requerer muito espaço e aumentaria o tempo gasto na execução.

Uma solução é personalizar quais caracteres serão importados para o seu projeto, um texto sobre isso pode

ser encontrado nesse link:

http://mtbeta.msdn.microsoft.com/pt-br/library/bb447751.aspx?altlang=pt-br

Caso você tente utilizar um caractere que não esteja disponível na faixa de valores fornecida no seu

SpriteFont, será gerado um Exception, dependendo da situação onde você tentou usar o texto. Caso você

tenha passado o texto a um método, por exemplo, poderia ser disparado um ArgumentException que ocorre

quando um argumento inválido é passado a um método, para evitar esse tipo de situação tente garantir que

os caracteres que você precisa estão dentro da faixa de valores fornecida no SpriteFont, para prevenir

surpresas em projetos grandes, utilize tratamento de erros.

No nosso caso, você pode usar tranquilamente a faixa de valores fornecida.

Encapsulando a fonte.

Depois de alterar o nosso arquivo “arial.spritefont” com a formatação escolhida, vamos importar o

conjunto de caracteres necessários ao nosso projeto, fazemos isso no método “LoadGraphicsContent”,

onde carregamos todos os recursos necessários ao projeto e inicializamos o “SpriteBatch” com o

dispositivo gráfico.

//carregando os recursos "externos" protected override void LoadGraphicsContent(bool loadAllContent) { if (loadAllContent) { //olha o asset name da fonte ai gente! :-) minhafont = recursos.Load<SpriteFont>("arial"); drawfont = new SpriteBatch(grafico.GraphicsDevice); } } Agora vamos ao método UpDate que vai atualizar os dados do nosso projeto, incrementando o contador e

verificando se já se passaram 5 (cinco) segundos. Essa rotina também vai preencher as nossas strings e

atualizar as posições onde os textos serão desenhados.

protected override void Update(GameTime gameTime) { //pegando o status do teclado para testar a saida KeyboardState teclado = Keyboard.GetState(); //o texto será o tempo total gasto no jogo meu_texto = gameTime.TotalGameTime.ToString(); //a posição será centralizada float X = grafico.GraphicsDevice.Viewport.Width / 2; float Y = grafico.GraphicsDevice.Viewport.Height / 2; //mostre a resolução da tela na tela resolucao = grafico.GraphicsDevice.Viewport.Width.ToString() + " x " + grafico.GraphicsDevice.Viewport.Height.ToString(); posicao = new Vector2(X,Y); //nova linha com transparência posicao2 = new Vector2(X,Y); //simplifique o nome para melhorar o código int segundo_atual = gameTime.TotalRealTime.Seconds;

Page 33: Tutorial XNA

//incremente o contador a cada cinco segundos if(((segundo_atual % 5) == 0) && (segundo_atual != 0) && (segundo_atual != segundo_anterior) ) { segundo_anterior = segundo_atual; total++; } //sair se pressionar esc if (teclado.IsKeyDown(Keys.Escape)) { this.Exit(); } }

Nesse código temos pouca coisa nova, na verdade apenas alguns itens são novos aqui. O objeto GameTime

fornece o tempo de execução do jogo e usamos o método “ToString” para converter o tempo para a nossa

string que será exibida na tela.

//o texto será o tempo total gasto no jogo meu_texto = gameTime.TotalGameTime.ToString();

Depois eu uso o objeto que representa nosso dispositivo gráfico para descobrir qual a resolução em que

estou trabalhando, você também poderia usar esse objeto para determinar a resolução que deseja trabalhar,

no meu caso eu apenas guardei os valores para usar depois, quando eu for desenhar o texto na tela,

centralizado. Repare que como eu vou centralizar o texto, já armazenei o valor dividido por 2 (dois).

//a posição será centralizada float X = grafico.GraphicsDevice.Viewport.Width / 2; float Y = grafico.GraphicsDevice.Viewport.Height / 2;

Agora vamos ao código que vai incrementar o contador a cada cinco segundos. A idéia é simples, testamos

os segundos, caso eles sejam divisíveis por 5 (cinco), então passaram mais cinco segundos depois do ultimo

incremento. Só que temos um problema, durante um segundo, o valor vai ser divisível por cinco, isso faz

com que o contador seja incrementado várias vezes a cada múltiplo de cinco, para solucionar isso, basta

guardar o valor do ultimo múltiplo em algum lugar e testar se ele já não foi utilizado.

Por exemplo, suponhamos que tenha passado 3 segundos, logo, 3 / 5 = 0 e resta 3, ou seja, não é divisível

por cinco. Agora suponhamos que passaram 15 segundos, logo 15 / 5 = 3 e resta 0 (zero), ou seja, é divisível

por cinco e devemos incrementar o total. Mas lembre que o valor do campo segundos, permanecerá sendo

15 por um segundo e no próximo loop, ainda seria divisível por 5, incrementando erroneamente o total.

Para resolver basta fazer o seguinte, guardamos o valor quinze logo depois de descobrirmos que ele é

divisível e no próximo loop testamos se o valor é igual ao ultimo que foi usado. Lembre que 0 (zero) / 5 = 0

e resta 0, logo também é divisível mas, no segundo 0 (zero) não podemos contar que passou cinco segundos

:-)

O código ficou assim:

//simplifique o nome para melhorar o código int segundo_atual = gameTime.TotalRealTime.Seconds; //incremente o contador a cada cinco segundos if(((segundo_atual % 5) == 0) && (segundo_atual != 0) && (segundo_atual != segundo_anterior) ) { segundo_anterior = segundo_atual; total++; }

Page 34: Tutorial XNA

Isso vai fazer com que o objeto “total” seja incrementado a cada cinco segundos, você poderia colocar

qualquer coisa no lugar dele, poderia chamar um método que faz chover no seu jogo a cada cinco segundos

ou fazer com que o sol fosse embora a cada meia hora de jogo.

Agora vamos ver como desenhar isso na tela.

protected override void Draw(GameTime gameTime) { //limpe a tela com a cor preta grafico.GraphicsDevice.Clear(Color.Black); //centralizar posicao.X = posicao.X - (minhafont.MeasureString(meu_texto).X)/2; posicao2.X = posicao2.X -(minhafont.MeasureString(meu_texto).X)/2; //pular uma linha e deixar um espaço de 10 posicao2.Y = posicao2.Y + (minhafont.MeasureString(meu_texto).Y + 10); drawfont.Begin(); //desenhe a resolução com o contador drawfont.DrawString(minhafont, resolucao + " " + total.ToString(), posi_resolucao, teste); //desenhe a frase amarela drawfont.DrawString(minhafont, meu_texto, posicao, Color.Yellow); //desenhe com transparência drawfont.DrawString(minhafont, meu_texto, posicao2, teste); drawfont.End(); }

O objeto SpriteFont possui um método que retorna o tamanho da string, tanto o tamanho vertical como o

horizontal, assim, podemos centralizar a string na tela usando os valores da resolução que já tínhamos

guardado.

//centralizar posicao.X = posicao.X - (minhafont.MeasureString(meu_texto).X)/2; posicao2.X = posicao2.X -(minhafont.MeasureString(meu_texto).X)/2;

Depois é só usar o método DrawString do nosso SpriteBatch para desenhar a string na tela, esse método

recebe como argumentos o SpriteFont onde encapsulamos o conjunto de caracteres da nossa fonte, a string

a ser desenhada, um objeto do tipo Vector2 que corresponde a posição onde o texto será desenhado e a cor

de desenho.

//desenhe a frase amarela drawfont.DrawString(minhafont, meu_texto, posicao, Color.Yellow);

Pronto, agora é só testar o resultado ficará assim:

Page 35: Tutorial XNA

Repare que no meu código eu também usei uma linha onde a cor de desenho possuía transparência. Ai vai o

código completo:

#region namespaces necessários using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; #endregion namespace fonts { class novo_jogo { static public void Main() { jogo teste = new jogo(); teste.Run(); } } } //herdando da classe Game class jogo : Game { //dispositivo grafico e recursos GraphicsDeviceManager grafico; ContentManager recursos; //string para mostrar texto na tela. string meu_texto; string resolucao; //posicao do texto na tela Vector2 posicao; //nova linha para o tempo com transparência Vector2 posicao2; //posicao do texto da resolucao na tela Vector2 posi_resolucao = Vector2.Zero; //para verificar se passaram cinco segundos int segundo_anterior = 0; //contador dos multiplos de cinco, //será incrementado a cada cinco segundos int total = 0;

Page 36: Tutorial XNA

//cor para desenhar os textos com transparência Color teste = new Color(Color.Yellow.R, Color.Yellow.G, Color.Yellow.B, 150); //representa a fonte SpriteFont minhafont; //vai servir para desenhar a fonte na tela SpriteBatch drawfont; //construtor da classe public jogo() { grafico = new GraphicsDeviceManager(this); recursos = new ContentManager(Services); } //inicializando membros que não precisam do //dispositivo grafico carregado para inicializar protected override void Initialize() { this.posi_resolucao.X += 10; this.posi_resolucao.Y += 10; base.Initialize(); } //carregando os recursos "externos" protected override void LoadGraphicsContent(bool loadAllContent) { if (loadAllContent) { //olha o asset name da fonte aqui minhafont = recursos.Load<SpriteFont>("arial"); drawfont = new SpriteBatch(grafico.GraphicsDevice); } } protected override void Update(GameTime gameTime) { //pegando o status do teclado para testar a saida KeyboardState teclado = Keyboard.GetState(); //o texto será o tempo total gasto no jogo meu_texto = gameTime.TotalGameTime.ToString(); //a posição será centralizada float X = grafico.GraphicsDevice.Viewport.Width / 2; float Y = grafico.GraphicsDevice.Viewport.Height / 2; //mostre a resolução da tela na tela resolucao = grafico.GraphicsDevice.Viewport.Width.ToString() + " x " + grafico.GraphicsDevice.Viewport.Height.ToString(); posicao = new Vector2(X,Y); //nova linha com transparência posicao2 = new Vector2(X,Y); //simplifique o nome para melhorar o código int segundo_atual = gameTime.TotalRealTime.Seconds; //incremente o contador a cada cinco segundos if(((segundo_atual % 5) == 0) && (segundo_atual != 0) && (segundo_atual != segundo_anterior) ) { segundo_anterior = segundo_atual; total++; }

Page 37: Tutorial XNA

//sair se pressionar esc if (teclado.IsKeyDown(Keys.Escape)) { this.Exit(); } } protected override void Draw(GameTime gameTime) { //limpe a tela com a cor preta grafico.GraphicsDevice.Clear(Color.Black); //centralizar posicao.X = posicao.X - (minhafont.MeasureString(meu_texto).X)/2; posicao2.X = posicao2.X - (minhafont.MeasureString(meu_texto).X)/2; //pular uma linha e deixar um espaço de 10 posicao2.Y = posicao2.Y + (minhafont.MeasureString(meu_texto).Y + 10); drawfont.Begin(); //desenhe a resolução com o contador drawfont.DrawString(minhafont, resolucao + " " + total.ToString(), posi_resolucao, teste); //desenhe a frase amarela drawfont.DrawString(minhafont, meu_texto, posicao, Color.Yellow); //desenhe com transparência drawfont.DrawString(minhafont, meu_texto, posicao2, teste);

drawfont.End(); } }

Bom, vimos como realizar um evento de tempos em tempos no nosso jogo, também aprendemos como

desenhar texto na tela e vimos que podemos inclusive usar transparência no texto usando o que já

aprendemos sobre cores. Espero que este texto seja útil e até a próxima parte do tutorial.

Page 38: Tutorial XNA