|
Neste artigo veremos como criar um componente reutilizável (GameComponent) para calcular os quadros por segundos (FPS) de um jogo desenvolvido em XNA. Este componente foi testado no XNA 3.1 e qualquer mudança de versão pode acarretar ou não no funcionamento do mesmo. Este artigo assume que o leitor já tenha os conhecimentos básicos de C# e XNA.
Introdução
Frame Rate (FPS – Frames Per Second) significa o número de quadros registrados, ou processados, ou exibidos por um dispositivo por uma unidade de tempo qualquer. Normalmente é expresso por quadros por segundos (FPS) e em monitores progressive-scan como hertz (Hz). Este tipo de medida é muito utilizado como instrumento de comparação para medir o desempenho das Placas de vídeos que é considerado muito importante nos vídeos games.
Nem todos os jogos precisão de altas taxas de fps, sendo considerado um mínimo entre 30 e 60 fps aceitável, mesmo que isto possa variar consideravelmente de jogo para jogo. Como assim, pode variar de jogo para jogo? Vamos pensar num jogo de cartas, este tipo de jogo não há necessidade de manter um FPS alto o tempo todo, pois você pode usar esse tempo para processar outras coisas mais importantes como Inteligência Artificial. Agora em jogos online e em outros jogos, a quantidade de FPS é muito importante como por exemplo nos jogos em primeira pessoa.
Calculando o Frame Rate de um jogo
Para medir a quantidade de FPS do seu jogo, você precisará armazenar a cada chamada a cada atualização do seu jogo o tempo passado e um contador que irá armazenar quantas vezes o jogo foi atualizado. Logo, quando o tempo passado for maior ou igual a 1 segundo, significa que a quantidade de quadros exibidos é à quantidade contada, então para continuar contando você deve zerar o contador e subtrair 1 segundo do tempo passado, pois se você zerar o tempo, provavelmente você vai ter perdido alguns milissegundos que a variável que armazena o tempo pode conter.
O primeiro passo então seria criar as variáveis necessárias para armazenamento do tempo passado, da quantidade de quadros e do total e dos fps do jogo.
private TimeSpan elapsedTime = new TimeSpan(); private byte totalFrames = 0; private byte frameRate = 0;
Agora no método Update o código ficaria desta forma
public void Update(GameTime gameTime)
{
elapsedTime += gameTime.ElapsedGameTime;
totalFrames++;
if (elapsedTime >= TimeSpan.FromSeconds(1))
{
frameRate = totalFrames;
totalFrames = 0;
elapsedTime -= TimeSpan.FromSeconds(1);
}
}
Muito fácil não ? mas agora vamos tentar fazer um código totalmente reutilizável.
Projeto do nosso componente reutilizável
Os componentes são instrumentos muito úteis para o desenvolvimento modular no XNA, com ele é possível criar ferramentas sem ter dependência alguma da arquitetura a ser usada no jogo. Uma observação interessante é que ao utilizar componentes, a perca de desempenho é inevitável, ou seja, se o seu objetivo não é perder desempenho não vale à pena utilizar Components, sem contar que se o seu jogo tiver muitos componentes provavelmente você vai se perder em alguma parte.
Existem dois tipos de Game Components no XNA: o GameComponent, que é um componente comum que não executa operações de desenho e o DrawableGameComponent que possui um método Draw e pode ser desenhado. No nosso caso, iremos usar um DrawableGameComponent para deixar o componente o mais independente possível. Mais detalhes sobre os tipos de componentes ficaram para um outro artigo.
Na figura abaixo é exibido o projeto do nosso componente.![]()
Após analisar o projeto, vamos começar então a programar.
Programando o componente de FPS
1º Passo: Crie uma classe para o componente, no meu caso chamarei de “FrameRateCounter”. Definiremos uma namespace adequada para nossa classe como pode ser visto no código abaixo junto com a classe derivada de DrawableGameComponet.
namespace Utils.Componentes.FrameRateCounter
{
///
/// Componente de contador de FPS (Frames por Segundo)
///
public class FrameRateCounter : DrawableGameComponent
{
}
}
2º Passo: Logo após, devemos então criar as variáveis necessárias para este componente.
#region [ Fields ] ////// SpriteBatch utilizado para escrever e/ou desenhar na tela /// private SpriteBatch spriteBatch = null; ////// SpriteFont para escrever /// private SpriteFont font = null; ////// Nome da SpriteFont à ser carregada /// private string fontName; ////// Cor do texto a ser desenhado na tela /// public Color Color { get { return color; } set { color = value; } } private Color color = Color.White; ////// Armazena o total de tempo (segundos) passados desde o último update /// private TimeSpan elapsedTime = new TimeSpan(); ////// Armazena o número total de frames /// private byte totalFrames = 0; ////// Armazena o total de FPS (Frames Por Segundo) /// private byte frameRate = 0; ////// Vetor para armazenar a posição do texto na tela /// public Vector2 Position { set { position = value; } get { return position; } } private Vector2 position = new Vector2(10.0f, 10.0f); #endregion
3º Passo: Vamos criar agora o construtor dessa classe e também algumas sobrecargas para o mesmo, mantendo um componente maleável.
#region [ Constructor ] ////// Construtor do componente contador de FPS (Frames por Segundo) /// /// Referência do jogo que o chamou /// Nome da SpriteFont ou caminho a ser carregado public FrameRateCounter(Game game, string fontName) : base(game) { this.fontName = fontName; } ////// Construtor do componente contador de FPS (Frames por Segundo) /// /// Referência do jogo que o chamou /// Nome da SpriteFont ou caminho a ser carregado /// Cor do Texto que sera escrito na tela public FrameRateCounter(Game game, string fontName, Color color) : base(game) { this.fontName = fontName; Color = color; } ////// Construtor do componente contador de FPS (Frames por Segundo) /// /// Referência do jogo que o chamou /// Nome da SpriteFont ou caminho a ser carregado /// Cor do Texto que sera escrito na tela /// Posição do Texto à ser desenhado public FrameRateCounter(Game game, string fontName, Color color, Vector2 position) : base(game) { this.fontName = fontName; Color = color; Position = position; } #endregion
4º Passo: Com os construtores implementados, precisamos então criar o nosso SpriteBatch para escrever o texto na tela e também carregar a SpriteFont. Note que este método utiliza a palavra override, o que significa que estamos utilizando o mecanismo de polimorfismo.
#region [ Load Content ] ////// Carrega o conteúdo necessário para que o componente funcione /// protected override void LoadContent() { // Tenta receber um serviço do tipo SpriteBatch do jogo spriteBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch)); if (spriteBatch == null) { // Cria uma nova SpriteBatch, que pode ser usada spriteBatch = new SpriteBatch(Game.GraphicsDevice); } // Carrega a font para escrever o texto if (!string.IsNullOrEmpty(fontName)) font = Game.Content.Load(fontName); } #endregion
5º Passo: Lembram do nosso método Update? aqui iremos copiar ele, porém utilizaremos também a palavra override para o método.
#region [ Update ] ////// Método que atualiza o componente /// /// Referência para o tempo de jogo public override void Update(GameTime gameTime) { // Recebe o tempo passado deste a última chamada elapsedTime += gameTime.ElapsedGameTime; // Incrementa o total de frames por segundos, uma vez // que este método está sendo chamado novamente totalFrames++; // Verifica se o tempo passado (elapsedTime) é maior que // 1 segundo if (elapsedTime >= TimeSpan.FromSeconds(1)) { // Se o tempo passado for maior ou igual a 1 segundo, // definimos então a variavel frameRate com a quantidade // total de frames exibido na tela da variavel totalFrames frameRate = totalFrames; // Logo após zeramos a variável que guardava o total de frames, // para começar a recontagem e verificar se houve mudanças totalFrames = 0; // Retiramos 1 segundo da variável que armazena o tempo passado, // pois pode ser que este tempo tenha passado de 1 segundo. elapsedTime -= TimeSpan.FromSeconds(1); } } #endregion
6º Passo: Por último, iremos desenhar então a informação da quantidade de FPS na tela do jogo. Note novamente que o método é escrito usando override.
#region [ Draw ] ////// Método para desenhar a quantidade de Frames por Segundos /// /// Referência para o tempo de jogo public override void Draw(GameTime gameTime) { // Armazena uma string indicando a quantidade de FPS string fps = string.Format("FPS: {0}", frameRate); // Inicializa o SpriteBatch para desenhar spriteBatch.Begin(); // Escreve o FPS na tela spriteBatch.DrawString(font, fps, position, color); // Finaliza o SpriteBatch spriteBatch.End(); } #endregion
Pronto, nosso componente esta concluído, agora precisamos testar ele em nosso jogo.
Usando o componente de FPS no meu jogo
1º Passo: Adicione a classe FrameRanteCounter.cs no seu projeto.
2º Passo: Crie um SpriteFont para que o componente possa escrever na tela a taxa de FPS. Chamarei minha font de “fpsFont”, veja um exemplo de arquivo spritefont abaixo usando font arial e tamanho 14.
Arial 14 0 true   ~
3º Passo: Adicione a namespace do componente na sua classe que irá declarar o objeto.
using Utils.Componentes.FrameRateCounter;
4º passo: Inicialize o objeto e adicione a lista de GameComponents do jogo, veja o exemplo abaixo, no qual o componente é adicionado na classe principal do jogo (Game1.cs).
Observação: Faça isso no método Initialize.
protected override void Initialize()
{
// TODO: Add your initialization logic here
FrameRateCounter FPSCounter = new FrameRateCounter(this, "fpsFont");
Components.Add(FPSCounter);
base.Initialize();
}
5º passo: Pronto agora é só compilar (F5) seu projeto e ver o componente de FPS em ação.
Download do Componente de FPS
Para fazer download deste componente e utilizar em seus jogos clique na imagem abaixo.
Conclusão
Espero que este componente seja muito útil para seus jogos, uma vez que ele o ajudará a medir a taxa de fps do seu jogo e também é de fácil utilização. Qualquer melhoria no componente poderá ser sugerida por vocês, para mantermos o componente atualizado para todos.












Kleber, parabéns por essa nova fase virtual.
O tutorial ficou excelente, muito fácil de se entender.
Que venham mais tutoriais assim...