Envie Artigos e Concorra a Prêmios
 
Artigos
Article List
Ganhando Desempenho em jogos com XNA

Algumas dicas para otimizacao de codigo em jogos com XNA

Enviado por Luciano José em 7/3/2008 0:00:00

Introdução

 
O componente de FPS utilizado para esta demonstração foi publicado na comunidade SharpGames, neste link.
 
Obs.: A única mudança feita, no componente de FPS, foi retirar a frase de exibição “Pressione (Escape) para trocar os efeitos”. Espero que o autor não fique bravo!!!!!
 
 

Ganhando Desempenho em assuntos relacionados com SpriteBatch

 
  • Desenhe muitos sprites dentro de um par Begin/End
 
  • Se possível, use SpriteSortMode.Immediate; SpriteSortMode.Immediate é mais rápido que SpriteSortMode.Deferred
 
  • Desenhe as texturas em ordem de exibição, evitando o uso de SpriteSortMode.FrontToBack ou SpriteSortMode.BackToFront
 
 
  • Se não for possível usar SpriteSortMode.Immediate, então, utilize SpriteSortMode.Texture ao invés de SpriteSortMode.Deferred
 

Observação 

Se você utiliza o bloco de código abaixo: 

spriteBatch.Begin();
 
. . . 

spriteBatch.End(); 

Por padrão, o SpriteSortMode utilizado será o SpriteSortMode.Deferred. Então, para utilizar o SpriteSortMode.Immediate, utilize o bloco de código a seguir: 

spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
                SpriteSortMode.Immediate, SaveStateMode.None);
 
. . .

spriteBatch.End();

 

Demonstração 

Para essa demonstração serão desenhados na tela 1.000 sprites. Também Serão demonstradas 2 formas de se fazer “a mesma coisa”, entretanto, ambas as formas obtendo diferentes resultados de performance.

 1º Forma:

 
Nesta forma, será atribuido a cada sprite 1 spriteBatch e também 1 textura.
Ou seja, o “jogo” vai conter 1.000 texturas e 1.000 spriteBatchs.
 
 
 
public class Sprite
    {
        SpriteBatch spriteBatch;
        public Texture2D texture;
        public Vector2 Position;
 
        public Sprite(Game game)
        {
            spriteBatch = new SpriteBatch(game.GraphicsDevice);
        }
 
        public void Draw()
        {
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
                SpriteSortMode.Immediate, SaveStateMode.None);
 
            spriteBatch.Draw(texture, Position, Color.White);
 
            spriteBatch.End();
        }
    }
 
 
public class Game1 : Microsoft.Xna.Framework.Game
{
        GraphicsDeviceManager graphics;
 
        Sprite[] xna = new Sprite[1000];
       
        currentFPSCounter fps;
 
. . .
 
protected override void Initialize()
{
      // TODO: Add your initialization logic here
 
. . .
 
      /*
       * na tela, será visto apenas 1 imagem pelo fato dos 1.000 sprites estarem            
       * sendo desenhado na mesma posicao como visto no bloco de codigo abaixo
       * */
       for (int i = 0; i < xna.Length; i++)
       {
           xna[i] = new Sprite(this);
           xna[i].Position.X = 400;
           xna[i].Position.Y = 20;
       }
 
       base.Initialize();
}
 
. . .
 
protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
 
            for (int i = 0; i < xna.Length; i++)
            {                      
                xna[i].Draw();
            }
 
            base.Draw(gameTime);
        }
 

Resultado obtido depois de 1 minuto: 

 

Projeto apresentado acima para download:  testspritebatch-1.zip 139988 bytes

2º Forma:

 
Nesta forma, será utilizado apenas 1 spriteBatch e 1 textura para desenhar os 1.000 sprites.
 
public class Sprite
{
        public Vector2 Position;       
}
 
public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D texture;
        Sprite[] xna = new Sprite[1000];
        currentFPSCounter fps;
. . .
 
protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
 
            // TODO: Add your drawing code here
 
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
                SpriteSortMode.Immediate, SaveStateMode.None);
 
            for (int i = 0; i < xna.Length; i++)
            {
                spriteBatch.Draw(texture, xna[i].Position, Color.White);
            }
 
            spriteBatch.End();
 
            base.Draw(gameTime);
        }
 
 
 

Resultado obtido depois de 1 minuto: 

 

 Projeto apresentado acima para download:  testspritebatch-2.zip 92937 bytes

Conclusão: 

  • Utilize 1 spriteBatch para desenhar várias texturas na tela.
  • Desenhe muitos sprites dentro de um par Begin/End.
  • Se possivel, em jogos que você tenha que ter por exemplo: uma chuva de meteoros na tela, ou vários objetos de mesmo aspecto/textura – Utilize uma única textura para essas tarefas. 

 

Ganhando Desempenho em assuntos relacionados a Matemática


