Artigo
Tutorial de XNA - Parte V
Continuação do tutorial de XNA para iniciantes, mostra como desenhar textos na tela e como realizar um evento de tempos em tempos.
Enviado por   em 9/1/2008 10:02:57

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;

//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 XML[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"?>

<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">

<Asset Type="Graphics:FontDescription">

 

  

  <FontName>ArialFontName>

 

  

  <Size>24Size>

 

  

  <Spacing>2Spacing>

 

  

  <Style>RegularStyle>

 

  

  <CharacterRegions>

    <CharacterRegion>

      <Start> Start>

      <End>~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.

 

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;

 

          //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++;

}

 

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:

 

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;

 

      //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++;

          }

 

          //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.

 ---------------------------

 

[1] Mesmo que você não saiba nada de XML poderá fazer as modificações nesse arquivo facilmente, pois ele é bem pequeno e simples. Não está no escopo desse documento explicar o que é XML, faça uma busca no google e achará rapidamente.


Sobre o Autor

jferreira
Não Definido
Não Definido

Clique para avaliar:

Comentários

Adicione seu Comentário  Voltar
Translator
Logos do XBox 360, XNA e Games For Windows
Copyright 2010 por SharpgamesPolítica de Privacidade  |  Termos de Uso