Passe objetos como Vector e/ou Matrix por referência. Tenha em mente que essa prática pode afetar na legibilidade do codigo.
 
 

Demonstração

 
Para a demonstração a seguir utilizaremos a classe abaixo. Aonde serão verificados os resultados de performance dos metodos Update1, Update2 e Update3.
Os 3 metodos, fazem a mesma coisa, entretanto fazem de maneira que diferem um do outro.
 
class Particle
{
        public Vector3 Position;
        public Vector3 Velocity;
 
        const float Friction = 0.9f;
 
        public void Update1()
        {           
            Position += Velocity;
            Velocity *= Friction;           
        }
       
    ///
    /// Passando Estruturas por Referencia
    ///
        public void Update2()
        {
            Vector3.Add(ref Position, ref Velocity, out Position);
            Vector3.Multiply(ref Velocity, Friction, out Velocity);            
        }
 
        public void Update3()
        {
            Position.X += Velocity.X;
            Position.Y += Velocity.Y;
            Position.Z += Velocity.Z;
 
            Velocity.X *= Friction;
            Velocity.Y *= Friction;
            Velocity.Z *= Friction;
        }
    }
 
Os fragmentos de codigo utilizados nos metodos Update1, Update2 e Update3 foram retirados da apresentação do Shawn Hargreaves com o tema Understanding XNA Framework Performance no evento GameFest 2007 em Washington.
 
Resultados apresentados:
Obs.: Quanto maior o número de Updates por segundo, melhor.
Com o Fragmento de codigo encontrado no método:
  1. Update1
3380000 Updates por Segundo.
  1. Update2
5540000 Updates por Segundo. Ou seja, (x 1.6) maior que no Update1.
  1. Update3
12840000 Updates por Segundo. Ou seja, (x 3.8) maior que no Update2.
 

Realizando mais alguns testes

 

Serão utilizados para o teste dos métodos Updates: 400.000 partículas. 

 

public class Game1 : Microsoft.Xna.Framework.Game
{
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Particle[] particle = new Particle[400000];
        currentFPSCounter fps;
. . .
 
protected override void Update(GameTime gameTime)
        { 
            for (int i = 0; i < particle.Length; i++)
            {
                particle[i].Update1();
                //particle[i].Update2();
                //particle[i].Update3();
            } 
            base.Update(gameTime);

        } 

 

Observe as fotos com os resultados, depois de 1 minuto decorrido:

 

Resultado utilizando o método Update1:

 

 

 

Resultado utilizando o método Update2:

Obs.: Comente a linha de código - particle[i].Update1(); - e retire o comentario da linha - particle[i].Update2();
 
O seu metodo Update ficará:
 
protected override void Update(GameTime gameTime)
        {
 
            for (int i = 0; i < particle.Length; i++)
            {
                //particle[i].Update1();
                particle[i].Update2();
                //particle[i].Update3();
            }
 
            base.Update(gameTime);

        }

 

 

Resultado utilizando o método Update3:

Faça o procedimento semelhante ao feito com o teste do método Update2; você irá comentar a linha do metodo Update2 e retirar o comentario do Update3

 

Projeto apresentado acima para download:  testandoperformance.zip 60507 bytes

 

Mais questões de Desempenho no Tópico Matemática:

Evite o uso da linha de codigo abaixo:
 
Position = new Vector3(1, 2, 3);
 
Ao invés disso use:
 
Position = new Vector3();
Position.X = 1;
Position.Y = 2;
Position.Z = 3;
 
A mesma prática pode ser estendida para estruturas criadas por você.
 
Não é por acaso que todas as Estruturas para checagem de colisão providas pelo XNA FrameWork(e também, a maioria das estruturas providas pelo XNA FrameWork), contêm um construtor vazio para, posteriormente, atribuir valores manualmente. 
 
 

Outros Tópicos 

  • Generics

  •  Multithreading  

 

Fontes consultadas:

Apresentação Understanding XNA Framework Performance: http://www.xnagamefest.com/presentations.htm#XNA_GAME_STUDIO
 

http://msdn2.microsoft.com/en-us/library/ms998547.aspx

 

Sugestões, Críticas e Dúvidas são bem vindas.

Obrigado!

 


Sobre o Autor

???
Não Definido
Não Definido
Ocorreu um erro.
Ocorreu um erro.


Clique para avaliar:

Comentários
" Muito bom mesmo, apesar de eu estar sumido da comunidade estou postando atrazado."
Enviado por kanedasam em 20/5/2008 0:53:02:
 

Adicione seu Comentário
juegos gratis
PrêmiosMinimizar

Envie um artigo para o Sharpgames, colabore com a comunidade e fique famoso! Clique aqui e saiba mais.

Logos do XBox 360, XNA e Games For Windows
Copyright 2010 por SharpgamesPolítica de Privacidade  |  Termos de Uso